diff --git a/dist/web/pubnub.js b/dist/web/pubnub.js index b2f732a97..9b8857a25 100644 --- a/dist/web/pubnub.js +++ b/dist/web/pubnub.js @@ -465,6 +465,181 @@ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; + /** + * Request processing status categories. + */ + var StatusCategory; + (function (StatusCategory) { + /** + * Call failed when network was unable to complete the call. + */ + StatusCategory["PNNetworkIssuesCategory"] = "PNNetworkIssuesCategory"; + /** + * Network call timed out. + */ + StatusCategory["PNTimeoutCategory"] = "PNTimeoutCategory"; + /** + * Request has been cancelled. + */ + StatusCategory["PNCancelledCategory"] = "PNCancelledCategory"; + /** + * Server responded with bad response. + */ + StatusCategory["PNBadRequestCategory"] = "PNBadRequestCategory"; + /** + * Server responded with access denied. + */ + StatusCategory["PNAccessDeniedCategory"] = "PNAccessDeniedCategory"; + /** + * Incomplete parameters provided for used endpoint. + */ + StatusCategory["PNValidationErrorCategory"] = "PNValidationErrorCategory"; + /** + * PubNub request acknowledgment status. + * + * Some API endpoints respond with request processing status w/o useful data. + */ + StatusCategory["PNAcknowledgmentCategory"] = "PNAcknowledgmentCategory"; + /** + * PubNub service or intermediate "actor" returned unexpected response. + * + * There can be few sources of unexpected return with success code: + * - proxy server / VPN; + * - Wi-Fi hotspot authorization page. + */ + StatusCategory["PNMalformedResponseCategory"] = "PNMalformedResponseCategory"; + /** + * Server can't process request. + * + * There can be few sources of unexpected return with success code: + * - potentially an ongoing incident; + * - proxy server / VPN. + */ + StatusCategory["PNServerErrorCategory"] = "PNServerErrorCategory"; + /** + * Something strange happened; please check the logs. + */ + StatusCategory["PNUnknownCategory"] = "PNUnknownCategory"; + // -------------------------------------------------------- + // --------------------- Network status ------------------- + // -------------------------------------------------------- + /** + * SDK will announce when the network appears to be connected again. + */ + StatusCategory["PNNetworkUpCategory"] = "PNNetworkUpCategory"; + /** + * SDK will announce when the network appears to down. + */ + StatusCategory["PNNetworkDownCategory"] = "PNNetworkDownCategory"; + // -------------------------------------------------------- + // -------------------- Real-time events ------------------ + // -------------------------------------------------------- + /** + * PubNub client reconnected to the real-time updates stream. + */ + StatusCategory["PNReconnectedCategory"] = "PNReconnectedCategory"; + /** + * PubNub client connected to the real-time updates stream. + */ + StatusCategory["PNConnectedCategory"] = "PNConnectedCategory"; + /** + * Set of active channels and groups has been changed. + */ + StatusCategory["PNSubscriptionChangedCategory"] = "PNSubscriptionChangedCategory"; + /** + * Received real-time updates exceed specified threshold. + * + * After temporary disconnection and catchup, this category means that potentially some + * real-time updates have been pushed into `storage` and need to be requested separately. + */ + StatusCategory["PNRequestMessageCountExceededCategory"] = "PNRequestMessageCountExceededCategory"; + /** + * PubNub client disconnected from the real-time updates streams. + */ + StatusCategory["PNDisconnectedCategory"] = "PNDisconnectedCategory"; + /** + * PubNub client wasn't able to connect to the real-time updates streams. + */ + StatusCategory["PNConnectionErrorCategory"] = "PNConnectionErrorCategory"; + /** + * PubNub client unexpectedly disconnected from the real-time updates streams. + */ + StatusCategory["PNDisconnectedUnexpectedlyCategory"] = "PNDisconnectedUnexpectedlyCategory"; + // -------------------------------------------------------- + // ------------------ Shared worker events ---------------- + // -------------------------------------------------------- + /** + * SDK will announce when newer shared worker will be 'noticed'. + */ + StatusCategory["PNSharedWorkerUpdatedCategory"] = "PNSharedWorkerUpdatedCategory"; + })(StatusCategory || (StatusCategory = {})); + var StatusCategory$1 = StatusCategory; + + /** + * PubNub operation error module. + */ + /** + * PubNub operation error. + * + * When an operation can't be performed or there is an error from the server, this object will be returned. + */ + class PubNubError extends Error { + /** + * Create PubNub operation error. + * + * @param message - Message with details about why operation failed. + * @param [status] - Additional information about performed operation. + * + * @returns Configured and ready to use PubNub operation error. + * + * @internal + */ + constructor(message, status) { + super(message); + this.status = status; + this.name = 'PubNubError'; + this.message = message; + Object.setPrototypeOf(this, new.target.prototype); + } + } + /** + * Create error status object. + * + * @param errorPayload - Additional information which should be attached to the error status object. + * @param category - Occurred error category. + * + * @returns Error status object. + * + * @internal + */ + function createError(errorPayload, category) { + var _a; + (_a = errorPayload.statusCode) !== null && _a !== void 0 ? _a : (errorPayload.statusCode = 0); + return Object.assign(Object.assign({}, errorPayload), { statusCode: errorPayload.statusCode, category, error: true }); + } + /** + * Create operation arguments validation error status object. + * + * @param message - Information about failed validation requirement. + * @param [statusCode] - Operation HTTP status code. + * + * @returns Operation validation error status object. + * + * @internal + */ + function createValidationError(message, statusCode) { + return createError(Object.assign({ message }, ({})), StatusCategory$1.PNValidationErrorCategory); + } + /** + * Create malformed service response error status object. + * + * @param [responseText] - Stringified original service response. + * @param [statusCode] - Operation HTTP status code. + */ + function createMalformedResponseError(responseText, statusCode) { + return createError(Object.assign(Object.assign({ message: 'Unable to deserialize service response' }, (responseText !== undefined ? { responseText } : {})), (statusCode !== undefined ? { statusCode } : {})), StatusCategory$1.PNMalformedResponseCategory); + } + /** * Crypto module. */ @@ -503,9 +678,12 @@ } // endregion constructor(configuration) { - var _a; + var _a, _b; + // AES-GCM cryptor is decrypt-only and can be registered only as an additional cryptor. + if (((_a = configuration.default) === null || _a === void 0 ? void 0 : _a.identifier) === 'ACGM') + throw new PubNubError("Crypto module error: AES-GCM cryptor (ACGM) can't be used as default cryptor."); this.defaultCryptor = configuration.default; - this.cryptors = (_a = configuration.cryptors) !== null && _a !== void 0 ? _a : []; + this.cryptors = (_b = configuration.cryptors) !== null && _b !== void 0 ? _b : []; } /** * Assign registered loggers' manager. @@ -822,1097 +1000,223 @@ } /** - * Request processing status categories. + * Byte helpers shared by cryptors and crypto primitives. + * + * @internal */ - var StatusCategory; - (function (StatusCategory) { + /** + * Lazily created UTF-8 decoder. + * + * **Note:** Not created at module load and doesn't use `fatal` option because some polyfills (for example + * `fast-text-encoding` used on React Native) throw when it is requested. Input is validated by + * {@link isWellFormedUtf8} instead, so the decoder only ever receives well-formed data. + */ + let decoder; + /** + * Whether {@link decoder} strips a leading BOM despite `ignoreBOM` option (detected once, on first use). + */ + let decoderStripsBOM = false; + /** + * Convert bytes to lowercase HEX string. + * + * @param bytes - Source bytes. + * + * @returns HEX encoded bytes. + * + * @internal + */ + const bytesToHex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + /** + * Copy bytes into standalone {@link ArrayBuffer}. + * + * @param bytes - Source bytes (may be a view into a larger buffer). + * + * @returns {@link ArrayBuffer} which contains only source bytes. + * + * @internal + */ + const toArrayBuffer = (bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); + /** + * Check whether bytes are well-formed UTF-8. + * + * Implements Unicode "Well-Formed UTF-8 Byte Sequences" (Table 3-7): rejects stray continuation bytes, overlong + * forms (`C0`, `C1`, `E0 80-9F`, `F0 80-8F`), surrogates (`ED A0-BF`), code points above U+10FFFF (`F4 90-BF`, + * `F5-FF`) and truncated sequences. Same rules as CryptoJS `Utf8.stringify` (`decodeURIComponent`). + * + * @param bytes - Bytes which should be checked. + * + * @returns `true` if bytes are well-formed UTF-8. + * + * @internal + */ + const isWellFormedUtf8 = (bytes) => { + const length = bytes.length; + let index = 0; + while (index < length) { + const lead = bytes[index]; + if (lead < 0x80) { + index += 1; + continue; + } + let continuationBytes; + // Allowed range of the first continuation byte (narrower for some lead bytes). + let lower = 0x80; + let upper = 0xbf; + if (lead >= 0xc2 && lead <= 0xdf) + continuationBytes = 1; + else if (lead >= 0xe0 && lead <= 0xef) { + continuationBytes = 2; + if (lead === 0xe0) + lower = 0xa0; + else if (lead === 0xed) + upper = 0x9f; + } + else if (lead >= 0xf0 && lead <= 0xf4) { + continuationBytes = 3; + if (lead === 0xf0) + lower = 0x90; + else if (lead === 0xf4) + upper = 0x8f; + } + else + return false; + if (index + continuationBytes >= length) + return false; + const first = bytes[index + 1]; + if (first < lower || first > upper) + return false; + for (let offset = 2; offset <= continuationBytes; offset++) { + const next = bytes[index + offset]; + if (next < 0x80 || next > 0xbf) + return false; + } + index += continuationBytes + 1; + } + return true; + }; + /** + * Strictly decode UTF-8 bytes. + * + * Behaves like CryptoJS `Utf8.stringify` used by previous SDK versions: malformed sequences are rejected instead of + * being replaced, and a leading BOM is preserved (regular decoders silently strip it). Result doesn't depend on the + * platform `TextDecoder` implementation or polyfill. + * + * @param bytes - UTF-8 encoded bytes. + * + * @returns Decoded string. + * + * @throws Error if bytes aren't a valid UTF-8 sequence. + * + * @internal + */ + const decodeUtf8Strict = (bytes) => { + if (!isWellFormedUtf8(bytes)) + throw new Error('Malformed UTF-8 data'); + if (!decoder) { + decoder = new TextDecoder('utf-8', { ignoreBOM: true }); + decoderStripsBOM = decoder.decode(new Uint8Array([0xef, 0xbb, 0xbf])).length === 0; + } + const decoded = decoder.decode(bytes); + const hasBOM = bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf; + return hasBOM && decoderStripsBOM ? `\uFEFF${decoded}` : decoded; + }; + + /** + * Browser cryptographic primitives backed by `crypto.subtle`. + * + * @internal + */ + /* global crypto */ + /** + * Browser cryptographic primitives. + * + * @internal + */ + class WebCryptoPrimitives { + constructor() { + /** + * Imported keys cache (keyed by the algorithm and hex representation of the raw key). + */ + this.keys = new Map(); + } + sha256(data) { + return __awaiter(this, void 0, void 0, function* () { + return new Uint8Array(yield this.subtle.digest('SHA-256', data)); + }); + } + aesCbcEncrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + const cryptoKey = yield this.importKey(key, 'AES-CBC'); + return new Uint8Array(yield this.subtle.encrypt({ name: 'AES-CBC', iv }, cryptoKey, data)); + }); + } + aesCbcDecrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + const cryptoKey = yield this.importKey(key, 'AES-CBC'); + return new Uint8Array(yield this.subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, data)); + }); + } /** - * Call failed when network was unable to complete the call. + * Decrypt and authenticate data using AES-256-GCM (128-bit tag, no additional authenticated data). + * + * Used only by the decrypt-only `ACGM` cryptor (Web and Node.js). Intentionally not part of + * {@link CryptoPrimitives}: React Native supports only the legacy cryptor. + * + * @param key - 32 bytes long raw key. + * @param nonce - 12 bytes long nonce. + * @param data - Data which should be decrypted, with 16 bytes long authentication tag appended to the ciphertext. + * + * @returns Decrypted data (returned only after the authentication tag has been verified). + * + * @throws Error if data can't be authenticated (for example: wrong key or modified nonce, ciphertext or tag). */ - StatusCategory["PNNetworkIssuesCategory"] = "PNNetworkIssuesCategory"; - /** - * Network call timed out. - */ - StatusCategory["PNTimeoutCategory"] = "PNTimeoutCategory"; - /** - * Request has been cancelled. - */ - StatusCategory["PNCancelledCategory"] = "PNCancelledCategory"; - /** - * Server responded with bad response. - */ - StatusCategory["PNBadRequestCategory"] = "PNBadRequestCategory"; - /** - * Server responded with access denied. - */ - StatusCategory["PNAccessDeniedCategory"] = "PNAccessDeniedCategory"; - /** - * Incomplete parameters provided for used endpoint. - */ - StatusCategory["PNValidationErrorCategory"] = "PNValidationErrorCategory"; - /** - * PubNub request acknowledgment status. - * - * Some API endpoints respond with request processing status w/o useful data. - */ - StatusCategory["PNAcknowledgmentCategory"] = "PNAcknowledgmentCategory"; + aesGcmDecrypt(key, nonce, data) { + return __awaiter(this, void 0, void 0, function* () { + const cryptoKey = yield this.importKey(key, 'AES-GCM'); + return new Uint8Array(yield this.subtle.decrypt({ name: 'AES-GCM', iv: nonce, tagLength: 128 }, cryptoKey, data)); + }); + } + randomBytes(length) { + return crypto.getRandomValues(new Uint8Array(length)); + } /** - * PubNub service or intermediate "actor" returned unexpected response. + * Retrieve `SubtleCrypto` interface. * - * There can be few sources of unexpected return with success code: - * - proxy server / VPN; - * - Wi-Fi hotspot authorization page. - */ - StatusCategory["PNMalformedResponseCategory"] = "PNMalformedResponseCategory"; - /** - * Server can't process request. - * - * There can be few sources of unexpected return with success code: - * - potentially an ongoing incident; - * - proxy server / VPN. - */ - StatusCategory["PNServerErrorCategory"] = "PNServerErrorCategory"; - /** - * Something strange happened; please check the logs. - */ - StatusCategory["PNUnknownCategory"] = "PNUnknownCategory"; - // -------------------------------------------------------- - // --------------------- Network status ------------------- - // -------------------------------------------------------- - /** - * SDK will announce when the network appears to be connected again. - */ - StatusCategory["PNNetworkUpCategory"] = "PNNetworkUpCategory"; - /** - * SDK will announce when the network appears to down. - */ - StatusCategory["PNNetworkDownCategory"] = "PNNetworkDownCategory"; - // -------------------------------------------------------- - // -------------------- Real-time events ------------------ - // -------------------------------------------------------- - /** - * PubNub client reconnected to the real-time updates stream. - */ - StatusCategory["PNReconnectedCategory"] = "PNReconnectedCategory"; - /** - * PubNub client connected to the real-time updates stream. - */ - StatusCategory["PNConnectedCategory"] = "PNConnectedCategory"; - /** - * Set of active channels and groups has been changed. - */ - StatusCategory["PNSubscriptionChangedCategory"] = "PNSubscriptionChangedCategory"; - /** - * Received real-time updates exceed specified threshold. + * @returns `SubtleCrypto` interface. * - * After temporary disconnection and catchup, this category means that potentially some - * real-time updates have been pushed into `storage` and need to be requested separately. - */ - StatusCategory["PNRequestMessageCountExceededCategory"] = "PNRequestMessageCountExceededCategory"; - /** - * PubNub client disconnected from the real-time updates streams. + * @throws PubNubError if `crypto.subtle` isn't available (non-secure context). */ - StatusCategory["PNDisconnectedCategory"] = "PNDisconnectedCategory"; - /** - * PubNub client wasn't able to connect to the real-time updates streams. - */ - StatusCategory["PNConnectionErrorCategory"] = "PNConnectionErrorCategory"; - /** - * PubNub client unexpectedly disconnected from the real-time updates streams. - */ - StatusCategory["PNDisconnectedUnexpectedlyCategory"] = "PNDisconnectedUnexpectedlyCategory"; - // -------------------------------------------------------- - // ------------------ Shared worker events ---------------- - // -------------------------------------------------------- - /** - * SDK will announce when newer shared worker will be 'noticed'. - */ - StatusCategory["PNSharedWorkerUpdatedCategory"] = "PNSharedWorkerUpdatedCategory"; - })(StatusCategory || (StatusCategory = {})); - var StatusCategory$1 = StatusCategory; - - /** - * PubNub operation error module. - */ - /** - * PubNub operation error. - * - * When an operation can't be performed or there is an error from the server, this object will be returned. - */ - class PubNubError extends Error { + get subtle() { + if (typeof crypto === 'undefined' || !crypto.subtle) + throw new PubNubError('Crypto error: crypto.subtle is unavailable. Encryption requires a secure context (HTTPS or localhost).'); + return crypto.subtle; + } /** - * Create PubNub operation error. + * Import raw AES key (cached, least recently imported keys evicted first). * - * @param message - Message with details about why operation failed. - * @param [status] - Additional information about performed operation. - * - * @returns Configured and ready to use PubNub operation error. + * @param key - Raw key bytes. + * @param algorithm - Algorithm for which key will be used. * - * @internal + * @returns Imported `CryptoKey`. */ - constructor(message, status) { - super(message); - this.status = status; - this.name = 'PubNubError'; - this.message = message; - Object.setPrototypeOf(this, new.target.prototype); + importKey(key, algorithm) { + const id = `${algorithm}:${bytesToHex(key)}`; + let cryptoKey = this.keys.get(id); + if (!cryptoKey) { + if (this.keys.size >= WebCryptoPrimitives.MAX_CACHED_KEYS) + this.keys.delete(this.keys.keys().next().value); + const usages = algorithm === 'AES-CBC' ? ['encrypt', 'decrypt'] : ['decrypt']; + cryptoKey = this.subtle.importKey('raw', key, algorithm, false, usages); + cryptoKey.catch(() => this.keys.delete(id)); + this.keys.set(id, cryptoKey); + } + return cryptoKey; } } /** - * Create error status object. - * - * @param errorPayload - Additional information which should be attached to the error status object. - * @param category - Occurred error category. - * - * @returns Error status object. - * - * @internal - */ - function createError(errorPayload, category) { - var _a; - (_a = errorPayload.statusCode) !== null && _a !== void 0 ? _a : (errorPayload.statusCode = 0); - return Object.assign(Object.assign({}, errorPayload), { statusCode: errorPayload.statusCode, category, error: true }); - } - /** - * Create operation arguments validation error status object. - * - * @param message - Information about failed validation requirement. - * @param [statusCode] - Operation HTTP status code. - * - * @returns Operation validation error status object. - * - * @internal - */ - function createValidationError(message, statusCode) { - return createError(Object.assign({ message }, ({})), StatusCategory$1.PNValidationErrorCategory); - } - /** - * Create malformed service response error status object. - * - * @param [responseText] - Stringified original service response. - * @param [statusCode] - Operation HTTP status code. + * Maximum number of imported keys kept in cache. */ - function createMalformedResponseError(responseText, statusCode) { - return createError(Object.assign(Object.assign({ message: 'Unable to deserialize service response' }, (responseText !== undefined ? { responseText } : {})), (statusCode !== undefined ? { statusCode } : {})), StatusCategory$1.PNMalformedResponseCategory); - } - - /** - * CryptoJS implementation. - * - * @internal - */ - - /*eslint-disable */ - - /* - CryptoJS v3.1.2 - code.google.com/p/crypto-js - (c) 2009-2013 by Jeff Mott. All rights reserved. - code.google.com/p/crypto-js/wiki/License - */ - var CryptoJS = - CryptoJS || - (function (h, s) { - var f = {}, - g = (f.lib = {}), - q = function () {}, - m = (g.Base = { - extend: function (a) { - q.prototype = this; - var c = new q(); - a && c.mixIn(a); - c.hasOwnProperty('init') || - (c.init = function () { - c.$super.init.apply(this, arguments); - }); - c.init.prototype = c; - c.$super = this; - return c; - }, - create: function () { - var a = this.extend(); - a.init.apply(a, arguments); - return a; - }, - init: function () {}, - mixIn: function (a) { - for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]); - a.hasOwnProperty('toString') && (this.toString = a.toString); - }, - clone: function () { - return this.init.prototype.extend(this); - }, - }), - r = (g.WordArray = m.extend({ - init: function (a, c) { - a = this.words = a || []; - this.sigBytes = c != s ? c : 4 * a.length; - }, - toString: function (a) { - return (a || k).stringify(this); - }, - concat: function (a) { - var c = this.words, - d = a.words, - b = this.sigBytes; - a = a.sigBytes; - this.clamp(); - if (b % 4) - for (var e = 0; e < a; e++) - c[(b + e) >>> 2] |= ((d[e >>> 2] >>> (24 - 8 * (e % 4))) & 255) << (24 - 8 * ((b + e) % 4)); - else if (65535 < d.length) for (e = 0; e < a; e += 4) c[(b + e) >>> 2] = d[e >>> 2]; - else c.push.apply(c, d); - this.sigBytes += a; - return this; - }, - clamp: function () { - var a = this.words, - c = this.sigBytes; - a[c >>> 2] &= 4294967295 << (32 - 8 * (c % 4)); - a.length = h.ceil(c / 4); - }, - clone: function () { - var a = m.clone.call(this); - a.words = this.words.slice(0); - return a; - }, - random: function (a) { - for (var c = [], d = 0; d < a; d += 4) c.push((4294967296 * h.random()) | 0); - return new r.init(c, a); - }, - })), - l = (f.enc = {}), - k = (l.Hex = { - stringify: function (a) { - var c = a.words; - a = a.sigBytes; - for (var d = [], b = 0; b < a; b++) { - var e = (c[b >>> 2] >>> (24 - 8 * (b % 4))) & 255; - d.push((e >>> 4).toString(16)); - d.push((e & 15).toString(16)); - } - return d.join(''); - }, - parse: function (a) { - for (var c = a.length, d = [], b = 0; b < c; b += 2) - d[b >>> 3] |= parseInt(a.substr(b, 2), 16) << (24 - 4 * (b % 8)); - return new r.init(d, c / 2); - }, - }), - n = (l.Latin1 = { - stringify: function (a) { - var c = a.words; - a = a.sigBytes; - for (var d = [], b = 0; b < a; b++) d.push(String.fromCharCode((c[b >>> 2] >>> (24 - 8 * (b % 4))) & 255)); - return d.join(''); - }, - parse: function (a) { - for (var c = a.length, d = [], b = 0; b < c; b++) d[b >>> 2] |= (a.charCodeAt(b) & 255) << (24 - 8 * (b % 4)); - return new r.init(d, c); - }, - }), - j = (l.Utf8 = { - stringify: function (a) { - try { - return decodeURIComponent(escape(n.stringify(a))); - } catch (c) { - throw Error('Malformed UTF-8 data'); - } - }, - parse: function (a) { - return n.parse(unescape(encodeURIComponent(a))); - }, - }), - u = (g.BufferedBlockAlgorithm = m.extend({ - reset: function () { - this._data = new r.init(); - this._nDataBytes = 0; - }, - _append: function (a) { - 'string' == typeof a && (a = j.parse(a)); - this._data.concat(a); - this._nDataBytes += a.sigBytes; - }, - _process: function (a) { - var c = this._data, - d = c.words, - b = c.sigBytes, - e = this.blockSize, - f = b / (4 * e), - f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0); - a = f * e; - b = h.min(4 * a, b); - if (a) { - for (var g = 0; g < a; g += e) this._doProcessBlock(d, g); - g = d.splice(0, a); - c.sigBytes -= b; - } - return new r.init(g, b); - }, - clone: function () { - var a = m.clone.call(this); - a._data = this._data.clone(); - return a; - }, - _minBufferSize: 0, - })); - g.Hasher = u.extend({ - cfg: m.extend(), - init: function (a) { - this.cfg = this.cfg.extend(a); - this.reset(); - }, - reset: function () { - u.reset.call(this); - this._doReset(); - }, - update: function (a) { - this._append(a); - this._process(); - return this; - }, - finalize: function (a) { - a && this._append(a); - return this._doFinalize(); - }, - blockSize: 16, - _createHelper: function (a) { - return function (c, d) { - return new a.init(d).finalize(c); - }; - }, - _createHmacHelper: function (a) { - return function (c, d) { - return new t.HMAC.init(a, d).finalize(c); - }; - }, - }); - var t = (f.algo = {}); - return f; - })(Math); - - // SHA256 - (function (h) { - for ( - var s = CryptoJS, - f = s.lib, - g = f.WordArray, - q = f.Hasher, - f = s.algo, - m = [], - r = [], - l = function (a) { - return (4294967296 * (a - (a | 0))) | 0; - }, - k = 2, - n = 0; - 64 > n; - - ) { - var j; - a: { - j = k; - for (var u = h.sqrt(j), t = 2; t <= u; t++) - if (!(j % t)) { - j = !1; - break a; - } - j = !0; - } - j && (8 > n && (m[n] = l(h.pow(k, 0.5))), (r[n] = l(h.pow(k, 1 / 3))), n++); - k++; - } - var a = [], - f = (f.SHA256 = q.extend({ - _doReset: function () { - this._hash = new g.init(m.slice(0)); - }, - _doProcessBlock: function (c, d) { - for ( - var b = this._hash.words, - e = b[0], - f = b[1], - g = b[2], - j = b[3], - h = b[4], - m = b[5], - n = b[6], - q = b[7], - p = 0; - 64 > p; - p++ - ) { - if (16 > p) a[p] = c[d + p] | 0; - else { - var k = a[p - 15], - l = a[p - 2]; - a[p] = - (((k << 25) | (k >>> 7)) ^ ((k << 14) | (k >>> 18)) ^ (k >>> 3)) + - a[p - 7] + - (((l << 15) | (l >>> 17)) ^ ((l << 13) | (l >>> 19)) ^ (l >>> 10)) + - a[p - 16]; - } - k = - q + - (((h << 26) | (h >>> 6)) ^ ((h << 21) | (h >>> 11)) ^ ((h << 7) | (h >>> 25))) + - ((h & m) ^ (~h & n)) + - r[p] + - a[p]; - l = - (((e << 30) | (e >>> 2)) ^ ((e << 19) | (e >>> 13)) ^ ((e << 10) | (e >>> 22))) + - ((e & f) ^ (e & g) ^ (f & g)); - q = n; - n = m; - m = h; - h = (j + k) | 0; - j = g; - g = f; - f = e; - e = (k + l) | 0; - } - b[0] = (b[0] + e) | 0; - b[1] = (b[1] + f) | 0; - b[2] = (b[2] + g) | 0; - b[3] = (b[3] + j) | 0; - b[4] = (b[4] + h) | 0; - b[5] = (b[5] + m) | 0; - b[6] = (b[6] + n) | 0; - b[7] = (b[7] + q) | 0; - }, - _doFinalize: function () { - var a = this._data, - d = a.words, - b = 8 * this._nDataBytes, - e = 8 * a.sigBytes; - d[e >>> 5] |= 128 << (24 - (e % 32)); - d[(((e + 64) >>> 9) << 4) + 14] = h.floor(b / 4294967296); - d[(((e + 64) >>> 9) << 4) + 15] = b; - a.sigBytes = 4 * d.length; - this._process(); - return this._hash; - }, - clone: function () { - var a = q.clone.call(this); - a._hash = this._hash.clone(); - return a; - }, - })); - s.SHA256 = q._createHelper(f); - s.HmacSHA256 = q._createHmacHelper(f); - })(Math); - - // HMAC SHA256 - (function () { - var h = CryptoJS, - s = h.enc.Utf8; - h.algo.HMAC = h.lib.Base.extend({ - init: function (f, g) { - f = this._hasher = new f.init(); - 'string' == typeof g && (g = s.parse(g)); - var h = f.blockSize, - m = 4 * h; - g.sigBytes > m && (g = f.finalize(g)); - g.clamp(); - for (var r = (this._oKey = g.clone()), l = (this._iKey = g.clone()), k = r.words, n = l.words, j = 0; j < h; j++) - (k[j] ^= 1549556828), (n[j] ^= 909522486); - r.sigBytes = l.sigBytes = m; - this.reset(); - }, - reset: function () { - var f = this._hasher; - f.reset(); - f.update(this._iKey); - }, - update: function (f) { - this._hasher.update(f); - return this; - }, - finalize: function (f) { - var g = this._hasher; - f = g.finalize(f); - g.reset(); - return g.finalize(this._oKey.clone().concat(f)); - }, - }); - })(); - - // Base64 - (function () { - var u = CryptoJS, - p = u.lib.WordArray; - u.enc.Base64 = { - stringify: function (d) { - var l = d.words, - p = d.sigBytes, - t = this._map; - d.clamp(); - d = []; - for (var r = 0; r < p; r += 3) - for ( - var w = - (((l[r >>> 2] >>> (24 - 8 * (r % 4))) & 255) << 16) | - (((l[(r + 1) >>> 2] >>> (24 - 8 * ((r + 1) % 4))) & 255) << 8) | - ((l[(r + 2) >>> 2] >>> (24 - 8 * ((r + 2) % 4))) & 255), - v = 0; - 4 > v && r + 0.75 * v < p; - v++ - ) - d.push(t.charAt((w >>> (6 * (3 - v))) & 63)); - if ((l = t.charAt(64))) for (; d.length % 4; ) d.push(l); - return d.join(''); - }, - parse: function (d) { - var l = d.length, - s = this._map, - t = s.charAt(64); - t && ((t = d.indexOf(t)), -1 != t && (l = t)); - for (var t = [], r = 0, w = 0; w < l; w++) - if (w % 4) { - var v = s.indexOf(d.charAt(w - 1)) << (2 * (w % 4)), - b = s.indexOf(d.charAt(w)) >>> (6 - 2 * (w % 4)); - t[r >>> 2] |= (v | b) << (24 - 8 * (r % 4)); - r++; - } - return p.create(t, r); - }, - _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', - }; - })(); - - // BlockCipher - (function (u) { - function p(b, n, a, c, e, j, k) { - b = b + ((n & a) | (~n & c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function d(b, n, a, c, e, j, k) { - b = b + ((n & c) | (a & ~c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function l(b, n, a, c, e, j, k) { - b = b + (n ^ a ^ c) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function s(b, n, a, c, e, j, k) { - b = b + (a ^ (n | ~c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - for (var t = CryptoJS, r = t.lib, w = r.WordArray, v = r.Hasher, r = t.algo, b = [], x = 0; 64 > x; x++) - b[x] = (4294967296 * u.abs(u.sin(x + 1))) | 0; - r = r.MD5 = v.extend({ - _doReset: function () { - this._hash = new w.init([1732584193, 4023233417, 2562383102, 271733878]); - }, - _doProcessBlock: function (q, n) { - for (var a = 0; 16 > a; a++) { - var c = n + a, - e = q[c]; - q[c] = (((e << 8) | (e >>> 24)) & 16711935) | (((e << 24) | (e >>> 8)) & 4278255360); - } - var a = this._hash.words, - c = q[n + 0], - e = q[n + 1], - j = q[n + 2], - k = q[n + 3], - z = q[n + 4], - r = q[n + 5], - t = q[n + 6], - w = q[n + 7], - v = q[n + 8], - A = q[n + 9], - B = q[n + 10], - C = q[n + 11], - u = q[n + 12], - D = q[n + 13], - E = q[n + 14], - x = q[n + 15], - f = a[0], - m = a[1], - g = a[2], - h = a[3], - f = p(f, m, g, h, c, 7, b[0]), - h = p(h, f, m, g, e, 12, b[1]), - g = p(g, h, f, m, j, 17, b[2]), - m = p(m, g, h, f, k, 22, b[3]), - f = p(f, m, g, h, z, 7, b[4]), - h = p(h, f, m, g, r, 12, b[5]), - g = p(g, h, f, m, t, 17, b[6]), - m = p(m, g, h, f, w, 22, b[7]), - f = p(f, m, g, h, v, 7, b[8]), - h = p(h, f, m, g, A, 12, b[9]), - g = p(g, h, f, m, B, 17, b[10]), - m = p(m, g, h, f, C, 22, b[11]), - f = p(f, m, g, h, u, 7, b[12]), - h = p(h, f, m, g, D, 12, b[13]), - g = p(g, h, f, m, E, 17, b[14]), - m = p(m, g, h, f, x, 22, b[15]), - f = d(f, m, g, h, e, 5, b[16]), - h = d(h, f, m, g, t, 9, b[17]), - g = d(g, h, f, m, C, 14, b[18]), - m = d(m, g, h, f, c, 20, b[19]), - f = d(f, m, g, h, r, 5, b[20]), - h = d(h, f, m, g, B, 9, b[21]), - g = d(g, h, f, m, x, 14, b[22]), - m = d(m, g, h, f, z, 20, b[23]), - f = d(f, m, g, h, A, 5, b[24]), - h = d(h, f, m, g, E, 9, b[25]), - g = d(g, h, f, m, k, 14, b[26]), - m = d(m, g, h, f, v, 20, b[27]), - f = d(f, m, g, h, D, 5, b[28]), - h = d(h, f, m, g, j, 9, b[29]), - g = d(g, h, f, m, w, 14, b[30]), - m = d(m, g, h, f, u, 20, b[31]), - f = l(f, m, g, h, r, 4, b[32]), - h = l(h, f, m, g, v, 11, b[33]), - g = l(g, h, f, m, C, 16, b[34]), - m = l(m, g, h, f, E, 23, b[35]), - f = l(f, m, g, h, e, 4, b[36]), - h = l(h, f, m, g, z, 11, b[37]), - g = l(g, h, f, m, w, 16, b[38]), - m = l(m, g, h, f, B, 23, b[39]), - f = l(f, m, g, h, D, 4, b[40]), - h = l(h, f, m, g, c, 11, b[41]), - g = l(g, h, f, m, k, 16, b[42]), - m = l(m, g, h, f, t, 23, b[43]), - f = l(f, m, g, h, A, 4, b[44]), - h = l(h, f, m, g, u, 11, b[45]), - g = l(g, h, f, m, x, 16, b[46]), - m = l(m, g, h, f, j, 23, b[47]), - f = s(f, m, g, h, c, 6, b[48]), - h = s(h, f, m, g, w, 10, b[49]), - g = s(g, h, f, m, E, 15, b[50]), - m = s(m, g, h, f, r, 21, b[51]), - f = s(f, m, g, h, u, 6, b[52]), - h = s(h, f, m, g, k, 10, b[53]), - g = s(g, h, f, m, B, 15, b[54]), - m = s(m, g, h, f, e, 21, b[55]), - f = s(f, m, g, h, v, 6, b[56]), - h = s(h, f, m, g, x, 10, b[57]), - g = s(g, h, f, m, t, 15, b[58]), - m = s(m, g, h, f, D, 21, b[59]), - f = s(f, m, g, h, z, 6, b[60]), - h = s(h, f, m, g, C, 10, b[61]), - g = s(g, h, f, m, j, 15, b[62]), - m = s(m, g, h, f, A, 21, b[63]); - a[0] = (a[0] + f) | 0; - a[1] = (a[1] + m) | 0; - a[2] = (a[2] + g) | 0; - a[3] = (a[3] + h) | 0; - }, - _doFinalize: function () { - var b = this._data, - n = b.words, - a = 8 * this._nDataBytes, - c = 8 * b.sigBytes; - n[c >>> 5] |= 128 << (24 - (c % 32)); - var e = u.floor(a / 4294967296); - n[(((c + 64) >>> 9) << 4) + 15] = (((e << 8) | (e >>> 24)) & 16711935) | (((e << 24) | (e >>> 8)) & 4278255360); - n[(((c + 64) >>> 9) << 4) + 14] = (((a << 8) | (a >>> 24)) & 16711935) | (((a << 24) | (a >>> 8)) & 4278255360); - b.sigBytes = 4 * (n.length + 1); - this._process(); - b = this._hash; - n = b.words; - for (a = 0; 4 > a; a++) - (c = n[a]), (n[a] = (((c << 8) | (c >>> 24)) & 16711935) | (((c << 24) | (c >>> 8)) & 4278255360)); - return b; - }, - clone: function () { - var b = v.clone.call(this); - b._hash = this._hash.clone(); - return b; - }, - }); - t.MD5 = v._createHelper(r); - t.HmacMD5 = v._createHmacHelper(r); - })(Math); - (function () { - var u = CryptoJS, - p = u.lib, - d = p.Base, - l = p.WordArray, - p = u.algo, - s = (p.EvpKDF = d.extend({ - cfg: d.extend({ keySize: 4, hasher: p.MD5, iterations: 1 }), - init: function (d) { - this.cfg = this.cfg.extend(d); - }, - compute: function (d, r) { - for ( - var p = this.cfg, s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; - u.length < q; - - ) { - n && s.update(n); - var n = s.update(d).finalize(r); - s.reset(); - for (var a = 1; a < p; a++) (n = s.finalize(n)), s.reset(); - b.concat(n); - } - b.sigBytes = 4 * q; - return b; - }, - })); - u.EvpKDF = function (d, l, p) { - return s.create(p).compute(d, l); - }; - })(); - - // Cipher - CryptoJS.lib.Cipher || - (function (u) { - var p = CryptoJS, - d = p.lib, - l = d.Base, - s = d.WordArray, - t = d.BufferedBlockAlgorithm, - r = p.enc.Base64, - w = p.algo.EvpKDF, - v = (d.Cipher = t.extend({ - cfg: l.extend(), - createEncryptor: function (e, a) { - return this.create(this._ENC_XFORM_MODE, e, a); - }, - createDecryptor: function (e, a) { - return this.create(this._DEC_XFORM_MODE, e, a); - }, - init: function (e, a, b) { - this.cfg = this.cfg.extend(b); - this._xformMode = e; - this._key = a; - this.reset(); - }, - reset: function () { - t.reset.call(this); - this._doReset(); - }, - process: function (e) { - this._append(e); - return this._process(); - }, - finalize: function (e) { - e && this._append(e); - return this._doFinalize(); - }, - keySize: 4, - ivSize: 4, - _ENC_XFORM_MODE: 1, - _DEC_XFORM_MODE: 2, - _createHelper: function (e) { - return { - encrypt: function (b, k, d) { - return ('string' == typeof k ? c : a).encrypt(e, b, k, d); - }, - decrypt: function (b, k, d) { - return ('string' == typeof k ? c : a).decrypt(e, b, k, d); - }, - }; - }, - })); - d.StreamCipher = v.extend({ - _doFinalize: function () { - return this._process(!0); - }, - blockSize: 1, - }); - var b = (p.mode = {}), - x = function (e, a, b) { - var c = this._iv; - c ? (this._iv = u) : (c = this._prevBlock); - for (var d = 0; d < b; d++) e[a + d] ^= c[d]; - }, - q = (d.BlockCipherMode = l.extend({ - createEncryptor: function (e, a) { - return this.Encryptor.create(e, a); - }, - createDecryptor: function (e, a) { - return this.Decryptor.create(e, a); - }, - init: function (e, a) { - this._cipher = e; - this._iv = a; - }, - })).extend(); - q.Encryptor = q.extend({ - processBlock: function (e, a) { - var b = this._cipher, - c = b.blockSize; - x.call(this, e, a, c); - b.encryptBlock(e, a); - this._prevBlock = e.slice(a, a + c); - }, - }); - q.Decryptor = q.extend({ - processBlock: function (e, a) { - var b = this._cipher, - c = b.blockSize, - d = e.slice(a, a + c); - b.decryptBlock(e, a); - x.call(this, e, a, c); - this._prevBlock = d; - }, - }); - b = b.CBC = q; - q = (p.pad = {}).Pkcs7 = { - pad: function (a, b) { - for ( - var c = 4 * b, c = c - (a.sigBytes % c), d = (c << 24) | (c << 16) | (c << 8) | c, l = [], n = 0; - n < c; - n += 4 - ) - l.push(d); - c = s.create(l, c); - a.concat(c); - }, - unpad: function (a) { - a.sigBytes -= a.words[(a.sigBytes - 1) >>> 2] & 255; - }, - }; - d.BlockCipher = v.extend({ - cfg: v.cfg.extend({ mode: b, padding: q }), - reset: function () { - v.reset.call(this); - var a = this.cfg, - b = a.iv, - a = a.mode; - if (this._xformMode == this._ENC_XFORM_MODE) var c = a.createEncryptor; - else (c = a.createDecryptor), (this._minBufferSize = 1); - this._mode = c.call(a, this, b && b.words); - }, - _doProcessBlock: function (a, b) { - this._mode.processBlock(a, b); - }, - _doFinalize: function () { - var a = this.cfg.padding; - if (this._xformMode == this._ENC_XFORM_MODE) { - a.pad(this._data, this.blockSize); - var b = this._process(!0); - } else (b = this._process(!0)), a.unpad(b); - return b; - }, - blockSize: 4, - }); - var n = (d.CipherParams = l.extend({ - init: function (a) { - this.mixIn(a); - }, - toString: function (a) { - return (a || this.formatter).stringify(this); - }, - })), - b = ((p.format = {}).OpenSSL = { - stringify: function (a) { - var b = a.ciphertext; - a = a.salt; - return (a ? s.create([1398893684, 1701076831]).concat(a).concat(b) : b).toString(r); - }, - parse: function (a) { - a = r.parse(a); - var b = a.words; - if (1398893684 == b[0] && 1701076831 == b[1]) { - var c = s.create(b.slice(2, 4)); - b.splice(0, 4); - a.sigBytes -= 16; - } - return n.create({ ciphertext: a, salt: c }); - }, - }), - a = (d.SerializableCipher = l.extend({ - cfg: l.extend({ format: b }), - encrypt: function (a, b, c, d) { - d = this.cfg.extend(d); - var l = a.createEncryptor(c, d); - b = l.finalize(b); - l = l.cfg; - return n.create({ - ciphertext: b, - key: c, - iv: l.iv, - algorithm: a, - mode: l.mode, - padding: l.padding, - blockSize: a.blockSize, - formatter: d.format, - }); - }, - decrypt: function (a, b, c, d) { - d = this.cfg.extend(d); - b = this._parse(b, d.format); - return a.createDecryptor(c, d).finalize(b.ciphertext); - }, - _parse: function (a, b) { - return 'string' == typeof a ? b.parse(a, this) : a; - }, - })), - p = ((p.kdf = {}).OpenSSL = { - execute: function (a, b, c, d) { - d || (d = s.random(8)); - a = w.create({ keySize: b + c }).compute(a, d); - c = s.create(a.words.slice(b), 4 * c); - a.sigBytes = 4 * b; - return n.create({ key: a, iv: c, salt: d }); - }, - }), - c = (d.PasswordBasedCipher = a.extend({ - cfg: a.cfg.extend({ kdf: p }), - encrypt: function (b, c, d, l) { - l = this.cfg.extend(l); - d = l.kdf.execute(d, b.keySize, b.ivSize); - l.iv = d.iv; - b = a.encrypt.call(this, b, c, d.key, l); - b.mixIn(d); - return b; - }, - decrypt: function (b, c, d, l) { - l = this.cfg.extend(l); - c = this._parse(c, l.format); - d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt); - l.iv = d.iv; - return a.decrypt.call(this, b, c, d.key, l); - }, - })); - })(); - - // AES - (function () { - for ( - var u = CryptoJS, - p = u.lib.BlockCipher, - d = u.algo, - l = [], - s = [], - t = [], - r = [], - w = [], - v = [], - b = [], - x = [], - q = [], - n = [], - a = [], - c = 0; - 256 > c; - c++ - ) - a[c] = 128 > c ? c << 1 : (c << 1) ^ 283; - for (var e = 0, j = 0, c = 0; 256 > c; c++) { - var k = j ^ (j << 1) ^ (j << 2) ^ (j << 3) ^ (j << 4), - k = (k >>> 8) ^ (k & 255) ^ 99; - l[e] = k; - s[k] = e; - var z = a[e], - F = a[z], - G = a[F], - y = (257 * a[k]) ^ (16843008 * k); - t[e] = (y << 24) | (y >>> 8); - r[e] = (y << 16) | (y >>> 16); - w[e] = (y << 8) | (y >>> 24); - v[e] = y; - y = (16843009 * G) ^ (65537 * F) ^ (257 * z) ^ (16843008 * e); - b[k] = (y << 24) | (y >>> 8); - x[k] = (y << 16) | (y >>> 16); - q[k] = (y << 8) | (y >>> 24); - n[k] = y; - e ? ((e = z ^ a[a[a[G ^ z]]]), (j ^= a[a[j]])) : (e = j = 1); - } - var H = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54], - d = (d.AES = p.extend({ - _doReset: function () { - for ( - var a = this._key, - c = a.words, - d = a.sigBytes / 4, - a = 4 * ((this._nRounds = d + 6) + 1), - e = (this._keySchedule = []), - j = 0; - j < a; - j++ - ) - if (j < d) e[j] = c[j]; - else { - var k = e[j - 1]; - j % d - ? 6 < d && - 4 == j % d && - (k = (l[k >>> 24] << 24) | (l[(k >>> 16) & 255] << 16) | (l[(k >>> 8) & 255] << 8) | l[k & 255]) - : ((k = (k << 8) | (k >>> 24)), - (k = (l[k >>> 24] << 24) | (l[(k >>> 16) & 255] << 16) | (l[(k >>> 8) & 255] << 8) | l[k & 255]), - (k ^= H[(j / d) | 0] << 24)); - e[j] = e[j - d] ^ k; - } - c = this._invKeySchedule = []; - for (d = 0; d < a; d++) - (j = a - d), - (k = d % 4 ? e[j] : e[j - 4]), - (c[d] = - 4 > d || 4 >= j ? k : b[l[k >>> 24]] ^ x[l[(k >>> 16) & 255]] ^ q[l[(k >>> 8) & 255]] ^ n[l[k & 255]]); - }, - encryptBlock: function (a, b) { - this._doCryptBlock(a, b, this._keySchedule, t, r, w, v, l); - }, - decryptBlock: function (a, c) { - var d = a[c + 1]; - a[c + 1] = a[c + 3]; - a[c + 3] = d; - this._doCryptBlock(a, c, this._invKeySchedule, b, x, q, n, s); - d = a[c + 1]; - a[c + 1] = a[c + 3]; - a[c + 3] = d; - }, - _doCryptBlock: function (a, b, c, d, e, j, l, f) { - for ( - var m = this._nRounds, - g = a[b] ^ c[0], - h = a[b + 1] ^ c[1], - k = a[b + 2] ^ c[2], - n = a[b + 3] ^ c[3], - p = 4, - r = 1; - r < m; - r++ - ) - var q = d[g >>> 24] ^ e[(h >>> 16) & 255] ^ j[(k >>> 8) & 255] ^ l[n & 255] ^ c[p++], - s = d[h >>> 24] ^ e[(k >>> 16) & 255] ^ j[(n >>> 8) & 255] ^ l[g & 255] ^ c[p++], - t = d[k >>> 24] ^ e[(n >>> 16) & 255] ^ j[(g >>> 8) & 255] ^ l[h & 255] ^ c[p++], - n = d[n >>> 24] ^ e[(g >>> 16) & 255] ^ j[(h >>> 8) & 255] ^ l[k & 255] ^ c[p++], - g = q, - h = s, - k = t; - q = ((f[g >>> 24] << 24) | (f[(h >>> 16) & 255] << 16) | (f[(k >>> 8) & 255] << 8) | f[n & 255]) ^ c[p++]; - s = ((f[h >>> 24] << 24) | (f[(k >>> 16) & 255] << 16) | (f[(n >>> 8) & 255] << 8) | f[g & 255]) ^ c[p++]; - t = ((f[k >>> 24] << 24) | (f[(n >>> 16) & 255] << 16) | (f[(g >>> 8) & 255] << 8) | f[h & 255]) ^ c[p++]; - n = ((f[n >>> 24] << 24) | (f[(g >>> 16) & 255] << 16) | (f[(h >>> 8) & 255] << 8) | f[k & 255]) ^ c[p++]; - a[b] = q; - a[b + 1] = s; - a[b + 2] = t; - a[b + 3] = n; - }, - keySize: 8, - })); - u.AES = p._createHelper(d); - })(); - - // Mode ECB - CryptoJS.mode.ECB = (function () { - var ECB = CryptoJS.lib.BlockCipherMode.extend(); - - ECB.Encryptor = ECB.extend({ - processBlock: function (words, offset) { - this._cipher.encryptBlock(words, offset); - }, - }); - - ECB.Decryptor = ECB.extend({ - processBlock: function (words, offset) { - this._cipher.decryptBlock(words, offset); - }, - }); - - return ECB; - })(); - - var hmacSha256 = CryptoJS; - - var CryptoJS$1 = /*@__PURE__*/getDefaultExportFromCjs(hmacSha256); + WebCryptoPrimitives.MAX_CACHED_KEYS = 8; /** * AES-CBC cryptor module. @@ -1925,34 +1229,24 @@ class AesCbcCryptor { constructor({ cipherKey }) { this.cipherKey = cipherKey; - this.CryptoJS = CryptoJS$1; - this.encryptedKey = this.CryptoJS.SHA256(cipherKey); } // -------------------------------------------------------- // --------------------- Encryption ----------------------- // -------------------------------------------------------- // region Encryption encrypt(data) { - const stringData = typeof data === 'string' ? data : AesCbcCryptor.decoder.decode(data); - if (stringData.length === 0) - throw new Error('encryption error. empty content'); - const abIv = this.getIv(); - return { - metadata: abIv, - data: decode(this.CryptoJS.AES.encrypt(data, this.encryptedKey, { - iv: this.bufferToWordArray(abIv), - mode: this.CryptoJS.mode.CBC, - }).ciphertext.toString(this.CryptoJS.enc.Base64)), - }; + return __awaiter(this, void 0, void 0, function* () { + const plaintext = typeof data === 'string' ? AesCbcCryptor.encoder.encode(data) : new Uint8Array(data); + if (plaintext.byteLength === 0) + throw new Error('encryption error. empty content'); + return this.encryptFileData(plaintext); + }); } encryptFileData(data) { return __awaiter(this, void 0, void 0, function* () { - const key = yield this.getKey(); const iv = this.getIv(); - return { - data: yield crypto.subtle.encrypt({ name: this.algo, iv: iv }, key, data), - metadata: iv, - }; + const encrypted = yield AesCbcCryptor.primitives.aesCbcEncrypt(yield this.getKey(), iv, data instanceof Uint8Array ? data : new Uint8Array(data)); + return { data: toArrayBuffer(encrypted), metadata: iv.buffer }; }); } // endregion @@ -1961,21 +1255,23 @@ // -------------------------------------------------------- // region Decryption decrypt(encryptedData) { - if (typeof encryptedData.data === 'string') - throw new Error('Decryption error: data for decryption should be ArrayBuffed.'); - const iv = this.bufferToWordArray(new Uint8ClampedArray(encryptedData.metadata)); - const data = this.bufferToWordArray(new Uint8ClampedArray(encryptedData.data)); - return AesCbcCryptor.encoder.encode(this.CryptoJS.AES.decrypt({ ciphertext: data }, this.encryptedKey, { - iv, - mode: this.CryptoJS.mode.CBC, - }).toString(this.CryptoJS.enc.Utf8)).buffer; + return __awaiter(this, void 0, void 0, function* () { + try { + return yield this.decryptFileData(encryptedData); + } + catch (error) { + if (error instanceof Error && error.message.startsWith('Decryption error')) + throw error; + throw new Error(`Decryption error: ${error instanceof Error ? error.message : 'unable to decrypt data'}`); + } + }); } decryptFileData(encryptedData) { return __awaiter(this, void 0, void 0, function* () { if (typeof encryptedData.data === 'string') throw new Error('Decryption error: data for decryption should be ArrayBuffed.'); - const key = yield this.getKey(); - return crypto.subtle.decrypt({ name: this.algo, iv: encryptedData.metadata }, key, encryptedData.data); + const decrypted = yield AesCbcCryptor.primitives.aesCbcDecrypt(yield this.getKey(), new Uint8Array(encryptedData.metadata), new Uint8Array(encryptedData.data)); + return toArrayBuffer(decrypted); }); } // endregion @@ -1986,70 +1282,181 @@ get identifier() { return 'ACRH'; } - /** - * Cryptor algorithm. - * - * @returns Cryptor module algorithm. - */ - get algo() { - return 'AES-CBC'; - } /** * Generate random initialization vector. * * @returns Random initialization vector. + * + * @internal */ getIv() { - return crypto.getRandomValues(new Uint8Array(AesCbcCryptor.BLOCK_SIZE)); + return AesCbcCryptor.primitives.randomBytes(AesCbcCryptor.BLOCK_SIZE); } /** - * Convert cipher key to the {@link Buffer}. + * Compute raw encryption key (cached). * - * @returns SHA256 encoded cipher key {@link Buffer}. + * @returns SHA-256 digest of the cipher key. + * + * @internal */ getKey() { + if (!this.key) { + this.key = AesCbcCryptor.primitives.sha256(AesCbcCryptor.encoder.encode(this.cipherKey)); + this.key.catch(() => (this.key = undefined)); + } + return this.key; + } + // endregion + /** + * Serialize cryptor information to string. + * + * @returns Serialized cryptor information. + */ + toString() { + return 'AesCbcCryptor {}'; + } + } + /** + * Cryptor block size. + */ + AesCbcCryptor.BLOCK_SIZE = 16; + /** + * {@link string|String} to {@link ArrayBuffer} response decoder. + */ + AesCbcCryptor.encoder = new TextEncoder(); + /** + * {@link ArrayBuffer} to {@link string} decoder. + */ + AesCbcCryptor.decoder = new TextDecoder(); + /** + * Native crypto primitives. + * + * @internal + */ + AesCbcCryptor.primitives = new WebCryptoPrimitives(); + + /** + * AES-GCM cryptor module. + */ + /** + * AES-GCM cryptor. + * + * Decrypt-only AES-256-GCM cryptor for payloads produced by other PubNub SDKs. Cryptor metadata stored in the + * header is `nonce (12 bytes)` and the authentication tag (16 bytes) is appended to the ciphertext: + * `ciphertext || authentication tag`. + * + * **Note:** Encryption isn't supported and cryptor can't be used as crypto module default cryptor. + */ + class AesGcmCryptor { + constructor({ cipherKey }) { + this.cipherKey = cipherKey; + } + // -------------------------------------------------------- + // --------------------- Encryption ----------------------- + // -------------------------------------------------------- + // region Encryption + encrypt(_data) { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + } + encryptFileData(_data) { + return __awaiter(this, void 0, void 0, function* () { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + }); + } + // endregion + // -------------------------------------------------------- + // --------------------- Decryption ----------------------- + // -------------------------------------------------------- + // region Decryption + decrypt(encryptedData) { + return __awaiter(this, void 0, void 0, function* () { + return this.decryptFileData(encryptedData); + }); + } + decryptFileData(encryptedData) { return __awaiter(this, void 0, void 0, function* () { - const bKey = AesCbcCryptor.encoder.encode(this.cipherKey); - const abHash = yield crypto.subtle.digest('SHA-256', bKey.buffer); - return crypto.subtle.importKey('raw', abHash, this.algo, true, ['encrypt', 'decrypt']); + if (typeof encryptedData.data === 'string') + throw new Error('Decryption error: data for decryption should be ArrayBuffed.'); + if (!encryptedData.metadata || encryptedData.metadata.byteLength !== AesGcmCryptor.METADATA_LENGTH) + throw new Error('Decryption error: invalid metadata'); + if (encryptedData.data.byteLength <= AesGcmCryptor.TAG_LENGTH) + throw new Error('Decryption error: empty content'); + try { + const decrypted = yield AesGcmCryptor.primitives.aesGcmDecrypt(yield this.getKey(), new Uint8Array(encryptedData.metadata), new Uint8Array(encryptedData.data)); + return toArrayBuffer(decrypted); + } + catch (_error) { + // Native error intentionally not exposed: wrong key and modified payload should be indistinguishable. + throw new Error(AesGcmCryptor.DECRYPTION_ERROR); + } }); } + // endregion + // -------------------------------------------------------- + // ----------------------- Helpers ------------------------ + // -------------------------------------------------------- + // region Helpers + get identifier() { + return AesGcmCryptor.IDENTIFIER; + } /** - * Convert bytes array to words array. + * Compute raw decryption key (cached). * - * @param b - Bytes array (buffer) which should be converted. + * @returns SHA-256 digest of the cipher key. * - * @returns Word sized array. + * @internal */ - bufferToWordArray(b) { - const wa = []; - let i; - for (i = 0; i < b.length; i += 1) { - wa[(i / 4) | 0] |= b[i] << (24 - 8 * i); + getKey() { + if (!this.key) { + this.key = AesGcmCryptor.primitives.sha256(AesGcmCryptor.encoder.encode(this.cipherKey)); + this.key.catch(() => (this.key = undefined)); } - return this.CryptoJS.lib.WordArray.create(wa, b.length); + return this.key; } + // endregion /** * Serialize cryptor information to string. * * @returns Serialized cryptor information. */ toString() { - return 'AesCbcCryptor {}'; + return 'AesGcmCryptor {}'; } } /** - * Cryptor block size. + * Cryptor unique identifier. */ - AesCbcCryptor.BLOCK_SIZE = 16; + AesGcmCryptor.IDENTIFIER = 'ACGM'; + /** + * Nonce length. + */ + AesGcmCryptor.NONCE_LENGTH = 12; + /** + * Authentication tag length. + */ + AesGcmCryptor.TAG_LENGTH = 16; + /** + * Cryptor metadata length (nonce only). + */ + AesGcmCryptor.METADATA_LENGTH = AesGcmCryptor.NONCE_LENGTH; + /** + * Generic decryption error message (doesn't reveal which part of the payload failed authentication). + */ + AesGcmCryptor.DECRYPTION_ERROR = 'Decryption error: unable to decrypt data'; + /** + * Unsupported encryption error message. + */ + AesGcmCryptor.ENCRYPTION_ERROR = 'Encryption error: AES-GCM cryptor (ACGM) supports decryption only.'; /** * {@link string|String} to {@link ArrayBuffer} response decoder. */ - AesCbcCryptor.encoder = new TextEncoder(); + AesGcmCryptor.encoder = new TextEncoder(); /** - * {@link ArrayBuffer} to {@link string} decoder. + * Native crypto primitives. + * + * @internal */ - AesCbcCryptor.decoder = new TextDecoder(); + AesGcmCryptor.primitives = new WebCryptoPrimitives(); /** * PubNub package utilities module. @@ -2237,62 +1644,43 @@ mfp[0] = (mfp[0] << 5) - mfp[0] + msg.charCodeAt(walk++); return mfp[0].toString(16).padStart(8, '0'); }; - /** - * Legacy cryptography module. + * Encrypt message payload which should be sent as part of request URL or body. + * + * @param crypto - Module which should be used to encrypt data. + * @param payload - User-provided data which should be encrypted. + * + * @returns Stringified encrypted payload (base64 encoded if encryption result is binary). + * + * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. * * @internal */ + const encryptMessagePayload = (crypto, payload) => __awaiter(void 0, void 0, void 0, function* () { + const encrypted = yield crypto.encrypt(JSON.stringify(payload)); + return JSON.stringify(typeof encrypted === 'string' ? encrypted : encode(encrypted)); + }); + /** - * Convert bytes array to words array. - * - * @param b - Bytes array (buffer) which should be converted. - * - * @returns Word sized array. + * Legacy cryptography module. * * @internal */ - /* eslint-disable @typescript-eslint/no-explicit-any */ - function bufferToWordArray(b) { - const wa = []; - let i; - for (i = 0; i < b.length; i += 1) { - wa[(i / 4) | 0] |= b[i] << (24 - 8 * i); - } - // @ts-expect-error Bundled library without types. - return CryptoJS$1.lib.WordArray.create(wa, b.length); - } /** - * Legacy cryptography module for files and signature. + * Legacy cryptography module for data encryption / decryption. + * + * Wire format (compatible with all PubNub SDKs): + * - key: first 32 characters of HEX encoded SHA-256 digest of the cipher key, used as UTF-8 bytes (AES-256), + * - cipher: AES-CBC with PKCS#7 padding, + * - random IV: `base64(iv + ciphertext)`, static IV (`0123456789012345`): `base64(ciphertext)`. * * @internal */ - class Crypto { - constructor(configuration) { + class LegacyCrypto { + constructor(configuration, primitives) { this.configuration = configuration; - /** - * Crypto initialization vector. - */ - this.iv = '0123456789012345'; - /** - * List os allowed cipher key encodings. - */ - this.allowedKeyEncodings = ['hex', 'utf8', 'base64', 'binary']; - /** - * Allowed cipher key lengths. - */ - this.allowedKeyLengths = [128, 256]; - /** - * Allowed crypto modes. - */ - this.allowedModes = ['ecb', 'cbc']; + this.primitives = primitives; this.logger = configuration.logger; - this.defaultOptions = { - encryptKey: true, - keyEncoding: 'utf8', - keyLength: 256, - mode: 'cbc', - }; } /** * Update registered loggers' manager. @@ -2320,253 +1708,165 @@ get logger() { return this._logger; } - /** - * Generate HMAC-SHA256 hash from input data. - * - * @param data - Data from which hash should be generated. - * - * @returns HMAC-SHA256 hash from provided `data`. - */ - HMACSHA256(data) { - // @ts-expect-error Bundled library without types. - const hash = CryptoJS$1.HmacSHA256(data, this.configuration.secretKey); - // @ts-expect-error Bundled library without types. - return hash.toString(CryptoJS$1.enc.Base64); - } - /** - * Generate SHA256 hash from input data. - * - * @param data - Data from which hash should be generated. - * - * @returns SHA256 hash from provided `data`. - */ - SHA256(data) { - // @ts-expect-error Bundled library without types. - return CryptoJS$1.SHA256(data).toString(CryptoJS$1.enc.Hex); - } /** * Encrypt provided data. * * @param data - Source data which should be encrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Encrypted `data`. */ - encrypt(data, customCipherKey, options) { - if (this.configuration.customEncrypt) { - if (this.logger) - this.logger.warn('Crypto', "'customEncrypt' is deprecated. Consult docs for better alternative."); - return this.configuration.customEncrypt(data); - } - return this.pnEncrypt(data, customCipherKey, options); + encrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + if (this.configuration.customEncrypt) { + if (this.logger) + this.logger.warn('Crypto', "'customEncrypt' is deprecated. Consult docs for better alternative."); + return this.configuration.customEncrypt(data); + } + return this.pnEncrypt(typeof data === 'string' ? data : JSON.stringify(data), customCipherKey); + }); } /** * Decrypt provided data. * * @param data - Encrypted data which should be decrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Decrypted `data`. */ - decrypt(data, customCipherKey, options) { - if (this.configuration.customDecrypt) { - if (this.logger) - this.logger.warn('Crypto', "'customDecrypt' is deprecated. Consult docs for better alternative."); - return this.configuration.customDecrypt(data); - } - return this.pnDecrypt(data, customCipherKey, options); + decrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + if (this.configuration.customDecrypt) { + if (this.logger) + this.logger.warn('Crypto', "'customDecrypt' is deprecated. Consult docs for better alternative."); + return this.configuration.customDecrypt(data); + } + return this.pnDecrypt(data, customCipherKey); + }); } /** * Encrypt provided data. * * @param data - Source data which should be encrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Encrypted `data` as string. */ - pnEncrypt(data, customCipherKey, options) { - const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; - if (!decidedCipherKey) - return data; - if (this.logger) { - this.logger.debug('Crypto', () => ({ - messageType: 'object', - message: Object.assign({ data }, (options !== null && options !== void 0 ? options : {})), - details: 'Encrypt with parameters:', - ignoredKeys: isSensitiveLogKey, - })); - } - options = this.parseOptions(options); - const mode = this.getMode(options); - const cipherKey = this.getPaddedKey(decidedCipherKey, options); - if (this.configuration.useRandomIVs) { - const waIv = this.getRandomIV(); - // @ts-expect-error Bundled library without types. - const waPayload = CryptoJS$1.AES.encrypt(data, cipherKey, { iv: waIv, mode }).ciphertext; - // @ts-expect-error Bundled library without types. - return waIv.clone().concat(waPayload.clone()).toString(CryptoJS$1.enc.Base64); - } - const iv = this.getIV(options); - // @ts-expect-error Bundled library without types. - const encryptedHexArray = CryptoJS$1.AES.encrypt(data, cipherKey, { iv, mode }).ciphertext; - // @ts-expect-error Bundled library without types. - const base64Encrypted = encryptedHexArray.toString(CryptoJS$1.enc.Base64); - return base64Encrypted || data; + pnEncrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; + if (!decidedCipherKey) + return data; + if (this.logger) { + this.logger.debug('Crypto', () => ({ + messageType: 'object', + message: { data }, + details: 'Encrypt with parameters:', + ignoredKeys: isSensitiveLogKey, + })); + } + const key = yield this.getKey(decidedCipherKey); + const plaintext = LegacyCrypto.encoder.encode(data); + if (this.configuration.useRandomIVs) { + const iv = this.primitives.randomBytes(LegacyCrypto.IV_LENGTH); + const ciphertext = yield this.primitives.aesCbcEncrypt(key, iv, plaintext); + return encode(this.concat(iv, ciphertext)); + } + const ciphertext = yield this.primitives.aesCbcEncrypt(key, LegacyCrypto.staticIV, plaintext); + return encode(toArrayBuffer(ciphertext)) || data; + }); } /** * Decrypt provided data. * * @param data - Encrypted data which should be decrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * - * @returns Decrypted `data`. + * @returns Decrypted `data` or `null` if it can't be decrypted. */ - pnDecrypt(data, customCipherKey, options) { - const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; - if (!decidedCipherKey) - return data; - if (this.logger) { - this.logger.debug('Crypto', () => ({ - messageType: 'object', - message: Object.assign({ data }, (options !== null && options !== void 0 ? options : {})), - details: 'Decrypt with parameters:', - ignoredKeys: isSensitiveLogKey, - })); - } - options = this.parseOptions(options); - const mode = this.getMode(options); - const cipherKey = this.getPaddedKey(decidedCipherKey, options); - if (this.configuration.useRandomIVs) { - const ciphertext = new Uint8ClampedArray(decode(data)); - const iv = bufferToWordArray(ciphertext.slice(0, 16)); - const payload = bufferToWordArray(ciphertext.slice(16)); - try { - // @ts-expect-error Bundled library without types. - const plainJSON = CryptoJS$1.AES.decrypt({ ciphertext: payload }, cipherKey, { iv, mode }).toString( - // @ts-expect-error Bundled library without types. - CryptoJS$1.enc.Utf8); - return JSON.parse(plainJSON); - } - catch (e) { - if (this.logger) - this.logger.error('Crypto', () => ({ messageType: 'error', message: e })); - return null; + pnDecrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; + if (!decidedCipherKey) + return data; + if (this.logger) { + this.logger.debug('Crypto', () => ({ + messageType: 'object', + message: { data }, + details: 'Decrypt with parameters:', + ignoredKeys: isSensitiveLogKey, + })); } - } - else { - const iv = this.getIV(options); try { - // @ts-expect-error Bundled library without types. - const ciphertext = CryptoJS$1.enc.Base64.parse(data); - // @ts-expect-error Bundled library without types. - const plainJSON = CryptoJS$1.AES.decrypt({ ciphertext }, cipherKey, { iv, mode }).toString(CryptoJS$1.enc.Utf8); - return JSON.parse(plainJSON); + const key = yield this.getKey(decidedCipherKey); + const encrypted = new Uint8Array(decode(data)); + let iv = LegacyCrypto.staticIV; + let ciphertext = encrypted; + if (this.configuration.useRandomIVs) { + iv = encrypted.slice(0, LegacyCrypto.IV_LENGTH); + ciphertext = encrypted.slice(LegacyCrypto.IV_LENGTH); + } + const plaintext = yield this.primitives.aesCbcDecrypt(key, iv, ciphertext); + return JSON.parse(decodeUtf8Strict(plaintext)); } catch (e) { if (this.logger) this.logger.error('Crypto', () => ({ messageType: 'error', message: e })); return null; } - } - } - /** - * Pre-process provided custom crypto configuration. - * - * @param incomingOptions - Configuration which should be pre-processed before use. - * - * @returns Normalized crypto configuration options. - */ - parseOptions(incomingOptions) { - var _a, _b, _c, _d; - if (!incomingOptions) - return this.defaultOptions; - // Defaults - const options = { - encryptKey: (_a = incomingOptions.encryptKey) !== null && _a !== void 0 ? _a : this.defaultOptions.encryptKey, - keyEncoding: (_b = incomingOptions.keyEncoding) !== null && _b !== void 0 ? _b : this.defaultOptions.keyEncoding, - keyLength: (_c = incomingOptions.keyLength) !== null && _c !== void 0 ? _c : this.defaultOptions.keyLength, - mode: (_d = incomingOptions.mode) !== null && _d !== void 0 ? _d : this.defaultOptions.mode, - }; - // Validation - if (this.allowedKeyEncodings.indexOf(options.keyEncoding.toLowerCase()) === -1) - options.keyEncoding = this.defaultOptions.keyEncoding; - if (this.allowedKeyLengths.indexOf(options.keyLength) === -1) - options.keyLength = this.defaultOptions.keyLength; - if (this.allowedModes.indexOf(options.mode.toLowerCase()) === -1) - options.mode = this.defaultOptions.mode; - return options; - } - /** - * Decode provided cipher key. - * - * @param key - Key in `encoding` provided by `options`. - * @param options - Crypto configuration options with cipher key details. - * - * @returns Array buffer with decoded key. - */ - decodeKey(key, options) { - // @ts-expect-error Bundled library without types. - if (options.keyEncoding === 'base64') - return CryptoJS$1.enc.Base64.parse(key); - // @ts-expect-error Bundled library without types. - if (options.keyEncoding === 'hex') - return CryptoJS$1.enc.Hex.parse(key); - return key; - } - /** - * Add padding to the cipher key. - * - * @param key - Key which should be padded. - * @param options - Crypto configuration options with cipher key details. - * - * @returns Properly padded cipher key. - */ - getPaddedKey(key, options) { - key = this.decodeKey(key, options); - // @ts-expect-error Bundled library without types. - if (options.encryptKey) - return CryptoJS$1.enc.Utf8.parse(this.SHA256(key).slice(0, 32)); - return key; + }); } /** - * Cipher mode. + * Derive AES-256 key from the cipher key (cached for configured cipher key). * - * @param options - Crypto configuration with information about cipher mode. + * @param cipherKey - User-provided cipher key. * - * @returns Crypto cipher mode. + * @returns First 32 characters of HEX encoded SHA-256 digest as UTF-8 bytes. */ - getMode(options) { - // @ts-expect-error Bundled library without types. - if (options.mode === 'ecb') - return CryptoJS$1.mode.ECB; - // @ts-expect-error Bundled library without types. - return CryptoJS$1.mode.CBC; + getKey(cipherKey) { + if (cipherKey !== this.configuration.cipherKey) + return this.deriveKey(cipherKey); + if (!this.configuredKey) { + this.configuredKey = this.deriveKey(cipherKey); + this.configuredKey.catch(() => (this.configuredKey = undefined)); + } + return this.configuredKey; } /** - * Cipher initialization vector. + * Derive AES-256 key from the cipher key. * - * @param options - Crypto configuration with information about cipher mode. + * @param cipherKey - User-provided cipher key. * - * @returns Initialization vector. + * @returns First 32 characters of HEX encoded SHA-256 digest as UTF-8 bytes. */ - getIV(options) { - // @ts-expect-error Bundled library without types. - return options.mode === 'cbc' ? CryptoJS$1.enc.Utf8.parse(this.iv) : null; + deriveKey(cipherKey) { + return __awaiter(this, void 0, void 0, function* () { + const digest = yield this.primitives.sha256(LegacyCrypto.encoder.encode(cipherKey)); + return LegacyCrypto.encoder.encode(bytesToHex(digest).slice(0, 32)); + }); } /** - * Random initialization vector. - * - * @returns Generated random initialization vector. + * Join two byte arrays into {@link ArrayBuffer}. */ - getRandomIV() { - // @ts-expect-error Bundled library without types. - return CryptoJS$1.lib.WordArray.random(16); + concat(a, b) { + const result = new Uint8Array(a.byteLength + b.byteLength); + result.set(a, 0); + result.set(b, a.byteLength); + return result.buffer; } } + /** + * `string` to {@link Uint8Array} encoder. + */ + LegacyCrypto.encoder = new TextEncoder(); + /** + * Static crypto initialization vector (used when random IVs are disabled). + */ + LegacyCrypto.staticIV = new TextEncoder().encode('0123456789012345'); + /** + * Initialization vector length. + */ + LegacyCrypto.IV_LENGTH = 16; /** * Legacy browser cryptography module. @@ -2799,7 +2099,7 @@ class LegacyCryptor { constructor(config) { this.config = config; - this.cryptor = new Crypto(Object.assign({}, config)); + this.cryptor = new LegacyCrypto(Object.assign({}, config), new WebCryptoPrimitives()); this.fileCryptor = new WebCryptography(); } /** @@ -2819,11 +2119,13 @@ // -------------------------------------------------------- // region Encryption encrypt(data) { - const stringData = typeof data === 'string' ? data : LegacyCryptor.decoder.decode(data); - return { - data: this.cryptor.encrypt(stringData), - metadata: null, - }; + return __awaiter(this, void 0, void 0, function* () { + const stringData = typeof data === 'string' ? data : LegacyCryptor.decoder.decode(data); + return { + data: yield this.cryptor.encrypt(stringData), + metadata: null, + }; + }); } encryptFile(file, File) { return __awaiter(this, void 0, void 0, function* () { @@ -2839,8 +2141,10 @@ // -------------------------------------------------------- // region Decryption decrypt(encryptedData) { - const data = typeof encryptedData.data === 'string' ? encryptedData.data : encode(encryptedData.data); - return this.cryptor.decrypt(data); + return __awaiter(this, void 0, void 0, function* () { + const data = typeof encryptedData.data === 'string' ? encryptedData.data : encode(encryptedData.data); + return this.cryptor.decrypt(data); + }); } decryptFile(file, File) { return __awaiter(this, void 0, void 0, function* () { @@ -2930,7 +2234,10 @@ } return new WebCryptoModule({ default: new LegacyCryptor(Object.assign(Object.assign({}, config), { useRandomIVs: (_a = config.useRandomIVs) !== null && _a !== void 0 ? _a : true })), - cryptors: [new AesCbcCryptor({ cipherKey: config.cipherKey })], + cryptors: [ + new AesCbcCryptor({ cipherKey: config.cipherKey }), + new AesGcmCryptor({ cipherKey: config.cipherKey }), + ], }); } static aesCbcCryptoModule(config) { @@ -2941,9 +2248,35 @@ default: new AesCbcCryptor({ cipherKey: config.cipherKey }), cryptors: [ new LegacyCryptor(Object.assign(Object.assign({}, config), { useRandomIVs: (_a = config.useRandomIVs) !== null && _a !== void 0 ? _a : true })), + new AesGcmCryptor({ cipherKey: config.cipherKey }), ], }); } + /** + * Construct decrypt-only AES-GCM cryptor. + * + * Modules created with {@link aesCbcCryptoModule}, {@link legacyCryptoModule} or `cipherKey` already register it. + * Use it to let a custom crypto module decrypt AES-GCM (`ACGM`) payloads produced by other PubNub SDKs: + * + * @example + * ```typescript + * const cryptoModule = new PubNub.CryptoModule({ + * default: myCryptor, + * cryptors: [PubNub.CryptoModule.aesGcmCryptor({ cipherKey: 'my-key' })], + * }); + * ``` + * + * @param config - Cryptor configuration options. + * + * @returns AES-GCM cryptor which can be used only as an additional (decryption) cryptor. + * + * @throws Error if `config.cipherKey` not set. + */ + static aesGcmCryptor(config) { + if (!config.cipherKey) + throw new PubNubError('Crypto module error: cipher key not set.'); + return new AesGcmCryptor({ cipherKey: config.cipherKey }); + } /** * Construct crypto module with `cryptor` as default for data encryption and decryption. * @@ -2960,16 +2293,18 @@ // -------------------------------------------------------- // region Encryption encrypt(data) { - // Encrypt data. - const encrypted = data instanceof ArrayBuffer && this.defaultCryptor.identifier === WebCryptoModule.LEGACY_IDENTIFIER - ? this.defaultCryptor.encrypt(WebCryptoModule.decoder.decode(data)) - : this.defaultCryptor.encrypt(data); - if (!encrypted.metadata) - return encrypted.data; - else if (typeof encrypted.data === 'string') - throw new Error('Encryption error: encrypted data should be ArrayBuffed.'); - const headerData = this.getHeaderData(encrypted); - return this.concatArrayBuffer(headerData, encrypted.data); + return __awaiter(this, void 0, void 0, function* () { + // Encrypt data. + const encrypted = data instanceof ArrayBuffer && this.defaultCryptor.identifier === WebCryptoModule.LEGACY_IDENTIFIER + ? yield this.defaultCryptor.encrypt(WebCryptoModule.decoder.decode(data)) + : yield this.defaultCryptor.encrypt(data); + if (!encrypted.metadata) + return encrypted.data; + else if (typeof encrypted.data === 'string') + throw new Error('Encryption error: encrypted data should be ArrayBuffed.'); + const headerData = this.getHeaderData(encrypted); + return this.concatArrayBuffer(headerData, encrypted.data); + }); } encryptFile(file, File) { return __awaiter(this, void 0, void 0, function* () { @@ -2996,17 +2331,19 @@ // -------------------------------------------------------- // region Decryption decrypt(data) { - const encryptedData = typeof data === 'string' ? decode(data) : data; - const header = CryptorHeader.tryParse(encryptedData); - const cryptor = this.getCryptor(header); - const metadata = header.length > 0 - ? encryptedData.slice(header.length - header.metadataLength, header.length) - : null; - if (encryptedData.slice(header.length).byteLength <= 0) - throw new Error('Decryption error: empty content'); - return cryptor.decrypt({ - data: encryptedData.slice(header.length), - metadata: metadata, + return __awaiter(this, void 0, void 0, function* () { + const encryptedData = typeof data === 'string' ? decode(data) : data; + const header = CryptorHeader.tryParse(encryptedData); + const cryptor = this.getCryptor(header); + const metadata = header.length > 0 + ? encryptedData.slice(header.length - header.metadataLength, header.length) + : null; + if (encryptedData.slice(header.length).byteLength <= 0) + throw new Error('Decryption error: empty content'); + return cryptor.decrypt({ + data: encryptedData.slice(header.length), + metadata: metadata, + }); }); } decryptFile(file, File) { @@ -3024,7 +2361,7 @@ const metadata = fileData.slice(header.length - header.metadataLength, header.length); return File.create({ name: file.name, - data: yield this.defaultCryptor.decryptFileData({ + data: yield cryptor.decryptFileData({ data: data.slice(header.length), metadata: metadata, }), @@ -6600,6 +5937,17 @@ validate() { return undefined; } + /** + * Prepare request for sending. + * + * Called after successful {@link validate} and before {@link request}. Used by requests which need to + * perform asynchronous pre-processing (for example, payload encryption) before the transport request can + * be built synchronously. + * + * @returns `Promise` only if asynchronous pre-processing is required (request scheduling stays + * synchronous otherwise). + */ + prepare() { } /** * Parse service response. * @@ -6842,13 +6190,13 @@ if (!serviceResponse) { throw new PubNubError('Service response error, check status for details', createMalformedResponseError(responseText, response.status)); } - const events = serviceResponse.m - .filter((envelope) => { + const envelopes = serviceResponse.m.filter((envelope) => { const subscribable = envelope.b === undefined ? envelope.c : envelope.b; return ((this.parameters.channels && this.parameters.channels.includes(subscribable)) || (this.parameters.channelGroups && this.parameters.channelGroups.includes(subscribable))); - }) - .map((envelope) => { + }); + // Envelopes processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + const parsedEvents = yield Promise.all(envelopes.map((envelope) => __awaiter(this, void 0, void 0, function* () { let { e: eventType } = envelope; // Events delivered on a presence channel are always presence events: the `-pnpres` suffix wins over // the service-reported type. `envelope.c` is the actual channel (`envelope.b` is the channel group @@ -6869,20 +6217,20 @@ if (eventType == PubNubEventType.Message) { return { type: PubNubEventType.Message, - data: this.messageFromEnvelope(envelope), + data: yield this.messageFromEnvelope(envelope), pn_mfp, }; } return { type: PubNubEventType.Files, - data: this.fileFromEnvelope(envelope), + data: yield this.fileFromEnvelope(envelope), pn_mfp, }; } else if (eventType == PubNubEventType.Message) { return { type: PubNubEventType.Message, - data: this.messageFromEnvelope(envelope), + data: yield this.messageFromEnvelope(envelope), pn_mfp, }; } @@ -6919,16 +6267,16 @@ // Guard: only treat as DataSync when the service marks it so; otherwise fall back to message. if (dataSync) return { type: PubNubEventType.DataSync, data: dataSync, pn_mfp }; - return { type: PubNubEventType.Message, data: this.messageFromEnvelope(envelope), pn_mfp }; + return { type: PubNubEventType.Message, data: yield this.messageFromEnvelope(envelope), pn_mfp }; } // The only known event type left is a file event. return { type: PubNubEventType.Files, - data: this.fileFromEnvelope(envelope), + data: yield this.fileFromEnvelope(envelope), pn_mfp, }; - }) - .filter((event) => event !== undefined); + }))); + const events = parsedEvents.filter((event) => event !== undefined); return { cursor: { timetoken: serviceResponse.t.t, region: serviceResponse.t.r }, messages: events, @@ -6984,28 +6332,30 @@ subscribedChannel, timetoken: envelope.p.t }, (presenceData !== null && presenceData !== void 0 ? presenceData : {})); } messageFromEnvelope(envelope) { - const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); - const [message, decryptionError] = this.decryptedData(envelope.d); - // Backward compatibility with deprecated properties. - const actualChannel = subscription !== null ? channel : null; - const subscribedChannel = subscription !== null ? subscription : channel; - // Basic message event payload. - const event = { - channel, - subscription, - actualChannel, - subscribedChannel, - timetoken: envelope.p.t, - publisher: envelope.i, - message, - }; - if (envelope.u) - event.userMetadata = envelope.u; - if (envelope.cmt) - event.customMessageType = envelope.cmt; - if (decryptionError) - event.error = decryptionError; - return event; + return __awaiter(this, void 0, void 0, function* () { + const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); + const [message, decryptionError] = yield this.decryptedData(envelope.d); + // Backward compatibility with deprecated properties. + const actualChannel = subscription !== null ? channel : null; + const subscribedChannel = subscription !== null ? subscription : channel; + // Basic message event payload. + const event = { + channel, + subscription, + actualChannel, + subscribedChannel, + timetoken: envelope.p.t, + publisher: envelope.i, + message, + }; + if (envelope.u) + event.userMetadata = envelope.u; + if (envelope.cmt) + event.customMessageType = envelope.cmt; + if (decryptionError) + event.error = decryptionError; + return event; + }); } signalFromEnvelope(envelope) { const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); @@ -7124,37 +6474,39 @@ }; } fileFromEnvelope(envelope) { - const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); - const [file, decryptionError] = this.decryptedData(envelope.d); - let errorMessage = decryptionError; - // Basic file event payload. - const event = { - channel, - subscription, - timetoken: envelope.p.t, - publisher: envelope.i, - }; - if (envelope.u) - event.userMetadata = envelope.u; - if (!file) - errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `File information payload is missing.`); - else if (typeof file === 'string') - errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `Unexpected file information payload data type.`); - else { - event.message = file.message; - if (file.file) { - event.file = { - id: file.file.id, - name: file.file.name, - url: this.parameters.getFileUrl({ id: file.file.id, name: file.file.name, channel }), - }; + return __awaiter(this, void 0, void 0, function* () { + const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); + const [file, decryptionError] = yield this.decryptedData(envelope.d); + let errorMessage = decryptionError; + // Basic file event payload. + const event = { + channel, + subscription, + timetoken: envelope.p.t, + publisher: envelope.i, + }; + if (envelope.u) + event.userMetadata = envelope.u; + if (!file) + errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `File information payload is missing.`); + else if (typeof file === 'string') + errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `Unexpected file information payload data type.`); + else { + event.message = file.message; + if (file.file) { + event.file = { + id: file.file.id, + name: file.file.name, + url: this.parameters.getFileUrl({ id: file.file.id, name: file.file.name, channel }), + }; + } } - } - if (envelope.cmt) - event.customMessageType = envelope.cmt; - if (errorMessage) - event.error = errorMessage; - return event; + if (envelope.cmt) + event.customMessageType = envelope.cmt; + if (errorMessage) + event.error = errorMessage; + return event; + }); } // endregion subscriptionChannelFromEnvelope(envelope) { @@ -7168,22 +6520,24 @@ * @returns Tuple with decrypted data and decryption error (if any). */ decryptedData(data) { - if (!this.parameters.crypto || typeof data !== 'string') - return [data, undefined]; - let payload; - let error; - try { - const decryptedData = this.parameters.crypto.decrypt(data); - payload = - decryptedData instanceof ArrayBuffer - ? JSON.parse(SubscribeRequest.decoder.decode(decryptedData)) - : decryptedData; - } - catch (err) { - payload = null; - error = `Error while decrypting message content: ${err.message}`; - } - return [(payload !== null && payload !== void 0 ? payload : data), error]; + return __awaiter(this, void 0, void 0, function* () { + if (!this.parameters.crypto || typeof data !== 'string') + return [data, undefined]; + let payload; + let error; + try { + const decryptedData = yield this.parameters.crypto.decrypt(data); + payload = + decryptedData instanceof ArrayBuffer + ? JSON.parse(SubscribeRequest.decoder.decode(decryptedData)) + : decryptedData; + } + catch (err) { + payload = null; + error = `Error while decrypting message content: ${err.message}`; + } + return [(payload !== null && payload !== void 0 ? payload : data), error]; + }); } } /** @@ -10286,6 +9640,14 @@ if (!publishKey) return "Missing 'publishKey'"; } + prepare() { + const { crypto, message } = this.parameters; + if (!crypto) + return; + return encryptMessagePayload(crypto, message).then((encrypted) => { + this.encryptedPayload = encrypted; + }); + } parse(response) { return __awaiter(this, void 0, void 0, function* () { return { timetoken: this.deserializeResponse(response)[2] }; @@ -10323,20 +9685,21 @@ /** * Pre-process provided data. * - * Data will be "normalized" and encrypted if `cryptoModule` has been provided. + * Data will be "normalized" and use encrypted payload computed by {@link prepare} if `cryptoModule` has + * been provided. * * @param payload - User-provided data which should be pre-processed before use. * * @returns Payload which can be used as part of request URL or body. * - * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. + * @throws {Error} in case if provided `payload` can't be stringified or request hasn't been prepared. */ prepareMessagePayload(payload) { - const { crypto } = this.parameters; - if (!crypto) + if (!this.parameters.crypto) return JSON.stringify(payload) || ''; - const encrypted = crypto.encrypt(JSON.stringify(payload)); - return JSON.stringify(typeof encrypted === 'string' ? encrypted : encode(encrypted)); + if (this.encryptedPayload === undefined) + throw new Error('Encryption error: request payload not prepared'); + return this.encryptedPayload; } } @@ -12265,8 +11628,9 @@ if (!Array.isArray(messages)) return { messages: [], startTimeToken, endTimeToken }; return { - messages: messages.map((payload) => { - const processedPayload = this.processPayload(payload.message); + // Messages processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + messages: yield Promise.all(messages.map((payload) => __awaiter(this, void 0, void 0, function* () { + const processedPayload = yield this.processPayload(payload.message); const item = { entry: processedPayload.payload, timetoken: payload.timetoken, @@ -12276,7 +11640,7 @@ if (payload.meta) item.meta = payload.meta; return item; - }), + }))), startTimeToken, endTimeToken, }; @@ -12291,28 +11655,30 @@ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ count: count, include_token: 'true' }, (start ? { start } : {})), (end ? { end } : {})), (stringifiedTimeToken ? { string_message_token: 'true' } : {})), (reverse !== undefined && reverse !== null ? { reverse: reverse.toString() } : {})), (includeMeta ? { include_meta: 'true' } : {})); } processPayload(payload) { - const { crypto, logVerbosity } = this.parameters; - if (!crypto || typeof payload !== 'string') - return { payload }; - let decryptedPayload; - let error; - try { - const decryptedData = crypto.decrypt(payload); - decryptedPayload = - decryptedData instanceof ArrayBuffer - ? JSON.parse(GetHistoryRequest.decoder.decode(decryptedData)) - : decryptedData; - } - catch (err) { - if (logVerbosity) - console.log(`decryption error`, err.message); - decryptedPayload = payload; - error = `Error while decrypting message content: ${err.message}`; - } - return { - payload: decryptedPayload, - error, - }; + return __awaiter(this, void 0, void 0, function* () { + const { crypto, logVerbosity } = this.parameters; + if (!crypto || typeof payload !== 'string') + return { payload }; + let decryptedPayload; + let error; + try { + const decryptedData = yield crypto.decrypt(payload); + decryptedPayload = + decryptedData instanceof ArrayBuffer + ? JSON.parse(GetHistoryRequest.decoder.decode(decryptedData)) + : decryptedData; + } + catch (err) { + if (logVerbosity) + console.log(`decryption error`, err.message); + decryptedPayload = payload; + error = `Error while decrypting message content: ${err.message}`; + } + return { + payload: decryptedPayload, + error, + }; + }); } } @@ -12420,28 +11786,30 @@ const serviceResponse = this.deserializeResponse(response); const responseChannels = (_a = serviceResponse.channels) !== null && _a !== void 0 ? _a : {}; const channels = {}; - Object.keys(responseChannels).forEach((channel) => { - // Map service response to expected data object type structure. - channels[channel] = responseChannels[channel].map((payload) => { - // `null` message type means regular message. - if (payload.message_type === null) - payload.message_type = PubNubMessageType.Message; - const processedPayload = this.processPayload(channel, payload); - const item = Object.assign(Object.assign({ channel, timetoken: payload.timetoken, message: processedPayload.payload, messageType: payload.message_type }, (payload.custom_message_type ? { customMessageType: payload.custom_message_type } : {})), { uuid: payload.uuid }); - if (payload.actions) { - const itemWithActions = item; - itemWithActions.actions = payload.actions; - // Backward compatibility for existing users. - // TODO: Remove in next release. - itemWithActions.data = payload.actions; - } - if (payload.meta) - item.meta = payload.meta; - if (processedPayload.error) - item.error = processedPayload.error; - return item; - }); - }); + // Messages processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + const channelNames = Object.keys(responseChannels); + const channelsMessages = yield Promise.all(channelNames.map((channel) => + // Map service response to expected data object type structure. + Promise.all(responseChannels[channel].map((payload) => __awaiter(this, void 0, void 0, function* () { + // `null` message type means regular message. + if (payload.message_type === null) + payload.message_type = PubNubMessageType.Message; + const processedPayload = yield this.processPayload(channel, payload); + const item = Object.assign(Object.assign({ channel, timetoken: payload.timetoken, message: processedPayload.payload, messageType: payload.message_type }, (payload.custom_message_type ? { customMessageType: payload.custom_message_type } : {})), { uuid: payload.uuid }); + if (payload.actions) { + const itemWithActions = item; + itemWithActions.actions = payload.actions; + // Backward compatibility for existing users. + // TODO: Remove in next release. + itemWithActions.data = payload.actions; + } + if (payload.meta) + item.meta = payload.meta; + if (processedPayload.error) + item.error = processedPayload.error; + return item; + }))))); + channelNames.forEach((channel, index) => (channels[channel] = channelsMessages[index])); if (serviceResponse.more) return { channels, more: serviceResponse.more }; return { channels }; @@ -12467,39 +11835,41 @@ * @returns */ processPayload(channel, payload) { - const { crypto, logVerbosity } = this.parameters; - if (!crypto || typeof payload.message !== 'string') - return { payload: payload.message }; - let decryptedPayload; - let error; - try { - const decryptedData = crypto.decrypt(payload.message); - decryptedPayload = - decryptedData instanceof ArrayBuffer - ? JSON.parse(FetchMessagesRequest.decoder.decode(decryptedData)) - : decryptedData; - } - catch (err) { - if (logVerbosity) - console.log(`decryption error`, err.message); - decryptedPayload = payload.message; - error = `Error while decrypting message content: ${err.message}`; - } - if (!error && - decryptedPayload && - payload.message_type == PubNubMessageType.Files && - typeof decryptedPayload === 'object' && - this.isFileMessage(decryptedPayload)) { - const fileMessage = decryptedPayload; - return { - payload: { - message: fileMessage.message, - file: Object.assign(Object.assign({}, fileMessage.file), { url: this.parameters.getFileUrl({ channel, id: fileMessage.file.id, name: fileMessage.file.name }) }), - }, - error, - }; - } - return { payload: decryptedPayload, error }; + return __awaiter(this, void 0, void 0, function* () { + const { crypto, logVerbosity } = this.parameters; + if (!crypto || typeof payload.message !== 'string') + return { payload: payload.message }; + let decryptedPayload; + let error; + try { + const decryptedData = yield crypto.decrypt(payload.message); + decryptedPayload = + decryptedData instanceof ArrayBuffer + ? JSON.parse(FetchMessagesRequest.decoder.decode(decryptedData)) + : decryptedData; + } + catch (err) { + if (logVerbosity) + console.log(`decryption error`, err.message); + decryptedPayload = payload.message; + error = `Error while decrypting message content: ${err.message}`; + } + if (!error && + decryptedPayload && + payload.message_type == PubNubMessageType.Files && + typeof decryptedPayload === 'object' && + this.isFileMessage(decryptedPayload)) { + const fileMessage = decryptedPayload; + return { + payload: { + message: fileMessage.message, + file: Object.assign(Object.assign({}, fileMessage.file), { url: this.parameters.getFileUrl({ channel, id: fileMessage.file.id, name: fileMessage.file.name }) }), + }, + error, + }; + } + return { payload: decryptedPayload, error }; + }); } /** * Check whether `payload` potentially represents file message. @@ -12706,18 +12076,34 @@ if (!fileName) return "file name can't be empty"; } + prepare() { + const { crypto } = this.parameters; + if (!crypto) + return; + return encryptMessagePayload(crypto, this.fileMessage).then((encrypted) => { + this.encryptedPayload = encrypted; + }); + } parse(response) { return __awaiter(this, void 0, void 0, function* () { return { timetoken: this.deserializeResponse(response)[2] }; }); } get path() { - const { message, channel, keySet: { publishKey, subscribeKey }, fileId, fileName, } = this.parameters; - const fileMessage = Object.assign({ file: { + const { channel, keySet: { publishKey, subscribeKey }, } = this.parameters; + return `/v1/files/publish-file/${publishKey}/${subscribeKey}/0/${encodeString(channel)}/0/${encodeString(this.prepareMessagePayload(this.fileMessage))}`; + } + /** + * File message which should be published. + * + * @returns File message payload. + */ + get fileMessage() { + const { message, fileId, fileName } = this.parameters; + return Object.assign({ file: { name: fileName, id: fileId, } }, (message ? { message } : {})); - return `/v1/files/publish-file/${publishKey}/${subscribeKey}/0/${encodeString(channel)}/0/${encodeString(this.prepareMessagePayload(fileMessage))}`; } get queryParameters() { const { customMessageType, storeInHistory, ttl, meta } = this.parameters; @@ -12726,20 +12112,21 @@ /** * Pre-process provided data. * - * Data will be "normalized" and encrypted if `cryptoModule` has been provided. + * Data will be "normalized" and use encrypted payload computed by {@link prepare} if `cryptoModule` has + * been provided. * * @param payload - User-provided data which should be pre-processed before use. * * @returns Payload which can be used as part of request URL or body. * - * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. + * @throws {Error} in case if provided `payload` can't be stringified or request hasn't been prepared. */ prepareMessagePayload(payload) { - const { crypto } = this.parameters; - if (!crypto) + if (!this.parameters.crypto) return JSON.stringify(payload) || ''; - const encrypted = crypto.encrypt(JSON.stringify(payload)); - return JSON.stringify(typeof encrypted === 'string' ? encrypted : encode(encrypted)); + if (this.encryptedPayload === undefined) + throw new Error('Encryption error: request payload not prepared'); + return this.encryptedPayload; } } @@ -18572,6 +17959,15 @@ * @internal */ constructor(configuration) { + /** + * Tail of the asynchronous requests preparation queue. + * + * Used to schedule requests which require asynchronous preparation (for example, payload encryption) in the + * same order in which they have been called. + * + * @internal + */ + this.requestsPreparation = Promise.resolve(); /** * List of subscribe capable objects with active subscriptions. * @@ -19249,6 +18645,19 @@ return callback(validationError, null); throw new PubNubError('Validation failed, check status for details', validationError); } + // Asynchronous request pre-processing (for example, payload encryption). + const preparation = request.prepare(); + if (preparation) { + // Requests which require preparation are scheduled in call order (same as synchronous requests). + const orderedPreparation = this.requestsPreparation.then(() => preparation); + this.requestsPreparation = orderedPreparation.catch(() => { }); + try { + yield orderedPreparation; + } + catch (error) { + return this.handleRequestError(error, request.operation(), callback); + } + } // Complete request configuration. const transportRequest = request.request(); const operation = request.operation(); @@ -19302,25 +18711,39 @@ return callback(status, parsed); return parsed; }) - .catch((error) => { - const apiError = !(error instanceof PubNubAPIError) ? PubNubAPIError.create(error) : error; - // Notify callback (if possible). - if (callback) { - if (apiError.category !== StatusCategory$1.PNCancelledCategory) { - this.logger.error('PubNub', () => ({ - messageType: 'error', - message: apiError.toPubNubError(operation, 'REST API request processing error, check status for details'), - })); - } - return callback(apiError.toStatus(operation), null); - } - const pubNubError = apiError.toPubNubError(operation, 'REST API request processing error, check status for details'); - if (apiError.category !== StatusCategory$1.PNCancelledCategory) - this.logger.error('PubNub', () => ({ messageType: 'error', message: pubNubError })); - throw pubNubError; - }); + .catch((error) => this.handleRequestError(error, operation, callback)); }); } + /** + * Handle REST API request processing error. + * + * @internal + * + * @param error - Request processing error. + * @param operation - Type of failed REST API operation. + * @param [callback] - Request completion handler callback. + * + * @returns `void` in case if `callback` provided. + * + * @throws PubNubError in case if `callback` not provided. + */ + handleRequestError(error, operation, callback) { + const apiError = !(error instanceof PubNubAPIError) ? PubNubAPIError.create(error) : error; + // Notify callback (if possible). + if (callback) { + if (apiError.category !== StatusCategory$1.PNCancelledCategory) { + this.logger.error('PubNub', () => ({ + messageType: 'error', + message: apiError.toPubNubError(operation, 'REST API request processing error, check status for details'), + })); + } + return callback(apiError.toStatus(operation), null); + } + const pubNubError = apiError.toPubNubError(operation, 'REST API request processing error, check status for details'); + if (apiError.category !== StatusCategory$1.PNCancelledCategory) + this.logger.error('PubNub', () => ({ messageType: 'error', message: pubNubError })); + throw pubNubError; + } /** * Unsubscribe from all channels and groups. * @@ -21433,22 +20856,24 @@ * @param [customCipherKey] - Cipher key which should be used to encrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data encryption result as a string. + * @returns Asynchronous data encryption result as a string. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ encrypt(data, customCipherKey) { - this.logger.warn('PubNub', "'encrypt' is deprecated. Use cryptoModule instead."); - const cryptoModule = this._configuration.getCryptoModule(); - if (!customCipherKey && cryptoModule && typeof data === 'string') { - const encrypted = cryptoModule.encrypt(data); - return typeof encrypted === 'string' ? encrypted : encode(encrypted); - } - if (!this.crypto) - throw new Error('Encryption error: cypher key not set'); - { - return this.crypto.encrypt(data, customCipherKey); - } + return __awaiter(this, void 0, void 0, function* () { + this.logger.warn('PubNub', "'encrypt' is deprecated. Use cryptoModule instead."); + const cryptoModule = this._configuration.getCryptoModule(); + if (!customCipherKey && cryptoModule && typeof data === 'string') { + const encrypted = yield cryptoModule.encrypt(data); + return typeof encrypted === 'string' ? encrypted : encode(encrypted); + } + if (!this.crypto) + throw new Error('Encryption error: cypher key not set'); + { + return this.crypto.encrypt(data, customCipherKey); + } + }); } /** * Decrypt data. @@ -21457,22 +20882,24 @@ * @param [customCipherKey] - Cipher key which should be used to decrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data decryption result as an object. + * @returns Asynchronous data decryption result as an object. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ decrypt(data, customCipherKey) { - this.logger.warn('PubNub', "'decrypt' is deprecated. Use cryptoModule instead."); - const cryptoModule = this._configuration.getCryptoModule(); - if (!customCipherKey && cryptoModule) { - const decrypted = cryptoModule.decrypt(data); - return decrypted instanceof ArrayBuffer ? JSON.parse(new TextDecoder().decode(decrypted)) : decrypted; - } - if (!this.crypto) - throw new Error('Decryption error: cypher key not set'); - { - return this.crypto.decrypt(data, customCipherKey); - } + return __awaiter(this, void 0, void 0, function* () { + this.logger.warn('PubNub', "'decrypt' is deprecated. Use cryptoModule instead."); + const cryptoModule = this._configuration.getCryptoModule(); + if (!customCipherKey && cryptoModule) { + const decrypted = yield cryptoModule.decrypt(data); + return decrypted instanceof ArrayBuffer ? JSON.parse(new TextDecoder().decode(decrypted)) : decrypted; + } + if (!this.crypto) + throw new Error('Decryption error: cypher key not set'); + { + return this.crypto.decrypt(data, customCipherKey); + } + }); } /** * Encrypt file content. @@ -21646,7 +21073,10 @@ { const cryptoModule = new WebCryptoModule({ default: new LegacyCryptor(Object.assign(Object.assign({}, cryptoConfiguration), (!cryptoConfiguration.logger ? { logger: clientConfiguration.logger() } : {}))), - cryptors: [new AesCbcCryptor({ cipherKey: cryptoConfiguration.cipherKey })], + cryptors: [ + new AesCbcCryptor({ cipherKey: cryptoConfiguration.cipherKey }), + new AesGcmCryptor({ cipherKey: cryptoConfiguration.cipherKey }), + ], }); return cryptoModule; } @@ -21674,14 +21104,14 @@ let crypto; { if (clientConfiguration.getCipherKey() || clientConfiguration.secretKey) { - crypto = new Crypto({ + crypto = new LegacyCrypto({ secretKey: clientConfiguration.secretKey, cipherKey: clientConfiguration.getCipherKey(), useRandomIVs: clientConfiguration.getUseRandomIVs(), customEncrypt: clientConfiguration.getCustomEncrypt(), customDecrypt: clientConfiguration.getCustomDecrypt(), logger: clientConfiguration.logger(), - }); + }, new WebCryptoPrimitives()); } } // Settings change handlers diff --git a/dist/web/pubnub.min.js b/dist/web/pubnub.min.js index 2fca410b4..64f2b544d 100644 --- a/dist/web/pubnub.min.js +++ b/dist/web/pubnub.min.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).PubNub=t()}(this,(function(){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var s={exports:{}};!function(t){!function(e,s){var n=Math.pow(2,-24),i=Math.pow(2,32),r=Math.pow(2,53);var a={encode:function(e){var t,n=new ArrayBuffer(256),a=new DataView(n),o=0;function c(e){for(var s=n.byteLength,i=o+e;s>2,u=0;u>6),i.push(128|63&a)):a<55296?(i.push(224|a>>12),i.push(128|a>>6&63),i.push(128|63&a)):(a=(1023&a)<<10,a|=1023&t.charCodeAt(++n),a+=65536,i.push(240|a>>18),i.push(128|a>>12&63),i.push(128|a>>6&63),i.push(128|63&a))}return d(3,i.length),h(i);default:var p;if(Array.isArray(t))for(d(4,p=t.length),n=0;n>5!==e)throw"Invalid indefinite length element";return s}function y(e,t){for(var s=0;s>10),e.push(56320|1023&n))}}"function"!=typeof t&&(t=function(e){return e}),"function"!=typeof r&&(r=function(){return s});var m=function e(){var i,d,m=l(),f=m>>5,v=31&m;if(7===f)switch(v){case 25:return function(){var e=new ArrayBuffer(4),t=new DataView(e),s=h(),i=32768&s,r=31744&s,a=1023&s;if(31744===r)r=261120;else if(0!==r)r+=114688;else if(0!==a)return a*n;return t.setUint32(0,i<<16|r<<13|a<<13),t.getFloat32(0)}();case 26:return c(a.getFloat32(o),4);case 27:return c(a.getFloat64(o),8)}if((d=g(v))<0&&(f<2||6=0;)S+=d,O.push(u(d));var j=new Uint8Array(S),w=0;for(i=0;i=0;)y(k,d);else y(k,d);return String.fromCharCode.apply(null,k);case 4:var C;if(d<0)for(C=[];!p();)C.push(e());else for(C=new Array(d),i=0;ie.toString())).join(", ")}]}`}}a.encoder=new TextEncoder,a.decoder=new TextDecoder;class o{static create(e){return new o(e)}constructor(e){let t,s,n,i;if(e instanceof File)i=e,n=e.name,s=e.type,t=e.size;else if("data"in e){const r=e.data;s=e.mimeType,n=e.name,i=new File([r],n,{type:s}),t=i.size}if(void 0===i)throw new Error("Couldn't construct a file out of supplied options.");if(void 0===n)throw new Error("Couldn't guess filename out of the options. Please provide one.");t&&(this.contentLength=t),this.mimeType=s,this.data=i,this.name=n}toBuffer(){return r(this,void 0,void 0,(function*(){throw new Error("This feature is only supported in Node.js environments.")}))}toArrayBuffer(){return r(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{const s=new FileReader;s.addEventListener("load",(()=>{if(s.result instanceof ArrayBuffer)return e(s.result)})),s.addEventListener("error",(()=>t(s.error))),s.readAsArrayBuffer(this.data)}))}))}toString(){return r(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{const s=new FileReader;s.addEventListener("load",(()=>{if("string"==typeof s.result)return e(s.result)})),s.addEventListener("error",(()=>{t(s.error)})),s.readAsBinaryString(this.data)}))}))}toStream(){return r(this,void 0,void 0,(function*(){throw new Error("This feature is only supported in Node.js environments.")}))}toFile(){return r(this,void 0,void 0,(function*(){return this.data}))}toFileUri(){return r(this,void 0,void 0,(function*(){throw new Error("This feature is only supported in React Native environments.")}))}toBlob(){return r(this,void 0,void 0,(function*(){return this.data}))}}o.supportsBlob="undefined"!=typeof Blob,o.supportsFile="undefined"!=typeof File,o.supportsBuffer=!1,o.supportsStream=!1,o.supportsString=!0,o.supportsArrayBuffer=!0,o.supportsEncryptFile=!0,o.supportsFileUri=!1;function c(e){const t=e.replace(/==?$/,""),s=Math.floor(t.length/4*3),n=new ArrayBuffer(s),i=new Uint8Array(n);let r=0;function a(){const e=t.charAt(r++),s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(e);if(-1===s)throw new Error(`Illegal character at ${r}: ${t.charAt(r-1)}`);return s}for(let e=0;e>4,c=(15&s)<<4|n>>2,u=(3&n)<<6|r;i[e]=o,64!=n&&(i[e+1]=c),64!=r&&(i[e+2]=u)}return n}function u(e){let t="";const s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=new Uint8Array(e),i=n.byteLength,r=i%3,a=i-r;let o,c,u,l,h;for(let e=0;e>18,c=(258048&h)>>12,u=(4032&h)>>6,l=63&h,t+=s[o]+s[c]+s[u]+s[l];return 1==r?(h=n[a],o=(252&h)>>2,c=(3&h)<<4,t+=s[o]+s[c]+"=="):2==r&&(h=n[a]<<8|n[a+1],o=(64512&h)>>10,c=(1008&h)>>4,u=(15&h)<<2,t+=s[o]+s[c]+s[u]+"="),t}var l;!function(e){e.PNNetworkIssuesCategory="PNNetworkIssuesCategory",e.PNTimeoutCategory="PNTimeoutCategory",e.PNCancelledCategory="PNCancelledCategory",e.PNBadRequestCategory="PNBadRequestCategory",e.PNAccessDeniedCategory="PNAccessDeniedCategory",e.PNValidationErrorCategory="PNValidationErrorCategory",e.PNAcknowledgmentCategory="PNAcknowledgmentCategory",e.PNMalformedResponseCategory="PNMalformedResponseCategory",e.PNServerErrorCategory="PNServerErrorCategory",e.PNUnknownCategory="PNUnknownCategory",e.PNNetworkUpCategory="PNNetworkUpCategory",e.PNNetworkDownCategory="PNNetworkDownCategory",e.PNReconnectedCategory="PNReconnectedCategory",e.PNConnectedCategory="PNConnectedCategory",e.PNSubscriptionChangedCategory="PNSubscriptionChangedCategory",e.PNRequestMessageCountExceededCategory="PNRequestMessageCountExceededCategory",e.PNDisconnectedCategory="PNDisconnectedCategory",e.PNConnectionErrorCategory="PNConnectionErrorCategory",e.PNDisconnectedUnexpectedlyCategory="PNDisconnectedUnexpectedlyCategory",e.PNSharedWorkerUpdatedCategory="PNSharedWorkerUpdatedCategory"}(l||(l={}));var h=l;class d extends Error{constructor(e,t){super(e),this.status=t,this.name="PubNubError",this.message=e,Object.setPrototypeOf(this,new.target.prototype)}}function p(e,t){var s;return null!==(s=e.statusCode)&&void 0!==s||(e.statusCode=0),Object.assign(Object.assign({},e),{statusCode:e.statusCode,category:t,error:!0})}function g(e,t){return p(Object.assign(Object.assign({message:"Unable to deserialize service response"},void 0!==e?{responseText:e}:{}),void 0!==t?{statusCode:t}:{}),h.PNMalformedResponseCategory)}var b,y,m,f,v,O=O||function(e){var t={},s=t.lib={},n=function(){},i=s.Base={extend:function(e){n.prototype=this;var t=new n;return e&&t.mixIn(e),t.hasOwnProperty("init")||(t.init=function(){t.$super.init.apply(this,arguments)}),t.init.prototype=t,t.$super=this,t},create:function(){var e=this.extend();return e.init.apply(e,arguments),e},init:function(){},mixIn:function(e){for(var t in e)e.hasOwnProperty(t)&&(this[t]=e[t]);e.hasOwnProperty("toString")&&(this.toString=e.toString)},clone:function(){return this.init.prototype.extend(this)}},r=s.WordArray=i.extend({init:function(e,t){e=this.words=e||[],this.sigBytes=null!=t?t:4*e.length},toString:function(e){return(e||o).stringify(this)},concat:function(e){var t=this.words,s=e.words,n=this.sigBytes;if(e=e.sigBytes,this.clamp(),n%4)for(var i=0;i>>2]|=(s[i>>>2]>>>24-i%4*8&255)<<24-(n+i)%4*8;else if(65535>>2]=s[i>>>2];else t.push.apply(t,s);return this.sigBytes+=e,this},clamp:function(){var t=this.words,s=this.sigBytes;t[s>>>2]&=4294967295<<32-s%4*8,t.length=e.ceil(s/4)},clone:function(){var e=i.clone.call(this);return e.words=this.words.slice(0),e},random:function(t){for(var s=[],n=0;n>>2]>>>24-n%4*8&255;s.push((i>>>4).toString(16)),s.push((15&i).toString(16))}return s.join("")},parse:function(e){for(var t=e.length,s=[],n=0;n>>3]|=parseInt(e.substr(n,2),16)<<24-n%8*4;return new r.init(s,t/2)}},c=a.Latin1={stringify:function(e){var t=e.words;e=e.sigBytes;for(var s=[],n=0;n>>2]>>>24-n%4*8&255));return s.join("")},parse:function(e){for(var t=e.length,s=[],n=0;n>>2]|=(255&e.charCodeAt(n))<<24-n%4*8;return new r.init(s,t)}},u=a.Utf8={stringify:function(e){try{return decodeURIComponent(escape(c.stringify(e)))}catch(e){throw Error("Malformed UTF-8 data")}},parse:function(e){return c.parse(unescape(encodeURIComponent(e)))}},l=s.BufferedBlockAlgorithm=i.extend({reset:function(){this._data=new r.init,this._nDataBytes=0},_append:function(e){"string"==typeof e&&(e=u.parse(e)),this._data.concat(e),this._nDataBytes+=e.sigBytes},_process:function(t){var s=this._data,n=s.words,i=s.sigBytes,a=this.blockSize,o=i/(4*a);if(t=(o=t?e.ceil(o):e.max((0|o)-this._minBufferSize,0))*a,i=e.min(4*t,i),t){for(var c=0;cu;){var l;e:{l=c;for(var h=e.sqrt(l),d=2;d<=h;d++)if(!(l%d)){l=!1;break e}l=!0}l&&(8>u&&(r[u]=o(e.pow(c,.5))),a[u]=o(e.pow(c,1/3)),u++),c++}var p=[];i=i.SHA256=n.extend({_doReset:function(){this._hash=new s.init(r.slice(0))},_doProcessBlock:function(e,t){for(var s=this._hash.words,n=s[0],i=s[1],r=s[2],o=s[3],c=s[4],u=s[5],l=s[6],h=s[7],d=0;64>d;d++){if(16>d)p[d]=0|e[t+d];else{var g=p[d-15],b=p[d-2];p[d]=((g<<25|g>>>7)^(g<<14|g>>>18)^g>>>3)+p[d-7]+((b<<15|b>>>17)^(b<<13|b>>>19)^b>>>10)+p[d-16]}g=h+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&u^~c&l)+a[d]+p[d],b=((n<<30|n>>>2)^(n<<19|n>>>13)^(n<<10|n>>>22))+(n&i^n&r^i&r),h=l,l=u,u=c,c=o+g|0,o=r,r=i,i=n,n=g+b|0}s[0]=s[0]+n|0,s[1]=s[1]+i|0,s[2]=s[2]+r|0,s[3]=s[3]+o|0,s[4]=s[4]+c|0,s[5]=s[5]+u|0,s[6]=s[6]+l|0,s[7]=s[7]+h|0},_doFinalize:function(){var t=this._data,s=t.words,n=8*this._nDataBytes,i=8*t.sigBytes;return s[i>>>5]|=128<<24-i%32,s[14+(i+64>>>9<<4)]=e.floor(n/4294967296),s[15+(i+64>>>9<<4)]=n,t.sigBytes=4*s.length,this._process(),this._hash},clone:function(){var e=n.clone.call(this);return e._hash=this._hash.clone(),e}});t.SHA256=n._createHelper(i),t.HmacSHA256=n._createHmacHelper(i)}(Math),y=(b=O).enc.Utf8,b.algo.HMAC=b.lib.Base.extend({init:function(e,t){e=this._hasher=new e.init,"string"==typeof t&&(t=y.parse(t));var s=e.blockSize,n=4*s;t.sigBytes>n&&(t=e.finalize(t)),t.clamp();for(var i=this._oKey=t.clone(),r=this._iKey=t.clone(),a=i.words,o=r.words,c=0;c>>2]>>>24-i%4*8&255)<<16|(t[i+1>>>2]>>>24-(i+1)%4*8&255)<<8|t[i+2>>>2]>>>24-(i+2)%4*8&255,a=0;4>a&&i+.75*a>>6*(3-a)&63));if(t=n.charAt(64))for(;e.length%4;)e.push(t);return e.join("")},parse:function(e){var t=e.length,s=this._map;(n=s.charAt(64))&&-1!=(n=e.indexOf(n))&&(t=n);for(var n=[],i=0,r=0;r>>6-r%4*2;n[i>>>2]|=(a|o)<<24-i%4*8,i++}return f.create(n,i)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="},function(e){function t(e,t,s,n,i,r,a){return((e=e+(t&s|~t&n)+i+a)<>>32-r)+t}function s(e,t,s,n,i,r,a){return((e=e+(t&n|s&~n)+i+a)<>>32-r)+t}function n(e,t,s,n,i,r,a){return((e=e+(t^s^n)+i+a)<>>32-r)+t}function i(e,t,s,n,i,r,a){return((e=e+(s^(t|~n))+i+a)<>>32-r)+t}for(var r=O,a=(c=r.lib).WordArray,o=c.Hasher,c=r.algo,u=[],l=0;64>l;l++)u[l]=4294967296*e.abs(e.sin(l+1))|0;c=c.MD5=o.extend({_doReset:function(){this._hash=new a.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(e,r){for(var a=0;16>a;a++){var o=e[c=r+a];e[c]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8)}a=this._hash.words;var c=e[r+0],l=(o=e[r+1],e[r+2]),h=e[r+3],d=e[r+4],p=e[r+5],g=e[r+6],b=e[r+7],y=e[r+8],m=e[r+9],f=e[r+10],v=e[r+11],O=e[r+12],S=e[r+13],j=e[r+14],w=e[r+15],k=t(k=a[0],N=a[1],P=a[2],C=a[3],c,7,u[0]),C=t(C,k,N,P,o,12,u[1]),P=t(P,C,k,N,l,17,u[2]),N=t(N,P,C,k,h,22,u[3]);k=t(k,N,P,C,d,7,u[4]),C=t(C,k,N,P,p,12,u[5]),P=t(P,C,k,N,g,17,u[6]),N=t(N,P,C,k,b,22,u[7]),k=t(k,N,P,C,y,7,u[8]),C=t(C,k,N,P,m,12,u[9]),P=t(P,C,k,N,f,17,u[10]),N=t(N,P,C,k,v,22,u[11]),k=t(k,N,P,C,O,7,u[12]),C=t(C,k,N,P,S,12,u[13]),P=t(P,C,k,N,j,17,u[14]),k=s(k,N=t(N,P,C,k,w,22,u[15]),P,C,o,5,u[16]),C=s(C,k,N,P,g,9,u[17]),P=s(P,C,k,N,v,14,u[18]),N=s(N,P,C,k,c,20,u[19]),k=s(k,N,P,C,p,5,u[20]),C=s(C,k,N,P,f,9,u[21]),P=s(P,C,k,N,w,14,u[22]),N=s(N,P,C,k,d,20,u[23]),k=s(k,N,P,C,m,5,u[24]),C=s(C,k,N,P,j,9,u[25]),P=s(P,C,k,N,h,14,u[26]),N=s(N,P,C,k,y,20,u[27]),k=s(k,N,P,C,S,5,u[28]),C=s(C,k,N,P,l,9,u[29]),P=s(P,C,k,N,b,14,u[30]),k=n(k,N=s(N,P,C,k,O,20,u[31]),P,C,p,4,u[32]),C=n(C,k,N,P,y,11,u[33]),P=n(P,C,k,N,v,16,u[34]),N=n(N,P,C,k,j,23,u[35]),k=n(k,N,P,C,o,4,u[36]),C=n(C,k,N,P,d,11,u[37]),P=n(P,C,k,N,b,16,u[38]),N=n(N,P,C,k,f,23,u[39]),k=n(k,N,P,C,S,4,u[40]),C=n(C,k,N,P,c,11,u[41]),P=n(P,C,k,N,h,16,u[42]),N=n(N,P,C,k,g,23,u[43]),k=n(k,N,P,C,m,4,u[44]),C=n(C,k,N,P,O,11,u[45]),P=n(P,C,k,N,w,16,u[46]),k=i(k,N=n(N,P,C,k,l,23,u[47]),P,C,c,6,u[48]),C=i(C,k,N,P,b,10,u[49]),P=i(P,C,k,N,j,15,u[50]),N=i(N,P,C,k,p,21,u[51]),k=i(k,N,P,C,O,6,u[52]),C=i(C,k,N,P,h,10,u[53]),P=i(P,C,k,N,f,15,u[54]),N=i(N,P,C,k,o,21,u[55]),k=i(k,N,P,C,y,6,u[56]),C=i(C,k,N,P,w,10,u[57]),P=i(P,C,k,N,g,15,u[58]),N=i(N,P,C,k,S,21,u[59]),k=i(k,N,P,C,d,6,u[60]),C=i(C,k,N,P,v,10,u[61]),P=i(P,C,k,N,l,15,u[62]),N=i(N,P,C,k,m,21,u[63]);a[0]=a[0]+k|0,a[1]=a[1]+N|0,a[2]=a[2]+P|0,a[3]=a[3]+C|0},_doFinalize:function(){var t=this._data,s=t.words,n=8*this._nDataBytes,i=8*t.sigBytes;s[i>>>5]|=128<<24-i%32;var r=e.floor(n/4294967296);for(s[15+(i+64>>>9<<4)]=16711935&(r<<8|r>>>24)|4278255360&(r<<24|r>>>8),s[14+(i+64>>>9<<4)]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8),t.sigBytes=4*(s.length+1),this._process(),s=(t=this._hash).words,n=0;4>n;n++)i=s[n],s[n]=16711935&(i<<8|i>>>24)|4278255360&(i<<24|i>>>8);return t},clone:function(){var e=o.clone.call(this);return e._hash=this._hash.clone(),e}}),r.MD5=o._createHelper(c),r.HmacMD5=o._createHmacHelper(c)}(Math),function(){var e,t=O,s=(e=t.lib).Base,n=e.WordArray,i=(e=t.algo).EvpKDF=s.extend({cfg:s.extend({keySize:4,hasher:e.MD5,iterations:1}),init:function(e){this.cfg=this.cfg.extend(e)},compute:function(e,t){for(var s=(o=this.cfg).hasher.create(),i=n.create(),r=i.words,a=o.keySize,o=o.iterations;r.length>>2]}},e.BlockCipher=a.extend({cfg:a.cfg.extend({mode:o,padding:u}),reset:function(){a.reset.call(this);var e=(t=this.cfg).iv,t=t.mode;if(this._xformMode==this._ENC_XFORM_MODE)var s=t.createEncryptor;else s=t.createDecryptor,this._minBufferSize=1;this._mode=s.call(t,this,e&&e.words)},_doProcessBlock:function(e,t){this._mode.processBlock(e,t)},_doFinalize:function(){var e=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){e.pad(this._data,this.blockSize);var t=this._process(!0)}else t=this._process(!0),e.unpad(t);return t},blockSize:4});var l=e.CipherParams=t.extend({init:function(e){this.mixIn(e)},toString:function(e){return(e||this.formatter).stringify(this)}}),h=(o=(d.format={}).OpenSSL={stringify:function(e){var t=e.ciphertext;return((e=e.salt)?s.create([1398893684,1701076831]).concat(e).concat(t):t).toString(i)},parse:function(e){var t=(e=i.parse(e)).words;if(1398893684==t[0]&&1701076831==t[1]){var n=s.create(t.slice(2,4));t.splice(0,4),e.sigBytes-=16}return l.create({ciphertext:e,salt:n})}},e.SerializableCipher=t.extend({cfg:t.extend({format:o}),encrypt:function(e,t,s,n){n=this.cfg.extend(n);var i=e.createEncryptor(s,n);return t=i.finalize(t),i=i.cfg,l.create({ciphertext:t,key:s,iv:i.iv,algorithm:e,mode:i.mode,padding:i.padding,blockSize:e.blockSize,formatter:n.format})},decrypt:function(e,t,s,n){return n=this.cfg.extend(n),t=this._parse(t,n.format),e.createDecryptor(s,n).finalize(t.ciphertext)},_parse:function(e,t){return"string"==typeof e?t.parse(e,this):e}})),d=(d.kdf={}).OpenSSL={execute:function(e,t,n,i){return i||(i=s.random(8)),e=r.create({keySize:t+n}).compute(e,i),n=s.create(e.words.slice(t),4*n),e.sigBytes=4*t,l.create({key:e,iv:n,salt:i})}},p=e.PasswordBasedCipher=h.extend({cfg:h.cfg.extend({kdf:d}),encrypt:function(e,t,s,n){return s=(n=this.cfg.extend(n)).kdf.execute(s,e.keySize,e.ivSize),n.iv=s.iv,(e=h.encrypt.call(this,e,t,s.key,n)).mixIn(s),e},decrypt:function(e,t,s,n){return n=this.cfg.extend(n),t=this._parse(t,n.format),s=n.kdf.execute(s,e.keySize,e.ivSize,t.salt),n.iv=s.iv,h.decrypt.call(this,e,t,s.key,n)}})}(),function(){for(var e=O,t=e.lib.BlockCipher,s=e.algo,n=[],i=[],r=[],a=[],o=[],c=[],u=[],l=[],h=[],d=[],p=[],g=0;256>g;g++)p[g]=128>g?g<<1:g<<1^283;var b=0,y=0;for(g=0;256>g;g++){var m=(m=y^y<<1^y<<2^y<<3^y<<4)>>>8^255&m^99;n[b]=m,i[m]=b;var f=p[b],v=p[f],S=p[v],j=257*p[m]^16843008*m;r[b]=j<<24|j>>>8,a[b]=j<<16|j>>>16,o[b]=j<<8|j>>>24,c[b]=j,j=16843009*S^65537*v^257*f^16843008*b,u[m]=j<<24|j>>>8,l[m]=j<<16|j>>>16,h[m]=j<<8|j>>>24,d[m]=j,b?(b=f^p[p[p[S^f]]],y^=p[p[y]]):b=y=1}var w=[0,1,2,4,8,16,32,64,128,27,54];s=s.AES=t.extend({_doReset:function(){for(var e=(s=this._key).words,t=s.sigBytes/4,s=4*((this._nRounds=t+6)+1),i=this._keySchedule=[],r=0;r>>24]<<24|n[a>>>16&255]<<16|n[a>>>8&255]<<8|n[255&a]):(a=n[(a=a<<8|a>>>24)>>>24]<<24|n[a>>>16&255]<<16|n[a>>>8&255]<<8|n[255&a],a^=w[r/t|0]<<24),i[r]=i[r-t]^a}for(e=this._invKeySchedule=[],t=0;tt||4>=r?a:u[n[a>>>24]]^l[n[a>>>16&255]]^h[n[a>>>8&255]]^d[n[255&a]]},encryptBlock:function(e,t){this._doCryptBlock(e,t,this._keySchedule,r,a,o,c,n)},decryptBlock:function(e,t){var s=e[t+1];e[t+1]=e[t+3],e[t+3]=s,this._doCryptBlock(e,t,this._invKeySchedule,u,l,h,d,i),s=e[t+1],e[t+1]=e[t+3],e[t+3]=s},_doCryptBlock:function(e,t,s,n,i,r,a,o){for(var c=this._nRounds,u=e[t]^s[0],l=e[t+1]^s[1],h=e[t+2]^s[2],d=e[t+3]^s[3],p=4,g=1;g>>24]^i[l>>>16&255]^r[h>>>8&255]^a[255&d]^s[p++],y=n[l>>>24]^i[h>>>16&255]^r[d>>>8&255]^a[255&u]^s[p++],m=n[h>>>24]^i[d>>>16&255]^r[u>>>8&255]^a[255&l]^s[p++];d=n[d>>>24]^i[u>>>16&255]^r[l>>>8&255]^a[255&h]^s[p++],u=b,l=y,h=m}b=(o[u>>>24]<<24|o[l>>>16&255]<<16|o[h>>>8&255]<<8|o[255&d])^s[p++],y=(o[l>>>24]<<24|o[h>>>16&255]<<16|o[d>>>8&255]<<8|o[255&u])^s[p++],m=(o[h>>>24]<<24|o[d>>>16&255]<<16|o[u>>>8&255]<<8|o[255&l])^s[p++],d=(o[d>>>24]<<24|o[u>>>16&255]<<16|o[l>>>8&255]<<8|o[255&h])^s[p++],e[t]=b,e[t+1]=y,e[t+2]=m,e[t+3]=d},keySize:8});e.AES=t._createHelper(s)}(),O.mode.ECB=((v=O.lib.BlockCipherMode.extend()).Encryptor=v.extend({processBlock:function(e,t){this._cipher.encryptBlock(e,t)}}),v.Decryptor=v.extend({processBlock:function(e,t){this._cipher.decryptBlock(e,t)}}),v);var S=t(O);class j{constructor({cipherKey:e}){this.cipherKey=e,this.CryptoJS=S,this.encryptedKey=this.CryptoJS.SHA256(e)}encrypt(e){if(0===("string"==typeof e?e:j.decoder.decode(e)).length)throw new Error("encryption error. empty content");const t=this.getIv();return{metadata:t,data:c(this.CryptoJS.AES.encrypt(e,this.encryptedKey,{iv:this.bufferToWordArray(t),mode:this.CryptoJS.mode.CBC}).ciphertext.toString(this.CryptoJS.enc.Base64))}}encryptFileData(e){return r(this,void 0,void 0,(function*(){const t=yield this.getKey(),s=this.getIv();return{data:yield crypto.subtle.encrypt({name:this.algo,iv:s},t,e),metadata:s}}))}decrypt(e){if("string"==typeof e.data)throw new Error("Decryption error: data for decryption should be ArrayBuffed.");const t=this.bufferToWordArray(new Uint8ClampedArray(e.metadata)),s=this.bufferToWordArray(new Uint8ClampedArray(e.data));return j.encoder.encode(this.CryptoJS.AES.decrypt({ciphertext:s},this.encryptedKey,{iv:t,mode:this.CryptoJS.mode.CBC}).toString(this.CryptoJS.enc.Utf8)).buffer}decryptFileData(e){return r(this,void 0,void 0,(function*(){if("string"==typeof e.data)throw new Error("Decryption error: data for decryption should be ArrayBuffed.");const t=yield this.getKey();return crypto.subtle.decrypt({name:this.algo,iv:e.metadata},t,e.data)}))}get identifier(){return"ACRH"}get algo(){return"AES-CBC"}getIv(){return crypto.getRandomValues(new Uint8Array(j.BLOCK_SIZE))}getKey(){return r(this,void 0,void 0,(function*(){const e=j.encoder.encode(this.cipherKey),t=yield crypto.subtle.digest("SHA-256",e.buffer);return crypto.subtle.importKey("raw",t,this.algo,!0,["encrypt","decrypt"])}))}bufferToWordArray(e){const t=[];let s;for(s=0;sw.has(e),C=e=>encodeURIComponent(e).replace(/[!~*'()]/g,(e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)),P=(e,t)=>{const s=e.map((e=>C(e)));return s.length?s.join(","):null!=t?t:""},N=(e,t)=>{const s=Object.fromEntries(t.map((e=>[e,!1])));return e.filter((e=>!(t.includes(e)&&!s[e])||(s[e]=!0,!1)))},E=(e,t)=>[...e].filter((s=>t.includes(s)&&e.indexOf(s)===e.lastIndexOf(s)&&t.indexOf(s)===t.lastIndexOf(s))),T=e=>Object.keys(e).map((t=>{const s=e[t];return Array.isArray(s)?s.map((e=>`${t}=${C(e)}`)).join("&"):`${t}=${C(s)}`})).join("&"),_=(e,t)=>{if("0"===t||"0"===e)return;const s=M(`${Date.now()}0000`,t,!1);return M(e,s,!0)},I=(e,t,s)=>{if(e&&0!==e.length){if(t&&t.length>0&&"0"!==t){const n=M(e,t,!1);return M(null!=s?s:`${Date.now()}0000`,n.replace("-",""),Number(n)<0)}return s&&s.length>0&&"0"!==s?s:`${Date.now()}0000`}},M=(e,t,s)=>{t.startsWith("-")&&(t=t.replace("-",""),s=!1),t=t.padStart(17,"0");const n=e.slice(0,10),i=e.slice(10,17),r=t.slice(0,10),a=t.slice(10,17);let o=Number(n),c=Number(i);return o+=Number(r)*(s?1:-1),c+=Number(a)*(s?1:-1),c>=1e7?(o+=Math.floor(c/1e7),c%=1e7):c<0?o>0?(o-=1,c+=1e7):o<0&&(c*=-1):o<0&&c>0&&(o+=1,c=1e7-c),0!==o?`${o}${`${c}`.padStart(7,"0")}`:`${c}`},R=e=>{const t="string"!=typeof e?JSON.stringify(e):e,s=new Uint32Array(1);let n=0,i=t.length;for(;i-- >0;)s[0]=(s[0]<<5)-s[0]+t.charCodeAt(n++);return s[0].toString(16).padStart(8,"0")};function D(e){const t=[];let s;for(s=0;s({messageType:"object",message:this.configuration,details:"Create with configuration:",ignoredKeys:(e,t)=>"function"==typeof t[e]||"logger"===e||k(e)})))}get logger(){return this._logger}HMACSHA256(e){return S.HmacSHA256(e,this.configuration.secretKey).toString(S.enc.Base64)}SHA256(e){return S.SHA256(e).toString(S.enc.Hex)}encrypt(e,t,s){return this.configuration.customEncrypt?(this.logger&&this.logger.warn("Crypto","'customEncrypt' is deprecated. Consult docs for better alternative."),this.configuration.customEncrypt(e)):this.pnEncrypt(e,t,s)}decrypt(e,t,s){return this.configuration.customDecrypt?(this.logger&&this.logger.warn("Crypto","'customDecrypt' is deprecated. Consult docs for better alternative."),this.configuration.customDecrypt(e)):this.pnDecrypt(e,t,s)}pnEncrypt(e,t,s){const n=null!=t?t:this.configuration.cipherKey;if(!n)return e;this.logger&&this.logger.debug("Crypto",(()=>({messageType:"object",message:Object.assign({data:e},null!=s?s:{}),details:"Encrypt with parameters:",ignoredKeys:k}))),s=this.parseOptions(s);const i=this.getMode(s),r=this.getPaddedKey(n,s);if(this.configuration.useRandomIVs){const t=this.getRandomIV(),s=S.AES.encrypt(e,r,{iv:t,mode:i}).ciphertext;return t.clone().concat(s.clone()).toString(S.enc.Base64)}const a=this.getIV(s);return S.AES.encrypt(e,r,{iv:a,mode:i}).ciphertext.toString(S.enc.Base64)||e}pnDecrypt(e,t,s){const n=null!=t?t:this.configuration.cipherKey;if(!n)return e;this.logger&&this.logger.debug("Crypto",(()=>({messageType:"object",message:Object.assign({data:e},null!=s?s:{}),details:"Decrypt with parameters:",ignoredKeys:k}))),s=this.parseOptions(s);const i=this.getMode(s),r=this.getPaddedKey(n,s);if(this.configuration.useRandomIVs){const t=new Uint8ClampedArray(c(e)),s=D(t.slice(0,16)),n=D(t.slice(16));try{const e=S.AES.decrypt({ciphertext:n},r,{iv:s,mode:i}).toString(S.enc.Utf8);return JSON.parse(e)}catch(e){return this.logger&&this.logger.error("Crypto",(()=>({messageType:"error",message:e}))),null}}else{const t=this.getIV(s);try{const s=S.enc.Base64.parse(e),n=S.AES.decrypt({ciphertext:s},r,{iv:t,mode:i}).toString(S.enc.Utf8);return JSON.parse(n)}catch(e){return this.logger&&this.logger.error("Crypto",(()=>({messageType:"error",message:e}))),null}}}parseOptions(e){var t,s,n,i;if(!e)return this.defaultOptions;const r={encryptKey:null!==(t=e.encryptKey)&&void 0!==t?t:this.defaultOptions.encryptKey,keyEncoding:null!==(s=e.keyEncoding)&&void 0!==s?s:this.defaultOptions.keyEncoding,keyLength:null!==(n=e.keyLength)&&void 0!==n?n:this.defaultOptions.keyLength,mode:null!==(i=e.mode)&&void 0!==i?i:this.defaultOptions.mode};return-1===this.allowedKeyEncodings.indexOf(r.keyEncoding.toLowerCase())&&(r.keyEncoding=this.defaultOptions.keyEncoding),-1===this.allowedKeyLengths.indexOf(r.keyLength)&&(r.keyLength=this.defaultOptions.keyLength),-1===this.allowedModes.indexOf(r.mode.toLowerCase())&&(r.mode=this.defaultOptions.mode),r}decodeKey(e,t){return"base64"===t.keyEncoding?S.enc.Base64.parse(e):"hex"===t.keyEncoding?S.enc.Hex.parse(e):e}getPaddedKey(e,t){return e=this.decodeKey(e,t),t.encryptKey?S.enc.Utf8.parse(this.SHA256(e).slice(0,32)):e}getMode(e){return"ecb"===e.mode?S.mode.ECB:S.mode.CBC}getIV(e){return"cbc"===e.mode?S.enc.Utf8.parse(this.iv):null}getRandomIV(){return S.lib.WordArray.random(16)}}class A{encrypt(e,t){return r(this,void 0,void 0,(function*(){if(!(t instanceof ArrayBuffer)&&"string"!=typeof t)throw new Error("Cannot encrypt this file. In browsers file encryption supports only string or ArrayBuffer");const s=yield this.getKey(e);return t instanceof ArrayBuffer?this.encryptArrayBuffer(s,t):this.encryptString(s,t)}))}encryptArrayBuffer(e,t){return r(this,void 0,void 0,(function*(){const s=crypto.getRandomValues(new Uint8Array(16));return this.concatArrayBuffer(s.buffer,yield crypto.subtle.encrypt({name:"AES-CBC",iv:s},e,t))}))}encryptString(e,t){return r(this,void 0,void 0,(function*(){const s=crypto.getRandomValues(new Uint8Array(16)),n=A.encoder.encode(t).buffer,i=yield crypto.subtle.encrypt({name:"AES-CBC",iv:s},e,n),r=this.concatArrayBuffer(s.buffer,i);return A.decoder.decode(r)}))}encryptFile(e,t,s){return r(this,void 0,void 0,(function*(){var n,i;if((null!==(n=t.contentLength)&&void 0!==n?n:0)<=0)throw new Error("encryption error. empty content");const r=yield this.getKey(e),a=yield t.toArrayBuffer(),o=yield this.encryptArrayBuffer(r,a);return s.create({name:t.name,mimeType:null!==(i=t.mimeType)&&void 0!==i?i:"application/octet-stream",data:o})}))}decrypt(e,t){return r(this,void 0,void 0,(function*(){if(!(t instanceof ArrayBuffer)&&"string"!=typeof t)throw new Error("Cannot decrypt this file. In browsers file decryption supports only string or ArrayBuffer");const s=yield this.getKey(e);return t instanceof ArrayBuffer?this.decryptArrayBuffer(s,t):this.decryptString(s,t)}))}decryptArrayBuffer(e,t){return r(this,void 0,void 0,(function*(){const s=t.slice(0,16);if(t.slice(A.IV_LENGTH).byteLength<=0)throw new Error("decryption error: empty content");return yield crypto.subtle.decrypt({name:"AES-CBC",iv:s},e,t.slice(A.IV_LENGTH))}))}decryptString(e,t){return r(this,void 0,void 0,(function*(){const s=A.encoder.encode(t).buffer,n=s.slice(0,16),i=s.slice(16),r=yield crypto.subtle.decrypt({name:"AES-CBC",iv:n},e,i);return A.decoder.decode(r)}))}decryptFile(e,t,s){return r(this,void 0,void 0,(function*(){const n=yield this.getKey(e),i=yield t.toArrayBuffer(),r=yield this.decryptArrayBuffer(n,i);return s.create({name:t.name,mimeType:t.mimeType,data:r})}))}getKey(e){return r(this,void 0,void 0,(function*(){const t=yield crypto.subtle.digest("SHA-256",A.encoder.encode(e)),s=Array.from(new Uint8Array(t)).map((e=>e.toString(16).padStart(2,"0"))).join(""),n=A.encoder.encode(s.slice(0,32)).buffer;return crypto.subtle.importKey("raw",n,"AES-CBC",!0,["encrypt","decrypt"])}))}concatArrayBuffer(e,t){const s=new Uint8Array(e.byteLength+t.byteLength);return s.set(new Uint8Array(e),0),s.set(new Uint8Array(t),e.byteLength),s.buffer}}A.IV_LENGTH=16,A.encoder=new TextEncoder,A.decoder=new TextDecoder;class ${constructor(e){this.config=e,this.cryptor=new U(Object.assign({},e)),this.fileCryptor=new A}set logger(e){this.cryptor.logger=e,!1===this.config.useRandomIVs&&e.warn("LegacyCryptor","Setting 'useRandomIVs' to false is insecure and should only be used to support legacy clients.\n Do not disable random IVs in new applications.")}encrypt(e){const t="string"==typeof e?e:$.decoder.decode(e);return{data:this.cryptor.encrypt(t),metadata:null}}encryptFile(e,t){return r(this,void 0,void 0,(function*(){var s;if(!this.config.cipherKey)throw new d("File encryption error: cipher key not set.");return this.fileCryptor.encryptFile(null===(s=this.config)||void 0===s?void 0:s.cipherKey,e,t)}))}decrypt(e){const t="string"==typeof e.data?e.data:u(e.data);return this.cryptor.decrypt(t)}decryptFile(e,t){return r(this,void 0,void 0,(function*(){if(!this.config.cipherKey)throw new d("File encryption error: cipher key not set.");return this.fileCryptor.decryptFile(this.config.cipherKey,e,t)}))}get identifier(){return""}toString(){return`LegacyCryptor { ${Object.entries(this.config).reduce(((e,[t,s])=>("logger"===t||k(t)||e.push(`${t}: ${"function"==typeof s?"":s}`),e)),[]).join(", ")} }`}}$.encoder=new TextEncoder,$.decoder=new TextDecoder;class F extends a{set logger(e){if(this.defaultCryptor.identifier===F.LEGACY_IDENTIFIER)e.warn("CryptoModule","'legacyCryptoModule' is deprecated. Use 'aesCbcCryptoModule' instead for new applications."),this.defaultCryptor.logger=e;else{const t=this.cryptors.find((e=>e.identifier===F.LEGACY_IDENTIFIER));t&&(t.logger=e)}}static legacyCryptoModule(e){var t;if(!e.cipherKey)throw new d("Crypto module error: cipher key not set.");return e.logger&&(e.logger.warn("CryptoModule","'legacyCryptoModule' is deprecated. Use 'aesCbcCryptoModule' instead for new applications."),!1===e.useRandomIVs&&e.logger.warn("CryptoModule","Setting 'useRandomIVs' to false is insecure and should only be used to support legacy clients.\n Do not disable random IVs in new applications.")),new F({default:new $(Object.assign(Object.assign({},e),{useRandomIVs:null===(t=e.useRandomIVs)||void 0===t||t})),cryptors:[new j({cipherKey:e.cipherKey})]})}static aesCbcCryptoModule(e){var t;if(!e.cipherKey)throw new d("Crypto module error: cipher key not set.");return new F({default:new j({cipherKey:e.cipherKey}),cryptors:[new $(Object.assign(Object.assign({},e),{useRandomIVs:null===(t=e.useRandomIVs)||void 0===t||t}))]})}static withDefaultCryptor(e){return new this({default:e})}encrypt(e){const t=e instanceof ArrayBuffer&&this.defaultCryptor.identifier===F.LEGACY_IDENTIFIER?this.defaultCryptor.encrypt(F.decoder.decode(e)):this.defaultCryptor.encrypt(e);if(!t.metadata)return t.data;if("string"==typeof t.data)throw new Error("Encryption error: encrypted data should be ArrayBuffed.");const s=this.getHeaderData(t);return this.concatArrayBuffer(s,t.data)}encryptFile(e,t){return r(this,void 0,void 0,(function*(){if(this.defaultCryptor.identifier===x.LEGACY_IDENTIFIER)return this.defaultCryptor.encryptFile(e,t);const s=yield this.getFileData(e),n=yield this.defaultCryptor.encryptFileData(s);if("string"==typeof n.data)throw new Error("Encryption error: encrypted data should be ArrayBuffed.");return t.create({name:e.name,mimeType:"application/octet-stream",data:this.concatArrayBuffer(this.getHeaderData(n),n.data)})}))}decrypt(e){const t="string"==typeof e?c(e):e,s=x.tryParse(t),n=this.getCryptor(s),i=s.length>0?t.slice(s.length-s.metadataLength,s.length):null;if(t.slice(s.length).byteLength<=0)throw new Error("Decryption error: empty content");return n.decrypt({data:t.slice(s.length),metadata:i})}decryptFile(e,t){return r(this,void 0,void 0,(function*(){const s=yield e.data.arrayBuffer(),n=x.tryParse(s),i=this.getCryptor(n);if((null==i?void 0:i.identifier)===x.LEGACY_IDENTIFIER)return i.decryptFile(e,t);const r=(yield this.getFileData(s)).slice(n.length-n.metadataLength,n.length);return t.create({name:e.name,data:yield this.defaultCryptor.decryptFileData({data:s.slice(n.length),metadata:r})})}))}getCryptorFromId(e){const t=this.getAllCryptors().find((t=>e===t.identifier));if(t)return t;throw Error("Unknown cryptor error")}getCryptor(e){if("string"==typeof e){const t=this.getAllCryptors().find((t=>t.identifier===e));if(t)return t;throw new Error("Unknown cryptor error")}if(e instanceof q)return this.getCryptorFromId(e.identifier)}getHeaderData(e){if(!e.metadata)return;const t=x.from(this.defaultCryptor.identifier,e.metadata),s=new Uint8Array(t.length);let n=0;return s.set(t.data,n),n+=t.length-e.metadata.byteLength,s.set(new Uint8Array(e.metadata),n),s.buffer}concatArrayBuffer(e,t){const s=new Uint8Array(e.byteLength+t.byteLength);return s.set(new Uint8Array(e),0),s.set(new Uint8Array(t),e.byteLength),s.buffer}getFileData(e){return r(this,void 0,void 0,(function*(){if(e instanceof ArrayBuffer)return e;if(e instanceof o)return e.toArrayBuffer();throw new Error("Cannot decrypt/encrypt file. In browsers file encrypt/decrypt supported for string, ArrayBuffer or Blob")}))}}F.LEGACY_IDENTIFIER="";class x{static from(e,t){if(e!==x.LEGACY_IDENTIFIER)return new q(e,t.byteLength)}static tryParse(e){const t=new Uint8Array(e);let s,n,i=null;if(t.byteLength>=4&&(s=t.slice(0,4),this.decoder.decode(s)!==x.SENTINEL))return F.LEGACY_IDENTIFIER;if(!(t.byteLength>=5))throw new Error("Decryption error: invalid header version");if(i=t[4],i>x.MAX_VERSION)throw new Error("Decryption error: Unknown cryptor error");let r=5+x.IDENTIFIER_LENGTH;if(!(t.byteLength>=r))throw new Error("Decryption error: invalid crypto identifier");n=t.slice(5,r);let a=null;if(!(t.byteLength>=r+1))throw new Error("Decryption error: invalid metadata length");return a=t[r],r+=1,255===a&&t.byteLength>=r+2&&(a=new Uint16Array(t.slice(r,r+2)).reduce(((e,t)=>(e<<8)+t),0)),new q(this.decoder.decode(n),a)}}x.SENTINEL="PNED",x.LEGACY_IDENTIFIER="",x.IDENTIFIER_LENGTH=4,x.VERSION=1,x.MAX_VERSION=1,x.decoder=new TextDecoder;class q{constructor(e,t){this._identifier=e,this._metadataLength=t}get identifier(){return this._identifier}set identifier(e){this._identifier=e}get metadataLength(){return this._metadataLength}set metadataLength(e){this._metadataLength=e}get version(){return x.VERSION}get length(){return x.SENTINEL.length+1+x.IDENTIFIER_LENGTH+(this.metadataLength<255?1:3)+this.metadataLength}get data(){let e=0;const t=new Uint8Array(this.length),s=new TextEncoder;t.set(s.encode(x.SENTINEL)),e+=x.SENTINEL.length,t[e]=this.version,e++,this.identifier&&t.set(s.encode(this.identifier),e);const n=this.metadataLength;return e+=x.IDENTIFIER_LENGTH,n<255?t[e]=n:t.set([255,n>>8,255&n],e),t}}q.IDENTIFIER_LENGTH=4,q.SENTINEL="PNED";class G extends Error{static create(e,t){return G.isErrorObject(e)?G.createFromError(e):G.createFromServiceResponse(e,t)}static createFromError(e){let t=h.PNUnknownCategory,s="Unknown error",n="Error";if(!e)return new G(s,t,0);if(e instanceof G)return e;if(G.isErrorObject(e)&&(s=e.message,n=e.name),"AbortError"===n||-1!==s.indexOf("Aborted"))t=h.PNCancelledCategory,s="Request cancelled";else if(-1!==s.toLowerCase().indexOf("timeout"))t=h.PNTimeoutCategory,s="Request timeout";else if(-1!==s.toLowerCase().indexOf("network"))t=h.PNNetworkIssuesCategory,s="Network issues";else if("TypeError"===n)t=-1!==s.indexOf("Load failed")||-1!=s.indexOf("Failed to fetch")?h.PNNetworkIssuesCategory:h.PNBadRequestCategory;else if("FetchError"===n){const n=e.code;["ECONNREFUSED","ENETUNREACH","ENOTFOUND","ECONNRESET","EAI_AGAIN"].includes(n)&&(t=h.PNNetworkIssuesCategory),"ECONNREFUSED"===n?s="Connection refused":"ENETUNREACH"===n?s="Network not reachable":"ENOTFOUND"===n?s="Server not found":"ECONNRESET"===n?s="Connection reset by peer":"EAI_AGAIN"===n?s="Name resolution error":"ETIMEDOUT"===n?(t=h.PNTimeoutCategory,s="Request timeout"):s=`Unknown system error: ${e}`}else"Request timeout"===s&&(t=h.PNTimeoutCategory);return new G(s,t,0,e)}static createFromServiceResponse(e,t){let s,n=h.PNUnknownCategory,i="Unknown error",{status:r}=e;if(null!=t||(t=e.body),402===r?i="Not available for used key set. Contact support@pubnub.com":404===r?i="Resource not found":400===r?(n=h.PNBadRequestCategory,i="Bad request"):403===r?(n=h.PNAccessDeniedCategory,i="Access denied"):r>=500&&(n=h.PNServerErrorCategory,i="Internal server error"),"object"==typeof e&&0===Object.keys(e).length&&(n=h.PNMalformedResponseCategory,i="Malformed response (network issues)",r=400),t&&t.byteLength>0){const n=(new TextDecoder).decode(t);if(-1!==e.headers["content-type"].indexOf("text/javascript")||-1!==e.headers["content-type"].indexOf("application/json"))try{const e=JSON.parse(n);"object"==typeof e&&(Array.isArray(e)?"number"==typeof e[0]&&0===e[0]&&e.length>1&&"string"==typeof e[1]&&(s=e[1]):("error"in e&&(1===e.error||!0===e.error)&&"status"in e&&"number"==typeof e.status&&"message"in e&&"service"in e?(s=e,r=e.status):s=e,"error"in e&&e.error instanceof Error&&(s=e.error)))}catch(e){s=n}else if(-1!==e.headers["content-type"].indexOf("xml")){const e=/(.*)<\/Message>/gi.exec(n);i=e?`Upload to bucket failed: ${e[1]}`:"Upload to bucket failed."}else s=n}return new G(i,n,r,s)}constructor(e,t,s,n){super(e),this.category=t,this.statusCode=s,this.errorData=n,this.name="PubNubAPIError"}toStatus(e){return{error:!0,category:this.category,operation:e,statusCode:this.statusCode,errorData:this.errorData,toJSON:function(){let e;const t=this.errorData;if(t)try{if("object"==typeof t){const s=Object.assign(Object.assign(Object.assign(Object.assign({},"name"in t?{name:t.name}:{}),"message"in t?{message:t.message}:{}),"stack"in t?{stack:t.stack}:{}),t);e=JSON.parse(JSON.stringify(s,G.circularReplacer()))}else e=t}catch(t){e={error:"Could not serialize the error object"}}const s=i(this,["toJSON"]);return JSON.stringify(Object.assign(Object.assign({},s),{errorData:e}))}}}toPubNubError(e,t){return new d(null!=t?t:this.message,this.toStatus(e))}static circularReplacer(){const e=new WeakSet;return function(t,s){if("object"==typeof s&&null!==s){if(e.has(s))return"[Circular]";e.add(s)}return s}}static isErrorObject(e){return!(!e||"object"!=typeof e)&&(e instanceof Error||("name"in e&&"message"in e&&"string"==typeof e.name&&"string"==typeof e.message||"[object Error]"===Object.prototype.toString.call(e)))}}var L;!function(e){e.PNPublishOperation="PNPublishOperation",e.PNSignalOperation="PNSignalOperation",e.PNSubscribeOperation="PNSubscribeOperation",e.PNUnsubscribeOperation="PNUnsubscribeOperation",e.PNWhereNowOperation="PNWhereNowOperation",e.PNHereNowOperation="PNHereNowOperation",e.PNGlobalHereNowOperation="PNGlobalHereNowOperation",e.PNSetStateOperation="PNSetStateOperation",e.PNGetStateOperation="PNGetStateOperation",e.PNHeartbeatOperation="PNHeartbeatOperation",e.PNAddMessageActionOperation="PNAddActionOperation",e.PNRemoveMessageActionOperation="PNRemoveMessageActionOperation",e.PNGetMessageActionsOperation="PNGetMessageActionsOperation",e.PNTimeOperation="PNTimeOperation",e.PNHistoryOperation="PNHistoryOperation",e.PNDeleteMessagesOperation="PNDeleteMessagesOperation",e.PNFetchMessagesOperation="PNFetchMessagesOperation",e.PNMessageCounts="PNMessageCountsOperation",e.PNGetAllUUIDMetadataOperation="PNGetAllUUIDMetadataOperation",e.PNGetUUIDMetadataOperation="PNGetUUIDMetadataOperation",e.PNSetUUIDMetadataOperation="PNSetUUIDMetadataOperation",e.PNRemoveUUIDMetadataOperation="PNRemoveUUIDMetadataOperation",e.PNGetAllChannelMetadataOperation="PNGetAllChannelMetadataOperation",e.PNGetChannelMetadataOperation="PNGetChannelMetadataOperation",e.PNSetChannelMetadataOperation="PNSetChannelMetadataOperation",e.PNRemoveChannelMetadataOperation="PNRemoveChannelMetadataOperation",e.PNGetMembersOperation="PNGetMembersOperation",e.PNSetMembersOperation="PNSetMembersOperation",e.PNGetMembershipsOperation="PNGetMembershipsOperation",e.PNSetMembershipsOperation="PNSetMembershipsOperation",e.PNCreateDataSyncEntityOperation="PNCreateDataSyncEntityOperation",e.PNGetDataSyncEntityOperation="PNGetDataSyncEntityOperation",e.PNGetDataSyncEntitiesOperation="PNGetDataSyncEntitiesOperation",e.PNSetDataSyncEntityOperation="PNSetDataSyncEntityOperation",e.PNUpdateDataSyncEntityOperation="PNUpdateDataSyncEntityOperation",e.PNRemoveDataSyncEntityOperation="PNRemoveDataSyncEntityOperation",e.PNCreateDataSyncRelationshipOperation="PNCreateDataSyncRelationshipOperation",e.PNGetDataSyncRelationshipOperation="PNGetDataSyncRelationshipOperation",e.PNGetDataSyncRelationshipsOperation="PNGetDataSyncRelationshipsOperation",e.PNSetDataSyncRelationshipOperation="PNSetDataSyncRelationshipOperation",e.PNUpdateDataSyncRelationshipOperation="PNUpdateDataSyncRelationshipOperation",e.PNRemoveDataSyncRelationshipOperation="PNRemoveDataSyncRelationshipOperation",e.PNCreateDataSyncUserOperation="PNCreateDataSyncUserOperation",e.PNGetDataSyncUserOperation="PNGetDataSyncUserOperation",e.PNGetDataSyncUsersOperation="PNGetDataSyncUsersOperation",e.PNSetDataSyncUserOperation="PNSetDataSyncUserOperation",e.PNUpdateDataSyncUserOperation="PNUpdateDataSyncUserOperation",e.PNRemoveDataSyncUserOperation="PNRemoveDataSyncUserOperation",e.PNCreateDataSyncChannelOperation="PNCreateDataSyncChannelOperation",e.PNGetDataSyncChannelOperation="PNGetDataSyncChannelOperation",e.PNGetDataSyncChannelsOperation="PNGetDataSyncChannelsOperation",e.PNSetDataSyncChannelOperation="PNSetDataSyncChannelOperation",e.PNUpdateDataSyncChannelOperation="PNUpdateDataSyncChannelOperation",e.PNRemoveDataSyncChannelOperation="PNRemoveDataSyncChannelOperation",e.PNCreateDataSyncMembershipOperation="PNCreateDataSyncMembershipOperation",e.PNGetDataSyncMembershipOperation="PNGetDataSyncMembershipOperation",e.PNGetDataSyncMembershipsOperation="PNGetDataSyncMembershipsOperation",e.PNSetDataSyncMembershipOperation="PNSetDataSyncMembershipOperation",e.PNUpdateDataSyncMembershipOperation="PNUpdateDataSyncMembershipOperation",e.PNRemoveDataSyncMembershipOperation="PNRemoveDataSyncMembershipOperation",e.PNListFilesOperation="PNListFilesOperation",e.PNGenerateUploadUrlOperation="PNGenerateUploadUrlOperation",e.PNPublishFileOperation="PNPublishFileOperation",e.PNPublishFileMessageOperation="PNPublishFileMessageOperation",e.PNGetFileUrlOperation="PNGetFileUrlOperation",e.PNDownloadFileOperation="PNDownloadFileOperation",e.PNDeleteFileOperation="PNDeleteFileOperation",e.PNAddPushNotificationEnabledChannelsOperation="PNAddPushNotificationEnabledChannelsOperation",e.PNRemovePushNotificationEnabledChannelsOperation="PNRemovePushNotificationEnabledChannelsOperation",e.PNPushNotificationEnabledChannelsOperation="PNPushNotificationEnabledChannelsOperation",e.PNRemoveAllPushNotificationsOperation="PNRemoveAllPushNotificationsOperation",e.PNChannelGroupsOperation="PNChannelGroupsOperation",e.PNRemoveGroupOperation="PNRemoveGroupOperation",e.PNChannelsForGroupOperation="PNChannelsForGroupOperation",e.PNAddChannelsToGroupOperation="PNAddChannelsToGroupOperation",e.PNRemoveChannelsFromGroupOperation="PNRemoveChannelsFromGroupOperation",e.PNAccessManagerGrant="PNAccessManagerGrant",e.PNAccessManagerGrantToken="PNAccessManagerGrantToken",e.PNAccessManagerAudit="PNAccessManagerAudit",e.PNAccessManagerRevokeToken="PNAccessManagerRevokeToken",e.PNHandshakeOperation="PNHandshakeOperation",e.PNReceiveMessagesOperation="PNReceiveMessagesOperation"}(L||(L={}));var K=L;class H{constructor(e){this.configuration=e,this.subscriptionWorkerReady=!1,this.accessTokensMap={},this.workerEventsQueue=[],this.callbacks=new Map,this.setupSubscriptionWorker()}set emitStatus(e){this._emitStatus=e}onUserIdChange(e){this.configuration.userId=e,this.scheduleEventPost({type:"client-update",heartbeatInterval:this.configuration.heartbeatInterval,clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,workerLogLevel:this.configuration.workerLogLevel})}onPresenceStateChange(e){this.scheduleEventPost({type:"client-presence-state-update",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,workerLogLevel:this.configuration.workerLogLevel,state:e})}onHeartbeatIntervalChange(e){this.configuration.heartbeatInterval=e,this.scheduleEventPost({type:"client-update",heartbeatInterval:this.configuration.heartbeatInterval,clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,workerLogLevel:this.configuration.workerLogLevel})}onTokenChange(e){const t={type:"client-update",heartbeatInterval:this.configuration.heartbeatInterval,clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,workerLogLevel:this.configuration.workerLogLevel};this.parsedAccessToken(e).then((s=>{t.preProcessedToken=s,t.accessToken=e})).then((()=>this.scheduleEventPost(t)))}disconnect(){this.scheduleEventPost({type:"client-disconnect",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,workerLogLevel:this.configuration.workerLogLevel})}terminate(){this.scheduleEventPost({type:"client-unregister",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,workerLogLevel:this.configuration.workerLogLevel})}makeSendable(e){if(!e.path.startsWith("/v2/subscribe")&&!e.path.endsWith("/heartbeat")&&!e.path.endsWith("/leave"))return this.configuration.transport.makeSendable(e);let t;this.configuration.logger.debug("SubscriptionWorkerMiddleware","Process request with SharedWorker transport.");const s={type:"send-request",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,request:e,workerLogLevel:this.configuration.workerLogLevel};return e.cancellable&&(t={abort:()=>{const t={type:"cancel-request",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,identifier:e.identifier,workerLogLevel:this.configuration.workerLogLevel};this.scheduleEventPost(t)}}),[new Promise(((t,n)=>{this.callbacks.set(e.identifier,{resolve:t,reject:n}),this.parsedAccessTokenForRequest(e).then((e=>s.preProcessedToken=e)).then((()=>this.scheduleEventPost(s)))})),t]}request(e){return e}scheduleEventPost(e,t=!1){const s=this.sharedSubscriptionWorker;s?s.port.postMessage(e):t?this.workerEventsQueue.splice(0,0,e):this.workerEventsQueue.push(e)}flushScheduledEvents(){const e=this.sharedSubscriptionWorker;if(!e||0===this.workerEventsQueue.length)return;const t=[];for(let e=0;e!t.includes(e))),this.workerEventsQueue.forEach((t=>e.port.postMessage(t))),this.workerEventsQueue=[]}get sharedSubscriptionWorker(){return this.subscriptionWorkerReady?this.subscriptionWorker:null}setupSubscriptionWorker(){if("undefined"!=typeof SharedWorker){try{this.subscriptionWorker=new SharedWorker(this.configuration.workerUrl,`/pubnub-${this.configuration.sdkVersion}`)}catch(e){throw this.configuration.logger.error("SubscriptionWorkerMiddleware",(()=>({messageType:"error",message:e}))),e}this.subscriptionWorker.port.start(),this.scheduleEventPost({type:"client-register",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,heartbeatInterval:this.configuration.heartbeatInterval,workerOfflineClientsCheckInterval:this.configuration.workerOfflineClientsCheckInterval,workerUnsubscribeOfflineClients:this.configuration.workerUnsubscribeOfflineClients,workerLogLevel:this.configuration.workerLogLevel},!0),this.subscriptionWorker.port.onmessage=e=>this.handleWorkerEvent(e),this.shouldAnnounceNewerSharedWorkerVersionAvailability()&&localStorage.setItem("PNSubscriptionSharedWorkerVersion",this.configuration.sdkVersion),window.addEventListener("storage",(e=>{"PNSubscriptionSharedWorkerVersion"===e.key&&e.newValue&&this._emitStatus&&this.isNewerSharedWorkerVersion(e.newValue)&&this._emitStatus({error:!1,category:h.PNSharedWorkerUpdatedCategory})}))}}handleWorkerEvent(e){const{data:t}=e;if("shared-worker-ping"===t.type||"shared-worker-connected"===t.type||"shared-worker-console-log"===t.type||"shared-worker-console-dir"===t.type||t.clientIdentifier===this.configuration.clientIdentifier)if("shared-worker-connected"===t.type)this.configuration.logger.trace("SharedWorker","Ready for events processing."),this.subscriptionWorkerReady=!0,this.flushScheduledEvents();else if("shared-worker-console-log"===t.type)this.configuration.logger.debug("SharedWorker",(()=>"string"==typeof t.message||"number"==typeof t.message||"boolean"==typeof t.message?{messageType:"text",message:t.message}:t.message));else if("shared-worker-console-dir"===t.type)this.configuration.logger.debug("SharedWorker",(()=>({messageType:"object",message:t.data,details:t.message?t.message:void 0})));else if("shared-worker-ping"===t.type){const{subscriptionKey:e,clientIdentifier:t}=this.configuration;this.scheduleEventPost({type:"client-pong",subscriptionKey:e,clientIdentifier:t,workerLogLevel:this.configuration.workerLogLevel})}else if("request-process-success"===t.type||"request-process-error"===t.type)if(this.callbacks.has(t.identifier)){const{resolve:e,reject:s}=this.callbacks.get(t.identifier);this.callbacks.delete(t.identifier),"request-process-success"===t.type?e({status:t.response.status,url:t.url,headers:t.response.headers,body:t.response.body}):s(this.errorFromRequestSendingError(t))}else this._emitStatus&&t.url.indexOf("/v2/presence")>=0&&t.url.indexOf("/heartbeat")>=0&&("request-process-success"===t.type&&this.configuration.announceSuccessfulHeartbeats?this._emitStatus({statusCode:t.response.status,error:!1,operation:K.PNHeartbeatOperation,category:h.PNAcknowledgmentCategory}):"request-process-error"===t.type&&this.configuration.announceFailedHeartbeats&&this._emitStatus(this.errorFromRequestSendingError(t).toStatus(K.PNHeartbeatOperation)))}parsedAccessTokenForRequest(e){return r(this,void 0,void 0,(function*(){var t;return this.parsedAccessToken(e.queryParameters?null!==(t=e.queryParameters.auth)&&void 0!==t?t:"":void 0)}))}parsedAccessToken(e){return r(this,void 0,void 0,(function*(){if(e)return this.accessTokensMap[e]?this.accessTokensMap[e]:this.stringifyAccessToken(e).then((([t,s])=>{if(t&&s)return(this.accessTokensMap={[e]:{token:s,expiration:t.timestamp+60*t.ttl}})[e]}))}))}stringifyAccessToken(e){return r(this,void 0,void 0,(function*(){if(!this.configuration.tokenManager)return[void 0,void 0];const t=this.configuration.tokenManager.parseToken(e);if(!t)return[void 0,void 0];const s=e=>e?Object.entries(e).sort((([e],[t])=>e.localeCompare(t))).map((([e,t])=>Object.entries(t||{}).sort((([e],[t])=>e.localeCompare(t))).map((([t,s])=>{return`${e}:${t}=${s?(n=s,Object.entries(n).filter((([e,t])=>t)).map((([e])=>e[0])).sort().join("")):""}`;var n})).join(","))).join(";"):"";let n=[s(t.resources),s(t.patterns),t.authorized_uuid].filter(Boolean).join("|");if("undefined"!=typeof crypto&&crypto.subtle){const e=yield crypto.subtle.digest("SHA-256",(new TextEncoder).encode(n));n=String.fromCharCode(...Array.from(new Uint8Array(e)))}return[t,"undefined"!=typeof btoa?btoa(n):n]}))}errorFromRequestSendingError(e){let t=h.PNUnknownCategory,s="Unknown error";if(e.error)"NETWORK_ISSUE"===e.error.type?t=h.PNNetworkIssuesCategory:"TIMEOUT"===e.error.type?t=h.PNTimeoutCategory:"ABORTED"===e.error.type&&(t=h.PNCancelledCategory),s=`${e.error.message} (${e.identifier})`;else if(e.response){const{url:t,response:s}=e;return G.create({url:t,headers:s.headers,body:s.body,status:s.status},s.body)}return new G(s,t,0,new Error(s))}shouldAnnounceNewerSharedWorkerVersionAvailability(){const e=localStorage.getItem("PNSubscriptionSharedWorkerVersion");return!e||!this.isNewerSharedWorkerVersion(e)}isNewerSharedWorkerVersion(e){const[t,s,n]=this.configuration.sdkVersion.split(".").map(Number),[i,r,a]=e.split(".").map(Number);return i>t||r>s||a>n}}function B(e,t=0){const s=e=>"object"==typeof e&&null!==e&&e.constructor===Object,n=e=>"number"==typeof e&&isFinite(e);if(!s(e))return e;const i={};return Object.keys(e).forEach((r=>{const a=(e=>"string"==typeof e||e instanceof String)(r);let o=r;const c=e[r];if(t<2)if(a&&r.indexOf(",")>=0){o=r.split(",").map(Number).reduce(((e,t)=>e+String.fromCharCode(t)),"")}else(n(r)||a&&!isNaN(Number(r)))&&(o=String.fromCharCode(n(r)?r:parseInt(r,10)));i[o]=s(c)?B(c,t+1):c})),i}const W=e=>{var t,s,n,i,r,a;return e.subscriptionWorkerUrl&&"undefined"==typeof SharedWorker&&(e.subscriptionWorkerUrl=null),Object.assign(Object.assign({},(e=>{var t,s,n,i,r,a,o,c,u,l,h,p,g,b,y;const m=Object.assign({},e);if(null!==(t=m.ssl)&&void 0!==t||(m.ssl=!0),null!==(s=m.transactionalRequestTimeout)&&void 0!==s||(m.transactionalRequestTimeout=15),null!==(n=m.subscribeRequestTimeout)&&void 0!==n||(m.subscribeRequestTimeout=310),null!==(i=m.fileRequestTimeout)&&void 0!==i||(m.fileRequestTimeout=300),null!==(r=m.restore)&&void 0!==r||(m.restore=!0),null!==(a=m.useInstanceId)&&void 0!==a||(m.useInstanceId=!1),null!==(o=m.suppressLeaveEvents)&&void 0!==o||(m.suppressLeaveEvents=!1),null!==(c=m.requestMessageCountThreshold)&&void 0!==c||(m.requestMessageCountThreshold=100),null!==(u=m.autoNetworkDetection)&&void 0!==u||(m.autoNetworkDetection=!1),null!==(l=m.enableEventEngine)&&void 0!==l||(m.enableEventEngine=!0),null!==(h=m.maintainPresenceState)&&void 0!==h||(m.maintainPresenceState=!0),null!==(p=m.useSmartHeartbeat)&&void 0!==p||(m.useSmartHeartbeat=!1),null!==(g=m.keepAlive)&&void 0!==g||(m.keepAlive=!1),m.userId&&m.uuid)throw new d("PubNub client configuration error: use only 'userId'");if(null!==(b=m.userId)&&void 0!==b||(m.userId=m.uuid),!m.userId)throw new d("PubNub client configuration error: 'userId' not set");if(0===(null===(y=m.userId)||void 0===y?void 0:y.trim().length))throw new d("PubNub client configuration error: 'userId' is empty");m.origin||(m.origin=Array.from({length:20},((e,t)=>`ps${t+1}.pndsn.com`)));const f={subscribeKey:m.subscribeKey,publishKey:m.publishKey,secretKey:m.secretKey};void 0!==m.presenceTimeout&&(m.presenceTimeout>320?(m.presenceTimeout=320,console.warn("WARNING: Presence timeout is larger than the maximum. Using maximum value: ",320)):m.presenceTimeout<=0&&(console.warn("WARNING: Presence timeout should be larger than zero."),delete m.presenceTimeout)),void 0!==m.presenceTimeout?m.heartbeatInterval=m.presenceTimeout/2-1:m.presenceTimeout=300;let v=!1,O=!0,S=5,j=!1,w=100,k=!0;return void 0!==m.dedupeOnSubscribe&&"boolean"==typeof m.dedupeOnSubscribe&&(j=m.dedupeOnSubscribe),void 0!==m.maximumCacheSize&&"number"==typeof m.maximumCacheSize&&(w=m.maximumCacheSize),void 0!==m.useRequestId&&"boolean"==typeof m.useRequestId&&(k=m.useRequestId),void 0!==m.announceSuccessfulHeartbeats&&"boolean"==typeof m.announceSuccessfulHeartbeats&&(v=m.announceSuccessfulHeartbeats),void 0!==m.announceFailedHeartbeats&&"boolean"==typeof m.announceFailedHeartbeats&&(O=m.announceFailedHeartbeats),void 0!==m.fileUploadPublishRetryLimit&&"number"==typeof m.fileUploadPublishRetryLimit&&(S=m.fileUploadPublishRetryLimit),Object.assign(Object.assign({},m),{keySet:f,dedupeOnSubscribe:j,maximumCacheSize:w,useRequestId:k,announceSuccessfulHeartbeats:v,announceFailedHeartbeats:O,fileUploadPublishRetryLimit:S})})(e)),{listenToBrowserNetworkEvents:null===(t=e.listenToBrowserNetworkEvents)||void 0===t||t,subscriptionWorkerUrl:e.subscriptionWorkerUrl,subscriptionWorkerOfflineClientsCheckInterval:null!==(s=e.subscriptionWorkerOfflineClientsCheckInterval)&&void 0!==s?s:10,subscriptionWorkerUnsubscribeOfflineClients:null!==(n=e.subscriptionWorkerUnsubscribeOfflineClients)&&void 0!==n&&n,subscriptionWorkerLogVerbosity:null!==(i=e.subscriptionWorkerLogVerbosity)&&void 0!==i&&i,transport:null!==(r=e.transport)&&void 0!==r?r:"fetch",keepAlive:null===(a=e.keepAlive)||void 0===a||a})};var V;!function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Info=2]="Info",e[e.Warn=3]="Warn",e[e.Error=4]="Error",e[e.None=5]="None"}(V||(V={}));class z{debug(e){this.log(e)}error(e){this.log(e)}info(e){this.log(e)}trace(e){this.log(e)}warn(e){this.log(e)}toString(){return"ConsoleLogger {}"}log(e){const t=V[e.level],s=t.toLowerCase();console["trace"===s?"debug":s](`${e.timestamp.toISOString()} PubNub-${e.pubNubId} ${t.padEnd(5," ")}${e.location?` ${e.location}`:""} ${this.logMessage(e)}`)}logMessage(e){if("text"===e.messageType)return e.message;if("object"===e.messageType)return`${e.details?`${e.details}\n`:""}${this.formattedObject(e)}`;if("network-request"===e.messageType){const t=!!e.canceled||!!e.failed,s=e.minimumLevel!==V.Trace||t?void 0:this.formattedHeaders(e),n=e.message,i=n.queryParameters&&Object.keys(n.queryParameters).length>0?T(n.queryParameters):void 0,r=`${n.origin}${n.path}${i?`?${i}`:""}`,a=t?void 0:this.formattedBody(e);let o="Sending";t&&(o=`${e.canceled?"Canceled":"Failed"}${e.details?` (${e.details})`:""}`);const c=((null==a?void 0:a.formData)?"FormData":"Method").length;return`${o} HTTP request:\n ${this.paddedString("Method",c)}: ${n.method}\n ${this.paddedString("URL",c)}: ${r}${s?`\n ${this.paddedString("Headers",c)}:\n${s}`:""}${(null==a?void 0:a.formData)?`\n ${this.paddedString("FormData",c)}:\n${a.formData}`:""}${(null==a?void 0:a.body)?`\n ${this.paddedString("Body",c)}:\n${a.body}`:""}`}if("network-response"===e.messageType){const t=e.minimumLevel===V.Trace?this.formattedHeaders(e):void 0,s=this.formattedBody(e),n=((null==s?void 0:s.formData)?"Headers":"Status").length,i=e.message;return`Received HTTP response:\n ${this.paddedString("URL",n)}: ${i.url}\n ${this.paddedString("Status",n)}: ${i.status}${e.details?`\n ${this.paddedString("Details",n)}: ${e.details}`:""}${t?`\n ${this.paddedString("Headers",n)}:\n${t}`:""}${(null==s?void 0:s.body)?`\n ${this.paddedString("Body",n)}:\n${s.body}`:""}`}if("error"===e.messageType){const t=this.formattedErrorStatus(e),s=e.message;return`${s.name}: ${s.message}${t?`\n${t}`:""}`}return""}formattedObject(e){const t=(s,n=1,i=!1)=>{const r=10===n,a=" ".repeat(2*n),o=[],c=(t,s)=>!!e.ignoredKeys&&("function"==typeof e.ignoredKeys?e.ignoredKeys(t,s):e.ignoredKeys.includes(t));if("string"==typeof s)o.push(`${a}- ${s}`);else if("number"==typeof s)o.push(`${a}- ${s}`);else if("boolean"==typeof s)o.push(`${a}- ${s}`);else if(null===s)o.push(`${a}- null`);else if(void 0===s)o.push(`${a}- undefined`);else if("function"==typeof s)o.push(`${a}- `);else if("object"==typeof s)if(Array.isArray(s)||"function"!=typeof s.toString||0===s.toString().indexOf("[object"))if(Array.isArray(s))for(const e of s){const s=i?"":a;if(null===e)o.push(`${s}- null`);else if(void 0===e)o.push(`${s}- undefined`);else if("function"==typeof e)o.push(`${s}- `);else if("object"==typeof e){const i=Array.isArray(e),a=r?"...":t(e,n+1,!i);o.push(`${s}-${i&&!r?"\n":" "}${a}`)}else o.push(`${s}- ${e}`);i=!1}else{const e=s,u=Object.keys(e),l=u.reduce(((t,s)=>Math.max(t,c(s,e)?t:s.length)),0);for(const s of u){if(c(s,e))continue;const u=i?"":a,h=e[s],d=s.padEnd(l," ");if(null===h)o.push(`${u}${d}: null`);else if(void 0===h)o.push(`${u}${d}: undefined`);else if("function"==typeof h)o.push(`${u}${d}: `);else if("object"==typeof h){const e=Array.isArray(h),s=e&&0===h.length,i=!(e||h instanceof String||0!==Object.keys(h).length),a=!e&&"function"==typeof h.toString&&0!==h.toString().indexOf("[object"),c=r?"...":s?"[]":i?"{}":t(h,n+1,a);o.push(`${u}${d}:${r||a||s||i?" ":"\n"}${c}`)}else o.push(`${u}${d}: ${h}`);i=!1}}else o.push(`${i?"":a}${s.toString()}`),i=!1;return o.join("\n")};return t(e.message)}formattedHeaders(e){if(!e.message.headers)return;const t=e.message.headers,s=Object.keys(t).reduce(((e,t)=>Math.max(e,t.length)),0);return Object.keys(t).map((e=>` - ${e.toLowerCase().padEnd(s," ")}: ${t[e]}`)).join("\n")}formattedBody(e){var t;if(!e.message.headers)return;let s,n;const i=e.message.headers,r=null!==(t=i["content-type"])&&void 0!==t?t:i["Content-Type"],a="formData"in e.message?e.message.formData:void 0,o=e.message.body;if(a){const e=a.reduce(((e,{key:t})=>Math.max(e,t.length)),0);s=a.map((({key:t,value:s})=>` - ${t.padEnd(e," ")}: ${s}`)).join("\n")}return o?(n="string"==typeof o?` ${o}`:o instanceof ArrayBuffer||"[object ArrayBuffer]"===Object.prototype.toString.call(o)?!r||-1===r.indexOf("javascript")&&-1===r.indexOf("json")?` ArrayBuffer { byteLength: ${o.byteLength} }`:` ${z.decoder.decode(o)}`:` File { name: ${o.name}${o.contentLength?`, contentLength: ${o.contentLength}`:""}${o.mimeType?`, mimeType: ${o.mimeType}`:""} }`,{body:n,formData:s}):{formData:s}}formattedErrorStatus(e){if(!e.message.status)return;const t=e.message.status,s=t.errorData;let n;if(z.isError(s))n=` ${s.name}: ${s.message}`,s.stack&&(n+=`\n${s.stack.split("\n").map((e=>` ${e}`)).join("\n")}`);else if(s)try{n=` ${JSON.stringify(s)}`}catch(e){n=` ${s}`}return` Category : ${t.category}\n Operation : ${t.operation}\n Status : ${t.statusCode}${n?`\n Error data:\n${n}`:""}`}paddedString(e,t){return e.padEnd(t-e.length," ")}static isError(e){return!!e&&(e instanceof Error||"[object Error]"===Object.prototype.toString.call(e))}}z.decoder=new TextDecoder;const J=[404,409];var X;!function(e){e.Unknown="UnknownEndpoint",e.MessageSend="MessageSendEndpoint",e.Subscribe="SubscribeEndpoint",e.Presence="PresenceEndpoint",e.Files="FilesEndpoint",e.MessageStorage="MessageStorageEndpoint",e.ChannelGroups="ChannelGroupsEndpoint",e.DevicePushNotifications="DevicePushNotificationsEndpoint",e.AppContext="AppContextEndpoint",e.MessageReactions="MessageReactionsEndpoint"}(X||(X={}));class Q{static None(){return{shouldRetry:(e,t,s,n,i)=>!1,getDelay:(e,t)=>-1,validate:()=>!0}}static LinearRetryPolicy(e){var t;return{delay:e.delay,maximumRetry:e.maximumRetry,excluded:null!==(t=e.excluded)&&void 0!==t?t:[],shouldRetry(e,t,s,n,i){return Y(e,t,s,null!=n?n:0,this.maximumRetry,this.excluded,i)},getDelay(e,t){let s=-1;return t&&void 0!==t.headers["retry-after"]&&(s=parseInt(t.headers["retry-after"],10)),-1===s&&(s=this.delay),1e3*(s+Math.random())},validate(){if(this.delay<2)throw new Error("Delay can not be set less than 2 seconds for retry")}}}static ExponentialRetryPolicy(e){var t;return{minimumDelay:e.minimumDelay,maximumDelay:e.maximumDelay,maximumRetry:e.maximumRetry,excluded:null!==(t=e.excluded)&&void 0!==t?t:[],shouldRetry(e,t,s,n,i){return Y(e,t,s,null!=n?n:0,this.maximumRetry,this.excluded,i)},getDelay(e,t){let s=-1;return t&&void 0!==t.headers["retry-after"]&&(s=parseInt(t.headers["retry-after"],10)),-1===s&&(s=Math.min(this.minimumDelay*Math.pow(2,e),this.maximumDelay)),1e3*(s+Math.random())},validate(){if(this.minimumDelay<2)throw new Error("Minimum delay can not be set less than 2 seconds for retry")}}}}const Y=(e,t,s,n,i,r,a)=>{var o;if(s&&(s===h.PNCancelledCategory||s===h.PNBadRequestCategory||s===h.PNAccessDeniedCategory))return!1;if(Z(e,r))return!1;if(n>i)return!1;const c=null!==(o=null==t?void 0:t.status)&&void 0!==o?o:a;return(void 0===c||!J.includes(c))&&(!t||(429===t.status||t.status>=500))},Z=(e,t)=>!!(t&&t.length>0)&&t.includes(ee(e)),ee=e=>{let t=X.Unknown;return e.path.startsWith("/v2/subscribe")?t=X.Subscribe:e.path.startsWith("/publish/")||e.path.startsWith("/signal/")?t=X.MessageSend:e.path.startsWith("/v2/presence")?t=X.Presence:e.path.startsWith("/v2/history")||e.path.startsWith("/v3/history")?t=X.MessageStorage:e.path.startsWith("/v1/message-actions/")?t=X.MessageReactions:e.path.startsWith("/v1/channel-registration/")||e.path.startsWith("/v2/objects/")?t=X.ChannelGroups:e.path.startsWith("/v1/push/")||e.path.startsWith("/v2/push/")?t=X.DevicePushNotifications:e.path.startsWith("/v1/files/")&&(t=X.Files),t};class te{constructor(e,t,s){this.previousEntryTimestamp=0,this.pubNubId=e,this.minLogLevel=t,this.loggers=s}get logLevel(){return this.minLogLevel}trace(e,t){this.log(V.Trace,e,t)}debug(e,t){this.log(V.Debug,e,t)}info(e,t){this.log(V.Info,e,t)}warn(e,t){this.log(V.Warn,e,t)}error(e,t){this.log(V.Error,e,t)}log(e,t,s){if(ee[i](r)))}}var se={exports:{}}; -/*! lil-uuid - v0.1 - MIT License - https://github.com/lil-js/uuid */!function(e,t){!function(e){var t="0.1.0",s={3:/^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,4:/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,5:/^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,all:/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i};function n(){var e,t,s="";for(e=0;e<32;e++)t=16*Math.random()|0,8!==e&&12!==e&&16!==e&&20!==e||(s+="-"),s+=(12===e?4:16===e?3&t|8:t).toString(16);return s}function i(e,t){var n=s[t||"all"];return n&&n.test(e)||!1}n.isUUID=i,n.VERSION=t,e.uuid=n,e.isUUID=i}(t),null!==e&&(e.exports=t.uuid)}(se,se.exports);var ne=t(se.exports),ie={createUUID:()=>ne.uuid?ne.uuid():ne()};const re=(e,t)=>{var s,n,i,r;!e.retryConfiguration&&e.enableEventEngine&&(e.retryConfiguration=Q.ExponentialRetryPolicy({minimumDelay:2,maximumDelay:150,maximumRetry:6,excluded:[X.MessageSend,X.Presence,X.Files,X.MessageStorage,X.ChannelGroups,X.DevicePushNotifications,X.AppContext,X.MessageReactions]}));const a=`pn-${ie.createUUID()}`;e.logVerbosity?e.logLevel=V.Debug:void 0===e.logLevel&&(e.logLevel=V.None);const o=new te(oe(a),e.logLevel,[...null!==(s=e.loggers)&&void 0!==s?s:[],new z]);void 0!==e.logVerbosity&&o.warn("Configuration","'logVerbosity' is deprecated. Use 'logLevel' instead."),null===(n=e.retryConfiguration)||void 0===n||n.validate();const c=e.useRandomIVs;null!==(i=e.useRandomIVs)&&void 0!==i||(e.useRandomIVs=true),void 0!==c&&(o.warn("Configuration","'useRandomIVs' is deprecated. Pass it to 'cryptoModule' instead."),!1===c&&o.warn("Configuration","Setting 'useRandomIVs' to false is insecure and should only be used to support legacy clients.\n Do not disable random IVs in new applications.")),e.origin=ae(null!==(r=e.ssl)&&void 0!==r&&r,e.origin);const u=e.cryptoModule;u&&delete e.cryptoModule;const l=Object.assign(Object.assign({},e),{_pnsdkSuffix:{},_loggerManager:o,_instanceId:a,_cryptoModule:void 0,_cipherKey:void 0,_setupCryptoModule:t,get instanceId(){if(e.useInstanceId)return this._instanceId},getInstanceId(){if(e.useInstanceId)return this._instanceId},getUserId(){return this.userId},setUserId(e){if(!e||"string"!=typeof e||0===e.trim().length)throw new Error("Missing or invalid userId parameter. Provide a valid string userId");this.userId=e},logger(){return this._loggerManager},getAuthKey(){return this.authKey},setAuthKey(e){this.authKey=e},getFilterExpression(){return this.filterExpression},setFilterExpression(e){this.filterExpression=e},getCipherKey(){return this._cipherKey},setCipherKey(t){this._cipherKey=t,t||!this._cryptoModule?t&&this._setupCryptoModule&&(this._cryptoModule=this._setupCryptoModule({cipherKey:t,useRandomIVs:e.useRandomIVs,customEncrypt:this.getCustomEncrypt(),customDecrypt:this.getCustomDecrypt(),logger:this.logger()})):this._cryptoModule=void 0},getCryptoModule(){return this._cryptoModule},getUseRandomIVs:()=>e.useRandomIVs,isSharedWorkerEnabled:()=>"Web"===e.sdkFamily&&e.subscriptionWorkerUrl,getKeepPresenceChannelsInPresenceRequests:()=>"Web"===e.sdkFamily&&e.subscriptionWorkerUrl,setPresenceTimeout(e){this.heartbeatInterval=e/2-1,this.presenceTimeout=e},getPresenceTimeout(){return this.presenceTimeout},getHeartbeatInterval(){return this.heartbeatInterval},setHeartbeatInterval(e){this.heartbeatInterval=e},getTransactionTimeout(){return this.transactionalRequestTimeout},getSubscribeTimeout(){return this.subscribeRequestTimeout},getFileTimeout(){return this.fileRequestTimeout},get PubNubFile(){return e.PubNubFile},get version(){return"13.1.3"},getVersion(){return this.version},_addPnsdkSuffix(e,t){this._pnsdkSuffix[e]=`${t}`},_getPnsdkSuffix(e){const t=Object.values(this._pnsdkSuffix).join(e);return t.length>0?e+t:""},getUUID(){return this.getUserId()},setUUID(e){this.setUserId(e)},getCustomEncrypt:()=>e.customEncrypt,getCustomDecrypt:()=>e.customDecrypt});return e.cipherKey?(o.warn("Configuration","'cipherKey' is deprecated. Use 'cryptoModule' instead."),l.setCipherKey(e.cipherKey)):u&&(l._cryptoModule=u),l},ae=(e,t)=>{const s=e?"https://":"http://";return"string"==typeof t?`${s}${t}`:`${s}${t[Math.floor(Math.random()*t.length)]}`},oe=e=>{let t=2166136261;for(let s=0;s>>0;return t.toString(16).padStart(8,"0")};class ce{constructor(e){this.cbor=e}setToken(e){e&&e.length>0?this.token=e:this.token=void 0}getToken(){return this.token}parseToken(e){var t,s;const n=this.cbor.decodeToken(e);if(void 0!==n){const e=n.res.uuid?Object.keys(n.res.uuid):[],i=n.res.usr?Object.keys(n.res.usr):[],r=Object.keys(n.res.chan),a=Object.keys(n.res.grp),o=n.pat.uuid?Object.keys(n.pat.uuid):[],c=n.pat.usr?Object.keys(n.pat.usr):[],u=Object.keys(n.pat.chan),l=Object.keys(n.pat.grp),h={version:n.v,timestamp:n.t,ttl:n.ttl,authorized_uuid:n.uuid,signature:n.sig},d=e.length>0,p=i.length>0,g=r.length>0,b=a.length>0;if(d||p||g||b){if(h.resources={},d){const t=h.resources.uuids={};e.forEach((e=>t[e]=this.extractPermissions(n.res.uuid[e])))}if(p){const e=h.resources.users={};i.forEach((t=>e[t]=this.extractCrudPermissions(n.res.usr[t])))}if(g){const e=h.resources.channels={};r.forEach((t=>e[t]=this.extractPermissions(n.res.chan[t])))}if(b){const e=h.resources.groups={};a.forEach((t=>e[t]=this.extractPermissions(n.res.grp[t])))}}const y=this.extractDataSyncScopes(n.res);y&&((null!==(t=h.resources)&&void 0!==t?t:h.resources={}).dataSync=y);const m=o.length>0,f=c.length>0,v=u.length>0,O=l.length>0;if(m||f||v||O){if(h.patterns={},m){const e=h.patterns.uuids={};o.forEach((t=>e[t]=this.extractPermissions(n.pat.uuid[t])))}if(f){const e=h.patterns.users={};c.forEach((t=>e[t]=this.extractCrudPermissions(n.pat.usr[t])))}if(v){const e=h.patterns.channels={};u.forEach((t=>e[t]=this.extractPermissions(n.pat.chan[t])))}if(O){const e=h.patterns.groups={};l.forEach((t=>e[t]=this.extractPermissions(n.pat.grp[t])))}}const S=this.extractDataSyncScopes(n.pat);S&&((null!==(s=h.patterns)&&void 0!==s?s:h.patterns={}).dataSync=S);const j=this.extractCategories(n.cat);return j&&(h.categories=j),n.meta&&Object.keys(n.meta).length>0&&(h.meta=n.meta),h}}extractPermissions(e){const t={read:!1,write:!1,manage:!1,delete:!1,get:!1,update:!1,join:!1};return 128&~e||(t.join=!0),64&~e||(t.update=!0),32&~e||(t.get=!0),8&~e||(t.delete=!0),4&~e||(t.manage=!0),2&~e||(t.write=!0),1&~e||(t.read=!0),t}extractCategories(e){if(!e)return;const t={};return"number"==typeof e.chan&&(t.channels={get:!(32&~e.chan)}),"number"==typeof e.uuid&&(t.uuids={get:!(32&~e.uuid)}),Object.keys(t).length>0?t:void 0}extractDataSyncScopes(e){let t;return[["entities","datasync:entities"],["relationships","datasync:relationships"],["memberships","datasync:memberships"]].forEach((([s,n])=>{const i=e[n];if(!i)return;const r=Object.keys(i);if(0===r.length)return;const a=(null!=t?t:t={})[s]={};r.forEach((e=>a[e]=this.extractCrudPermissions(i[e])))})),t}extractCrudPermissions(e){return{create:!(16&~e),get:!(32&~e),update:!(64&~e),delete:!(8&~e)}}}var ue;!function(e){e.GET="GET",e.POST="POST",e.PATCH="PATCH",e.PUT="PUT",e.DELETE="DELETE",e.LOCAL="LOCAL"}(ue||(ue={}));class le{constructor(e,t,s,n){this.publishKey=e,this.secretKey=t,this.hasher=s,this.logger=n}signature(e){const t=e.path.startsWith("/publish")?ue.GET:e.method;let s=`${t}\n${this.publishKey}\n${e.path}\n${this.queryParameters(e.queryParameters)}\n`;if(t===ue.POST||t===ue.PATCH||t===ue.PUT){const t=e.body;let n;t&&t instanceof ArrayBuffer?n=le.textDecoder.decode(t):t&&"object"!=typeof t&&(n=t),n&&(s+=n)}return this.logger.trace("RequestSignature",(()=>({messageType:"text",message:`Request signature input:\n${s}`}))),`v2.${this.hasher(s,this.secretKey)}`.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}queryParameters(e){return Object.keys(e).sort().map((t=>{const s=e[t];return Array.isArray(s)?s.sort().map((e=>`${t}=${C(e)}`)).join("&"):`${t}=${C(s)}`})).join("&")}}le.textDecoder=new TextDecoder("utf-8");class he{constructor(e){this.configuration=e;const{clientConfiguration:{keySet:t},shaHMAC:s}=e;t.secretKey&&s&&(this.signatureGenerator=new le(t.publishKey,t.secretKey,s,this.logger))}get logger(){return this.configuration.clientConfiguration.logger()}makeSendable(e){const t=this.configuration.clientConfiguration.retryConfiguration,s=this.configuration.transport;if(void 0!==t){let n,i,r=!1,a=0;const o={abort:e=>{r=!0,n&&clearTimeout(n),i&&i.abort(e)}};return[new Promise(((o,c)=>{const u=()=>{if(r)return;const[l,h]=s.makeSendable(this.request(e));i=h;const d=(s,i)=>{let r=-1;t.shouldRetry(e,s,null==i?void 0:i.category,a+1,null==i?void 0:i.statusCode)&&(r=t.getDelay(a,s)),r>0?(a++,this.logger.warn("PubNubMiddleware",`HTTP request retry #${a} in ${r}ms.`),n=setTimeout((()=>u()),r)):s?o(s):i&&c(i)};l.then((e=>d(e))).catch((e=>d(void 0,e)))};u()})),i?o:void 0]}return s.makeSendable(this.request(e))}request(e){var t;const{clientConfiguration:s}=this.configuration;return(e=this.configuration.transport.request(e)).queryParameters||(e.queryParameters={}),s.useInstanceId&&(e.queryParameters.instanceid=s.getInstanceId()),e.queryParameters.uuid||(e.queryParameters.uuid=s.userId),s.useRequestId&&(e.queryParameters.requestid=e.identifier),e.queryParameters.pnsdk=this.generatePNSDK(),null!==(t=e.origin)&&void 0!==t||(e.origin=s.origin),this.authenticateRequest(e),this.signRequest(e),e}authenticateRequest(e){var t;if(e.path.startsWith("/v2/auth/")||e.path.startsWith("/v3/pam/")||e.path.startsWith("/time"))return;const{clientConfiguration:s,tokenManager:n}=this.configuration,i=null!==(t=n&&n.getToken())&&void 0!==t?t:s.authKey;i&&(e.queryParameters.auth=i)}signRequest(e){this.signatureGenerator&&!e.path.startsWith("/time")&&(e.queryParameters.timestamp=String(Math.floor((new Date).getTime()/1e3)),e.queryParameters.signature=this.signatureGenerator.signature(e))}generatePNSDK(){const{clientConfiguration:e}=this.configuration;if(e.sdkName)return e.sdkName;let t=`PubNub-JS-${e.sdkFamily}`;e.partnerId&&(t+=`-${e.partnerId}`),t+=`/${e.getVersion()}`;const s=e._getPnsdkSuffix(" ");return s.length>0&&(t+=s),t}}class de{constructor(e,t="fetch"){this.logger=e,this.transport=t,e.debug("WebTransport",`Create with configuration:\n - transport: ${t}`),"fetch"===t&&"undefined"==typeof fetch&&(e.warn("WebTransport",`'${t}' not supported in this browser. Fallback to the 'xhr' transport.`),this.transport="xhr"),"fetch"===this.transport&&(de.originalFetch=de.getOriginalFetch(),this.isFetchMonkeyPatched()&&e.warn("WebTransport","Native Web Fetch API 'fetch' function monkey patched."))}makeSendable(e){const t=new AbortController,s={abortController:t,abort:e=>{t.signal.aborted||(this.logger.trace("WebTransport",`On-demand request aborting: ${e}`),t.abort(e))}};return[this.webTransportRequestFromTransportRequest(e).then((t=>(this.logger.debug("WebTransport",(()=>({messageType:"network-request",message:e}))),this.sendRequest(t,s).then((e=>e.arrayBuffer().then((t=>[e,t])))).then((e=>{const s=e[1].byteLength>0?e[1]:void 0,{status:n,headers:i}=e[0],r={};i.forEach(((e,t)=>r[t]=e.toLowerCase()));const a={status:n,url:t.url,headers:r,body:s};if(this.logger.debug("WebTransport",(()=>({messageType:"network-response",message:a}))),n>=400)throw G.create(a);return a})).catch((t=>{const s=("string"==typeof t?t:t.message).toLowerCase();let n="string"==typeof t?new Error(t):t;throw s.includes("timeout")?this.logger.warn("WebTransport",(()=>({messageType:"network-request",message:e,details:"Timeout",canceled:!0}))):s.includes("cancel")||s.includes("abort")?(this.logger.debug("WebTransport",(()=>({messageType:"network-request",message:e,details:"Aborted",canceled:!0}))),n=new Error("Aborted"),n.name="AbortError"):s.includes("network")?this.logger.warn("WebTransport",(()=>({messageType:"network-request",message:e,details:"Network error",failed:!0}))):this.logger.warn("WebTransport",(()=>({messageType:"network-request",message:e,details:G.create(n).message,failed:!0}))),G.create(n)}))))),s]}request(e){return e}sendRequest(e,t){return r(this,void 0,void 0,(function*(){return"fetch"===this.transport?this.sendFetchRequest(e,t):this.sendXHRRequest(e,t)}))}sendFetchRequest(e,t){return r(this,void 0,void 0,(function*(){let s;const n=new Promise(((n,i)=>{s=setTimeout((()=>{clearTimeout(s),i(new Error("Request timeout")),t.abort("Cancel because of timeout")}),1e3*e.timeout)})),i=new Request(e.url,{method:e.method,headers:e.headers,redirect:"follow",body:e.body});return Promise.race([de.originalFetch(i,{signal:t.abortController.signal,credentials:"omit",cache:"no-cache"}).then((e=>(s&&clearTimeout(s),e))),n])}))}sendXHRRequest(e,t){return r(this,void 0,void 0,(function*(){return new Promise(((s,n)=>{var i;const r=new XMLHttpRequest;r.open(e.method,e.url,!0);let a=!1;r.responseType="arraybuffer",r.timeout=1e3*e.timeout,t.abortController.signal.onabort=()=>{r.readyState!=XMLHttpRequest.DONE&&r.readyState!=XMLHttpRequest.UNSENT&&(a=!0,r.abort())},Object.entries(null!==(i=e.headers)&&void 0!==i?i:{}).forEach((([e,t])=>r.setRequestHeader(e,t))),r.onabort=()=>{n(new Error("Aborted"))},r.ontimeout=()=>{n(new Error("Request timeout"))},r.onerror=()=>{if(!a){const t=this.transportResponseFromXHR(e.url,r);n(new Error(G.create(t).message))}},r.onload=()=>{const e=new Headers;r.getAllResponseHeaders().split("\r\n").forEach((t=>{const[s,n]=t.split(": ");s.length>1&&n.length>1&&e.append(s,n)})),s(new Response(r.response,{status:r.status,headers:e,statusText:r.statusText}))},r.send(e.body)}))}))}webTransportRequestFromTransportRequest(e){return r(this,void 0,void 0,(function*(){let t,s=e.path;if(e.formData&&e.formData.length>0){e.queryParameters={};const s=e.body,n=new FormData;for(const{key:t,value:s}of e.formData)n.append(t,s);try{const e=yield s.toArrayBuffer();n.append("file",new Blob([e],{type:"application/octet-stream"}),s.name)}catch(e){this.logger.warn("WebTransport",(()=>({messageType:"error",message:e})));try{const e=yield s.toFileUri();n.append("file",e,s.name)}catch(e){this.logger.error("WebTransport",(()=>({messageType:"error",message:e})))}}t=n}else if(e.body&&("string"==typeof e.body||e.body instanceof ArrayBuffer))if(e.compressible&&"undefined"!=typeof CompressionStream){const s="string"==typeof e.body?de.encoder.encode(e.body):e.body,n=s.byteLength,i=new ReadableStream({start(e){e.enqueue(s),e.close()}});t=yield new Response(i.pipeThrough(new CompressionStream("deflate"))).arrayBuffer(),this.logger.trace("WebTransport",(()=>{const e=t.byteLength,s=(e/n).toFixed(2);return{messageType:"text",message:`Body of ${n} bytes, compressed by ${s}x to ${e} bytes.`}}))}else t=e.body;return e.queryParameters&&0!==Object.keys(e.queryParameters).length&&(s=`${s}?${T(e.queryParameters)}`),{url:`${e.origin}${s}`,method:e.method,headers:e.headers,timeout:e.timeout,body:t}}))}isFetchMonkeyPatched(e){return!(null!=e?e:fetch).toString().includes("[native code]")&&"fetch"!==fetch.name}transportResponseFromXHR(e,t){const s=t.getAllResponseHeaders().split("\n"),n={};for(const e of s){const[t,s]=e.trim().split(":");t&&s&&(n[t.toLowerCase()]=s.trim())}return{status:t.status,url:e,headers:n,body:t.response}}static getOriginalFetch(){if("undefined"==typeof document||!document.body)return fetch.bind(globalThis);let e=document.querySelector('iframe[name="pubnub-context-unpatched-fetch"]');return e||(e=document.createElement("iframe"),e.style.display="none",e.name="pubnub-context-unpatched-fetch",e.src="about:blank",document.body.appendChild(e)),e.contentWindow?e.contentWindow.fetch.bind(e.contentWindow):fetch.bind(globalThis)}}de.encoder=new TextEncoder,de.decoder=new TextDecoder;class pe{constructor(e){this.params=e,this.requestIdentifier=ie.createUUID(),this._cancellationController=null}get cancellationController(){return this._cancellationController}set cancellationController(e){this._cancellationController=e}abort(e){this&&this.cancellationController&&this.cancellationController.abort(e)}operation(){throw Error("Should be implemented by subclass.")}validate(){}parse(e){return r(this,void 0,void 0,(function*(){return this.deserializeResponse(e)}))}request(){var e,t,s,n,i,r;const a={method:null!==(t=null===(e=this.params)||void 0===e?void 0:e.method)&&void 0!==t?t:ue.GET,path:this.path,queryParameters:this.queryParameters,cancellable:null!==(n=null===(s=this.params)||void 0===s?void 0:s.cancellable)&&void 0!==n&&n,compressible:null!==(r=null===(i=this.params)||void 0===i?void 0:i.compressible)&&void 0!==r&&r,timeout:10,identifier:this.requestIdentifier},o=this.headers;if(o&&(a.headers=o),a.method===ue.POST||a.method===ue.PATCH||a.method===ue.PUT){const[e,t]=[this.body,this.formData];t&&(a.formData=t),e&&(a.body=e)}return a}get headers(){var e,t;return Object.assign({"Accept-Encoding":"gzip, deflate"},null!==(t=null===(e=this.params)||void 0===e?void 0:e.compressible)&&void 0!==t&&t?{"Content-Encoding":"deflate"}:{})}get path(){throw Error("`path` getter should be implemented by subclass.")}get queryParameters(){return{}}get formData(){}get body(){}deserializeResponse(e){const t=pe.decoder.decode(e.body),s=e.headers["content-type"];let n;if(!s||-1===s.indexOf("javascript")&&-1===s.indexOf("json"))throw new d("Service response error, check status for details",g(t,e.status));try{n=JSON.parse(t)}catch(s){throw console.error("Error parsing JSON response:",s),new d("Service response error, check status for details",g(t,e.status))}if("status"in n&&"number"==typeof n.status&&n.status>=400)throw G.create(e);return n}}pe.decoder=new TextDecoder;var ge;!function(e){e[e.Presence=-2]="Presence",e[e.Message=-1]="Message",e[e.Signal=1]="Signal",e[e.AppContext=2]="AppContext",e[e.MessageAction=3]="MessageAction",e[e.Files=4]="Files",e[e.DataSync=5]="DataSync"}(ge||(ge={}));const be=new Set([ge.Presence,ge.Message,ge.Signal,ge.AppContext,ge.MessageAction,ge.Files,ge.DataSync]),ye={user:"user",channel:"channel",membership:"membership"};class me extends pe{constructor(e){var t,s,n,i,r,a;super({cancellable:!0}),this.parameters=e,null!==(t=(i=this.parameters).withPresence)&&void 0!==t||(i.withPresence=false),null!==(s=(r=this.parameters).channelGroups)&&void 0!==s||(r.channelGroups=[]),null!==(n=(a=this.parameters).channels)&&void 0!==n||(a.channels=[])}operation(){return K.PNSubscribeOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroups:s}=this.parameters;return e?t||s?void 0:"`channels` and `channelGroups` both should not be empty":"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){let t,s;try{s=pe.decoder.decode(e.body);t=JSON.parse(s)}catch(e){console.error("Error parsing JSON response:",e)}if(!t)throw new d("Service response error, check status for details",g(s,e.status));const n=t.m.filter((e=>{const t=void 0===e.b?e.c:e.b;return this.parameters.channels&&this.parameters.channels.includes(t)||this.parameters.channelGroups&&this.parameters.channelGroups.includes(t)})).map((e=>{let{e:t}=e;if(e.c.endsWith("-pnpres")?t=ge.Presence:null!=t||(t=ge.Message),!be.has(t))return;const s=R(e.d);if(t!=ge.Presence&&t!=ge.Signal&&"string"==typeof e.d)return t==ge.Message?{type:ge.Message,data:this.messageFromEnvelope(e),pn_mfp:s}:{type:ge.Files,data:this.fileFromEnvelope(e),pn_mfp:s};if(t==ge.Message)return{type:ge.Message,data:this.messageFromEnvelope(e),pn_mfp:s};if(t===ge.Presence)return{type:ge.Presence,data:this.presenceEventFromEnvelope(e),pn_mfp:s};if(t==ge.Signal)return{type:ge.Signal,data:this.signalFromEnvelope(e),pn_mfp:s};if(t===ge.AppContext)return{type:ge.AppContext,data:this.appContextFromEnvelope(e),pn_mfp:s};if(t===ge.MessageAction)return{type:ge.MessageAction,data:this.messageActionFromEnvelope(e),pn_mfp:s};if(t===ge.DataSync){const t=this.dataSyncFromEnvelope(e);return t?{type:ge.DataSync,data:t,pn_mfp:s}:{type:ge.Message,data:this.messageFromEnvelope(e),pn_mfp:s}}return{type:ge.Files,data:this.fileFromEnvelope(e),pn_mfp:s}})).filter((e=>void 0!==e));return{cursor:{timetoken:t.t.t,region:t.t.r},messages:n}}))}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{accept:"text/javascript"})}presenceEventFromEnvelope(e){var t;const{d:s}=e,[n,i]=this.subscriptionChannelFromEnvelope(e),r=n.replace("-pnpres",""),a=null!==i?r:null,o=null!==i?i:r;let c;if("string"==typeof s)try{const e=JSON.parse(s);null===e||"object"!=typeof e||Array.isArray(e)||(c=e)}catch(e){}else c=s;return c&&("data"in c?(c.state=c.data,delete c.data):"action"in c&&"interval"===c.action&&(c.hereNowRefresh=null!==(t=c.here_now_refresh)&&void 0!==t&&t,delete c.here_now_refresh)),Object.assign({channel:r,subscription:i,actualChannel:a,subscribedChannel:o,timetoken:e.p.t},null!=c?c:{})}messageFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),[n,i]=this.decryptedData(e.d),r={channel:t,subscription:s,actualChannel:null!==s?t:null,subscribedChannel:null!==s?s:t,timetoken:e.p.t,publisher:e.i,message:n};return e.u&&(r.userMetadata=e.u),e.cmt&&(r.customMessageType=e.cmt),i&&(r.error=i),r}signalFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),n={channel:t,subscription:s,timetoken:e.p.t,publisher:e.i,message:e.d};return e.u&&(n.userMetadata=e.u),e.cmt&&(n.customMessageType=e.cmt),n}messageActionFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),n=e.d;return{channel:t,subscription:s,timetoken:e.p.t,publisher:e.i,event:n.event,data:Object.assign(Object.assign({},n.data),{uuid:e.i})}}appContextFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),n=e.d;return{channel:t,subscription:s,timetoken:e.p.t,message:n}}dataSyncFromEnvelope(e){var t;const[s,n]=this.subscriptionChannelFromEnvelope(e),i=e.d,r=null==i?void 0:i.metadata;if(!r||"data-sync"!==r.source||!r.event||!r.type)return;const a=r.className?r.className.split(":"):[],o=a.filter((e=>e.length>0)),c=o.length>0?o[o.length-1]:void 0,u=r.classLevel,l=void 0===u?a[0]:"Global"===u?c:void 0,h=("entity"===r.type||"relationship"===r.type)&&l&&ye[l.toLowerCase()]||r.type,d=void 0!==r.classVersion?Number.parseInt(`${r.classVersion}`,10):NaN,p=Number.isNaN(d)?void 0:d,g=null!==(t=i.data)&&void 0!==t?t:{},b={version:i.version,source:r.source,type:r.type,className:c,classLevel:u,classVersion:p};let y;if("delete"===r.event&&"membership"===h){const e={id:g.id,deletedAt:g.deletedAt};"string"==typeof g.channelId&&(e.channelId=g.channelId),"string"==typeof g.userId&&(e.userId=g.userId),y=Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:e})}else if("delete"===r.event&&"relationship"===h){const e={id:g.id,deletedAt:g.deletedAt};"string"==typeof g.entityAId&&(e.entityAId=g.entityAId),"string"==typeof g.entityBId&&(e.entityBId=g.entityBId),y=Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:e})}else y="delete"===r.event?Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:{id:g.id,deletedAt:g.deletedAt}}):Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:Object.assign({},g)});return{channel:s,subscription:n,timetoken:e.p.t,message:y}}fileFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),[n,i]=this.decryptedData(e.d);let r=i;const a={channel:t,subscription:s,timetoken:e.p.t,publisher:e.i};return e.u&&(a.userMetadata=e.u),n?"string"==typeof n?null!=r||(r="Unexpected file information payload data type."):(a.message=n.message,n.file&&(a.file={id:n.file.id,name:n.file.name,url:this.parameters.getFileUrl({id:n.file.id,name:n.file.name,channel:t})})):null!=r||(r="File information payload is missing."),e.cmt&&(a.customMessageType=e.cmt),r&&(a.error=r),a}subscriptionChannelFromEnvelope(e){return[e.c,void 0===e.b?e.c:e.b]}decryptedData(e){if(!this.parameters.crypto||"string"!=typeof e)return[e,void 0];let t,s;try{const s=this.parameters.crypto.decrypt(e);t=s instanceof ArrayBuffer?JSON.parse(fe.decoder.decode(s)):s}catch(e){t=null,s=`Error while decrypting message content: ${e.message}`}return[null!=t?t:e,s]}}class fe extends me{get path(){var e;const{keySet:{subscribeKey:t},channels:s}=this.parameters;return`/v2/subscribe/${t}/${P(null!==(e=null==s?void 0:s.sort())&&void 0!==e?e:[],",")}/0`}get queryParameters(){const{channelGroups:e,filterExpression:t,heartbeat:s,state:n,timetoken:i,region:r,onDemand:a}=this.parameters,o={};return a&&(o["on-demand"]=1),e&&e.length>0&&(o["channel-group"]=e.sort().join(",")),t&&t.length>0&&(o["filter-expr"]=t),s&&(o.heartbeat=s),n&&Object.keys(n).length>0&&(o.state=JSON.stringify(n)),void 0!==i&&"string"==typeof i?i.length>0&&"0"!==i&&(o.tt=i):void 0!==i&&i>0&&(o.tt=i),r&&(o.tr=r),o}}class ve{constructor(){this.hasListeners=!1,this.listeners=[{count:-1,listener:{}}]}set onStatus(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"status"})}set onMessage(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"message"})}set onPresence(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"presence"})}set onSignal(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"signal"})}set onObjects(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"objects"})}set onMessageAction(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"messageAction"})}set onFile(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"file"})}set onDataSync(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"dataSync"})}handleEvent(e){if(this.hasListeners)if(e.type===ge.Message)this.announce("message",e.data);else if(e.type===ge.Signal)this.announce("signal",e.data);else if(e.type===ge.Presence)this.announce("presence",e.data);else if(e.type===ge.AppContext){const{data:t}=e,{message:s}=t;if(this.announce("objects",t),"uuid"===s.type){const{message:e,channel:n}=t,r=i(t,["message","channel"]),{event:a,type:o}=s,c=i(s,["event","type"]),u=Object.assign(Object.assign({},r),{spaceId:n,message:Object.assign(Object.assign({},c),{event:"set"===a?"updated":"removed",type:"user"})});this.announce("user",u)}else if("channel"===s.type){const{message:e,channel:n}=t,r=i(t,["message","channel"]),{event:a,type:o}=s,c=i(s,["event","type"]),u=Object.assign(Object.assign({},r),{spaceId:n,message:Object.assign(Object.assign({},c),{event:"set"===a?"updated":"removed",type:"space"})});this.announce("space",u)}else if("membership"===s.type){const{message:e,channel:n}=t,r=i(t,["message","channel"]),{event:a,data:o}=s,c=i(s,["event","data"]),{uuid:u,channel:l}=o,h=i(o,["uuid","channel"]),d=Object.assign(Object.assign({},r),{spaceId:n,message:Object.assign(Object.assign({},c),{event:"set"===a?"updated":"removed",data:Object.assign(Object.assign({},h),{user:u,space:l})})});this.announce("membership",d)}}else e.type===ge.MessageAction?this.announce("messageAction",e.data):e.type===ge.Files?this.announce("file",e.data):e.type===ge.DataSync&&this.announce("dataSync",e.data)}handleStatus(e){this.hasListeners&&this.announce("status",e)}addListener(e){this.updateTypeOrObjectListener({add:!0,listener:e})}removeListener(e){this.updateTypeOrObjectListener({add:!1,listener:e})}removeAllListeners(){this.listeners=[{count:-1,listener:{}}],this.hasListeners=!1}updateTypeOrObjectListener(e){if(e.type)"function"==typeof e.listener?this.listeners[0].listener[e.type]=e.listener:delete this.listeners[0].listener[e.type];else if(e.listener&&"function"!=typeof e.listener){let t,s=!1;for(t of this.listeners)if(t.listener===e.listener){e.add?(t.count++,s=!0):(t.count--,0===t.count&&this.listeners.splice(this.listeners.indexOf(t),1));break}e.add&&!s&&this.listeners.push({count:1,listener:e.listener})}this.hasListeners=this.listeners.length>1||Object.keys(this.listeners[0]).length>0}announce(e,t){this.listeners.forEach((({listener:s})=>{const n=s[e];n&&n(t)}))}}class Oe{constructor(e){this.time=e}onReconnect(e){this.callback=e}startPolling(){this.timeTimer=setInterval((()=>this.callTime()),3e3)}stopPolling(){this.timeTimer&&clearInterval(this.timeTimer),this.timeTimer=null}callTime(){this.time((e=>{e.error||(this.stopPolling(),this.callback&&this.callback())}))}}class Se{constructor(e){this.config=e,e.logger().debug("DedupingManager",(()=>({messageType:"object",message:{maximumCacheSize:e.maximumCacheSize},details:"Create with configuration:"}))),this.maximumCacheSize=e.maximumCacheSize,this.hashHistory=[]}getKey(e){var t;return`${e.timetoken}-${this.hashCode(JSON.stringify(null!==(t=e.message)&&void 0!==t?t:"")).toString()}`}isDuplicate(e){return this.hashHistory.includes(this.getKey(e))}addEntry(e){this.hashHistory.length>=this.maximumCacheSize&&this.hashHistory.shift(),this.hashHistory.push(this.getKey(e))}clearHistory(){this.hashHistory=[]}hashCode(e){let t=0;if(0===e.length)return t;for(let s=0;s{this.pendingChannelSubscriptions.add(e),this.channels[e]={},i&&(this.presenceChannels[e]={}),(r||this.configuration.getHeartbeatInterval())&&(this.heartbeatChannels[e]={})})),null==s||s.forEach((e=>{this.pendingChannelGroupSubscriptions.add(e),this.channelGroups[e]={},i&&(this.presenceChannelGroups[e]={}),(r||this.configuration.getHeartbeatInterval())&&(this.heartbeatChannelGroups[e]={})})),this.subscriptionStatusAnnounced=!1,this.reconnect()}unsubscribe(e,t=!1){let{channels:s,channelGroups:n}=e;const r=new Set,a=new Set;if(null==s||s.forEach((e=>{e in this.channels&&(delete this.channels[e],a.add(e),e in this.heartbeatChannels&&delete this.heartbeatChannels[e]),e in this.presenceState&&delete this.presenceState[e],e in this.presenceChannels&&(delete this.presenceChannels[e],a.add(e))})),null==n||n.forEach((e=>{e in this.channelGroups&&(delete this.channelGroups[e],r.add(e),e in this.heartbeatChannelGroups&&delete this.heartbeatChannelGroups[e]),e in this.presenceState&&delete this.presenceState[e],e in this.presenceChannelGroups&&(delete this.presenceChannelGroups[e],r.add(e))})),0===a.size&&0===r.size)return;const o=this.lastTimetoken,c=this.currentTimetoken;0===Object.keys(this.channels).length&&0===Object.keys(this.presenceChannels).length&&0===Object.keys(this.channelGroups).length&&0===Object.keys(this.presenceChannelGroups).length&&(this.lastTimetoken="0",this.currentTimetoken="0",this.referenceTimetoken=null,this.storedTimetoken=null,this.region=null,this.reconnectionManager.stopPolling()),this.reconnect(!0),!1!==this.configuration.suppressLeaveEvents||t||(n=Array.from(r),s=Array.from(a),this.leaveCall({channels:s,channelGroups:n},(e=>{const{error:t}=e,r=i(e,["error"]);let a;t&&(e.errorData&&"object"==typeof e.errorData&&"message"in e.errorData&&"string"==typeof e.errorData.message?a=e.errorData.message:"message"in e&&"string"==typeof e.message&&(a=e.message)),this.emitStatus(Object.assign(Object.assign({},r),{error:null!=a&&a,affectedChannels:s,affectedChannelGroups:n,currentTimetoken:c,lastTimetoken:o}))})))}unsubscribeAll(e=!1){this.disconnectedWhileHandledEvent=!0,this.unsubscribe({channels:this.subscribedChannels,channelGroups:this.subscribedChannelGroups},e)}startSubscribeLoop(e=!1){this.disconnectedWhileHandledEvent=!1,this.stopSubscribeLoop();const t=[...Object.keys(this.channelGroups)],s=[...Object.keys(this.channels)];Object.keys(this.presenceChannelGroups).forEach((e=>t.push(`${e}-pnpres`))),Object.keys(this.presenceChannels).forEach((e=>s.push(`${e}-pnpres`))),0===s.length&&0===t.length||(this.subscribeCall(Object.assign(Object.assign(Object.assign({channels:s,channelGroups:t,state:this.presenceState,heartbeat:this.configuration.getPresenceTimeout(),timetoken:this.currentTimetoken},null!==this.region?{region:this.region}:{}),this.configuration.filterExpression?{filterExpression:this.configuration.filterExpression}:{}),{onDemand:!this.subscriptionStatusAnnounced||e}),((e,t)=>{this.processSubscribeResponse(e,t)})),!e&&this.configuration.useSmartHeartbeat&&this.startHeartbeatTimer())}stopSubscribeLoop(){this._subscribeAbort&&(this._subscribeAbort(),this._subscribeAbort=null)}processSubscribeResponse(e,t){if(e.error){if("object"==typeof e.errorData&&"name"in e.errorData&&"AbortError"===e.errorData.name||e.category===h.PNCancelledCategory)return;return void(e.category===h.PNTimeoutCategory?this.startSubscribeLoop():e.category===h.PNNetworkIssuesCategory||e.category===h.PNMalformedResponseCategory?(this.disconnect(),e.error&&this.configuration.autoNetworkDetection&&this.isOnline&&(this.isOnline=!1,this.emitStatus({category:h.PNNetworkDownCategory})),this.reconnectionManager.onReconnect((()=>{this.configuration.autoNetworkDetection&&!this.isOnline&&(this.isOnline=!0,this.emitStatus({category:h.PNNetworkUpCategory})),this.reconnect(),this.subscriptionStatusAnnounced=!0;const t={category:h.PNReconnectedCategory,operation:e.operation,lastTimetoken:this.lastTimetoken,currentTimetoken:this.currentTimetoken};this.emitStatus(t)})),this.reconnectionManager.startPolling(),this.emitStatus(Object.assign(Object.assign({},e),{category:h.PNNetworkIssuesCategory}))):e.category===h.PNBadRequestCategory?(this.stopHeartbeatTimer(),this.emitStatus(e)):this.emitStatus(e))}if(this.referenceTimetoken=I(t.cursor.timetoken,this.storedTimetoken),this.storedTimetoken?(this.currentTimetoken=this.storedTimetoken,this.storedTimetoken=null):(this.lastTimetoken=this.currentTimetoken,this.currentTimetoken=t.cursor.timetoken),!this.subscriptionStatusAnnounced){const t={category:h.PNConnectedCategory,operation:e.operation,affectedChannels:Array.from(this.pendingChannelSubscriptions),subscribedChannels:this.subscribedChannels,affectedChannelGroups:Array.from(this.pendingChannelGroupSubscriptions),lastTimetoken:this.lastTimetoken,currentTimetoken:this.currentTimetoken};this.subscriptionStatusAnnounced=!0,this.emitStatus(t),this.pendingChannelGroupSubscriptions.clear(),this.pendingChannelSubscriptions.clear()}const{messages:s}=t,{requestMessageCountThreshold:n,dedupeOnSubscribe:i}=this.configuration;n&&s.length>=n&&this.emitStatus({category:h.PNRequestMessageCountExceededCategory,operation:e.operation});try{const e={timetoken:this.currentTimetoken,region:this.region?this.region:void 0};this.configuration.logger().debug("SubscriptionManager",(()=>({messageType:"object",message:s.map((e=>({type:e.type,data:Object.assign(Object.assign({},e.data),{pn_mfp:e.pn_mfp})}))),details:"Received events:"}))),s.forEach((t=>{if(i&&"message"in t.data&&"timetoken"in t.data){if(this.dedupingManager.isDuplicate(t.data))return void this.configuration.logger().warn("SubscriptionManager",(()=>({messageType:"object",message:t.data,details:"Duplicate message detected (skipped):"})));this.dedupingManager.addEntry(t.data)}this.emitEvent(e,t)}))}catch(e){const t={error:!0,category:h.PNUnknownCategory,errorData:e,statusCode:0};this.emitStatus(t)}this.region=t.cursor.region,this.disconnectedWhileHandledEvent?this.disconnectedWhileHandledEvent=!1:this.startSubscribeLoop()}setState(e){const{state:t,channels:s,channelGroups:n}=e;null==s||s.forEach((e=>e in this.channels&&(this.presenceState[e]=t))),null==n||n.forEach((e=>e in this.channelGroups&&(this.presenceState[e]=t)))}changePresence(e){const{connected:t,channels:s,channelGroups:n}=e;t?(null==s||s.forEach((e=>this.heartbeatChannels[e]={})),null==n||n.forEach((e=>this.heartbeatChannelGroups[e]={}))):(null==s||s.forEach((e=>{e in this.heartbeatChannels&&delete this.heartbeatChannels[e]})),null==n||n.forEach((e=>{e in this.heartbeatChannelGroups&&delete this.heartbeatChannelGroups[e]})),!1===this.configuration.suppressLeaveEvents&&this.leaveCall({channels:s,channelGroups:n},(e=>this.emitStatus(e)))),this.reconnect()}startHeartbeatTimer(){this.stopHeartbeatTimer();const e=this.configuration.getHeartbeatInterval();e&&0!==e&&(this.configuration.useSmartHeartbeat||this.sendHeartbeat(),this.heartbeatTimer=setInterval((()=>this.sendHeartbeat()),1e3*e))}stopHeartbeatTimer(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}sendHeartbeat(){const e=Object.keys(this.heartbeatChannelGroups),t=Object.keys(this.heartbeatChannels);0===t.length&&0===e.length||this.heartbeatCall({channels:t,channelGroups:e,heartbeat:this.configuration.getPresenceTimeout(),state:this.presenceState},(e=>{e.error&&this.configuration.announceFailedHeartbeats&&this.emitStatus(e),e.error&&this.configuration.autoNetworkDetection&&this.isOnline&&(this.isOnline=!1,this.disconnect(),this.emitStatus({category:h.PNNetworkDownCategory}),this.reconnect()),!e.error&&this.configuration.announceSuccessfulHeartbeats&&this.emitStatus(e)}))}}class we{constructor(e,t,s){this._payload=e,this.setDefaultPayloadStructure(),this.title=t,this.body=s}get payload(){return this._payload}set title(e){this._title=e}set subtitle(e){this._subtitle=e}set body(e){this._body=e}set badge(e){this._badge=e}set sound(e){this._sound=e}setDefaultPayloadStructure(){}toObject(){return{}}}class ke extends we{constructor(){super(...arguments),this._apnsPushType="apns",this._isSilent=!1}get payload(){return this._payload}set configurations(e){e&&e.length&&(this._configurations=e)}get notification(){return this.payload.aps}get title(){return this._title}set title(e){e&&e.length&&(this.payload.aps.alert.title=e,this._title=e)}get subtitle(){return this._subtitle}set subtitle(e){e&&e.length&&(this.payload.aps.alert.subtitle=e,this._subtitle=e)}get body(){return this._body}set body(e){e&&e.length&&(this.payload.aps.alert.body=e,this._body=e)}get badge(){return this._badge}set badge(e){null!=e&&(this.payload.aps.badge=e,this._badge=e)}get sound(){return this._sound}set sound(e){e&&e.length&&(this.payload.aps.sound=e,this._sound=e)}set silent(e){this._isSilent=e}setDefaultPayloadStructure(){this.payload.aps={alert:{}}}toObject(){const e=Object.assign({},this.payload),{aps:t}=e;let{alert:s}=t;if(this._isSilent&&(t["content-available"]=1),"apns2"===this._apnsPushType){if(!this._configurations||!this._configurations.length)throw new ReferenceError("APNS2 configuration is missing");const t=[];this._configurations.forEach((e=>{t.push(this.objectFromAPNS2Configuration(e))})),t.length&&(e.pn_push=t)}return s&&Object.keys(s).length||delete t.alert,this._isSilent&&(delete t.alert,delete t.badge,delete t.sound,s={}),this._isSilent||s&&Object.keys(s).length?e:null}objectFromAPNS2Configuration(e){if(!e.targets||!e.targets.length)throw new ReferenceError("At least one APNS2 target should be provided");const{collapseId:t,expirationDate:s}=e,n={auth_method:"token",targets:e.targets.map((e=>this.objectFromAPNSTarget(e))),version:"v2"};return t&&t.length&&(n.collapse_id=t),s&&(n.expiration=s.toISOString()),n}objectFromAPNSTarget(e){if(!e.topic||!e.topic.length)throw new TypeError("Target 'topic' undefined.");const{topic:t,environment:s="development",excludedDevices:n=[]}=e,i={topic:t,environment:s};return n.length&&(i.excluded_devices=n),i}}class Ce extends we{get payload(){return this._payload}get notification(){return this.payload.notification}get data(){return this.payload.data}get androidNotification(){var e;return null===(e=this.payload.android)||void 0===e?void 0:e.notification}get title(){return this._title}set title(e){e&&e.length&&(this.payload.notification.title=e,this.androidNotification.title=e,this._title=e)}get body(){return this._body}set body(e){e&&e.length&&(this.payload.notification.body=e,this.androidNotification.body=e,this._body=e)}get badge(){return this._badge}set badge(e){null!=e&&(this.androidNotification.notification_count=e,this._badge=e)}get sound(){return this._sound}set sound(e){e&&e.length&&(this.androidNotification.sound=e,this._sound=e)}get icon(){return this._icon}set icon(e){e&&e.length&&(this.androidNotification.icon=e,this._icon=e)}get tag(){return this._tag}set tag(e){e&&e.length&&(this.androidNotification.tag=e,this._tag=e)}set silent(e){this._isSilent=e}setDefaultPayloadStructure(){this.payload.notification={},this.payload.data={},this.payload.android={notification:{}}}toObject(){var e,t;const s={},n=Object.assign({},this.payload.notification),r=Object.assign({},this.payload.android),a=i(Object.assign({},null!==(e=r.notification)&&void 0!==e?e:{}),["title","body"]);if(this._isSilent){const e={};this._title&&(e.title=this._title),this._body&&(e.body=this._body);for(const[t,s]of Object.entries(a))null!=s&&(e[t]=String(s));this.payload.data&&Object.assign(e,this.payload.data),Object.keys(e).length&&(s.data=e),delete r.notification,Object.keys(r).length&&(s.android=r)}else if(Object.keys(n).length&&(s.notification=n),this.payload.data&&Object.keys(this.payload.data).length&&(s.data=Object.assign({},this.payload.data)),Object.keys(a).length){const e=i(r,["notification"]);s.android=Object.assign(Object.assign({},e),{notification:a})}else{const e=i(r,["notification"]);Object.keys(e).length&&(s.android=e)}const o=i(this.payload,["notification","android","data","pn_exceptions"]);return Object.assign(s,o),(null===(t=this.payload.pn_exceptions)||void 0===t?void 0:t.length)&&(s.pn_exceptions=this.payload.pn_exceptions),Object.keys(s).length?s:null}}class Pe{constructor(e,t){this._payload={apns:{},fcm:{}},this._title=e,this._body=t,this.apns=new ke(this._payload.apns,e,t),this.fcm=new Ce(this._payload.fcm,e,t)}set debugging(e){this._debugging=e}get title(){return this._title}get subtitle(){return this._subtitle}set subtitle(e){this._subtitle=e,this.apns.subtitle=e,this.fcm.subtitle=e}get body(){return this._body}get badge(){return this._badge}set badge(e){this._badge=e,this.apns.badge=e,this.fcm.badge=e}get sound(){return this._sound}set sound(e){this._sound=e,this.apns.sound=e,this.fcm.sound=e}buildPayload(e){const t={};if(e.includes("apns")||e.includes("apns2")){this.apns._apnsPushType=e.includes("apns")?"apns":"apns2";const s=this.apns.toObject();s&&Object.keys(s).length&&(t.pn_apns=s)}if(e.includes("fcm")){const e=this.fcm.toObject();e&&Object.keys(e).length&&(t.pn_fcm=e)}return Object.keys(t).length&&this._debugging&&(t.pn_debug=!0),t}}class Ne{constructor(e=!1){this.sync=e,this.listeners=new Set}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}notify(e){const t=()=>{this.listeners.forEach((t=>{t(e)}))};this.sync?t():setTimeout(t,0)}}class Ee{transition(e,t){var s;if(this.transitionMap.has(t.type))return null===(s=this.transitionMap.get(t.type))||void 0===s?void 0:s(e,t)}constructor(e){this.label=e,this.transitionMap=new Map,this.enterEffects=[],this.exitEffects=[]}on(e,t){return this.transitionMap.set(e,t),this}with(e,t){return[this,e,null!=t?t:[]]}onEnter(e){return this.enterEffects.push(e),this}onExit(e){return this.exitEffects.push(e),this}}class Te extends Ne{constructor(e){super(!0),this.logger=e,this._pendingEvents=[],this._inTransition=!1}get currentState(){return this._currentState}get currentContext(){return this._currentContext}describe(e){return new Ee(e)}start(e,t){this._currentState=e,this._currentContext=t,this.notify({type:"engineStarted",state:e,context:t})}transition(e){if(!this._currentState)throw this.logger.error("Engine","Finite state machine is not started"),new Error("Start the engine first");if(this._inTransition)return this.logger.trace("Engine",(()=>({messageType:"object",message:e,details:"Event engine in transition. Enqueue received event:"}))),void this._pendingEvents.push(e);this._inTransition=!0,this.logger.trace("Engine",(()=>({messageType:"object",message:e,details:"Event engine received event:"}))),this.notify({type:"eventReceived",event:e});const t=this._currentState.transition(this._currentContext,e);if(t){const[s,n,i]=t;this.logger.trace("Engine",`Exiting state: ${this._currentState.label}`);for(const e of this._currentState.exitEffects)this.notify({type:"invocationDispatched",invocation:e(this._currentContext)});this.logger.trace("Engine",(()=>({messageType:"object",details:`Entering '${s.label}' state with context:`,message:n})));const r=this._currentState;this._currentState=s;const a=this._currentContext;this._currentContext=n,this.notify({type:"transitionDone",fromState:r,fromContext:a,toState:s,toContext:n,event:e});for(const e of i)this.notify({type:"invocationDispatched",invocation:e});for(const e of this._currentState.enterEffects)this.notify({type:"invocationDispatched",invocation:e(this._currentContext)})}else this.logger.warn("Engine",`No transition from '${this._currentState.label}' found for event: ${e.type}`);if(this._inTransition=!1,this._pendingEvents.length>0){const e=this._pendingEvents.shift();e&&(this.logger.trace("Engine",(()=>({messageType:"object",message:e,details:"De-queueing pending event:"}))),this.transition(e))}}}class _e{constructor(e,t){this.dependencies=e,this.logger=t,this.instances=new Map,this.handlers=new Map}on(e,t){this.handlers.set(e,t)}dispatch(e){if(this.logger.trace("Dispatcher",`Process invocation: ${e.type}`),"CANCEL"===e.type){if(this.instances.has(e.payload)){const t=this.instances.get(e.payload);null==t||t.cancel(),this.instances.delete(e.payload)}return}const t=this.handlers.get(e.type);if(!t)throw this.logger.error("Dispatcher",`Unhandled invocation '${e.type}'`),new Error(`Unhandled invocation '${e.type}'`);const s=t(e.payload,this.dependencies);this.logger.trace("Dispatcher",(()=>({messageType:"object",details:"Call invocation handler with parameters:",message:e.payload,ignoredKeys:["abortSignal"]}))),e.managed&&this.instances.set(e.type,s),s.start()}dispose(){for(const[e,t]of this.instances.entries())t.cancel(),this.instances.delete(e)}}function Ie(e,t){const s=function(...s){return{type:e,payload:null==t?void 0:t(...s)}};return s.type=e,s}function Me(e,t){const s=(...s)=>({type:e,payload:t(...s),managed:!1});return s.type=e,s}function Re(e,t){const s=(...s)=>({type:e,payload:t(...s),managed:!0});return s.type=e,s.cancel={type:"CANCEL",payload:e,managed:!1},s}class De extends Error{constructor(){super("The operation was aborted."),this.name="AbortError",Object.setPrototypeOf(this,new.target.prototype)}}class Ue extends Ne{constructor(){super(...arguments),this._aborted=!1}get aborted(){return this._aborted}throwIfAborted(){if(this._aborted)throw new De}abort(){this._aborted=!0,this.notify(new De)}}class Ae{constructor(e,t){this.payload=e,this.dependencies=t}}class $e extends Ae{constructor(e,t,s){super(e,t),this.asyncFunction=s,this.abortSignal=new Ue}start(){this.asyncFunction(this.payload,this.abortSignal,this.dependencies).catch((e=>{}))}cancel(){this.abortSignal.abort()}}const Fe=e=>(t,s)=>new $e(t,s,e),xe=Re("HEARTBEAT",((e,t)=>({channels:e,groups:t}))),qe=Me("LEAVE",((e,t)=>({channels:e,groups:t}))),Ge=Me("EMIT_STATUS",(e=>e)),Le=Re("WAIT",(()=>({}))),Ke=Ie("RECONNECT",(()=>({}))),He=Ie("DISCONNECT",((e=!1)=>({isOffline:e}))),Be=Ie("JOINED",((e,t)=>({channels:e,groups:t}))),We=Ie("LEFT",((e,t)=>({channels:e,groups:t}))),Ve=Ie("LEFT_ALL",((e=!1)=>({isOffline:e}))),ze=Ie("HEARTBEAT_SUCCESS",(e=>({statusCode:e}))),Je=Ie("HEARTBEAT_FAILURE",(e=>e)),Xe=Ie("TIMES_UP",(()=>({})));class Qe extends _e{constructor(e,t){super(t,t.config.logger()),this.on(xe.type,Fe(((t,s,n)=>r(this,[t,s,n],void 0,(function*(t,s,{heartbeat:n,presenceState:i,config:r}){s.throwIfAborted();try{yield n(Object.assign(Object.assign({abortSignal:s,channels:t.channels,channelGroups:t.groups},r.maintainPresenceState&&{state:i}),{heartbeat:r.presenceTimeout}));e.transition(ze(200))}catch(t){if(t instanceof d){if(t.status&&t.status.category==h.PNCancelledCategory)return;e.transition(Je(t))}}}))))),this.on(qe.type,Fe(((e,t,s)=>r(this,[e,t,s],void 0,(function*(e,t,{leave:s,config:n}){if(!n.suppressLeaveEvents)try{s({channels:e.channels,channelGroups:e.groups})}catch(e){}}))))),this.on(Le.type,Fe(((t,s,n)=>r(this,[t,s,n],void 0,(function*(t,s,{heartbeatDelay:n}){const{cancelDelay:i,promise:r}=n();s.throwIfAborted();const a=s.subscribe((()=>i()));return yield r,a(),s.throwIfAborted(),e.transition(Xe())}))))),this.on(Ge.type,Fe(((e,t,s)=>r(this,[e,t,s],void 0,(function*(e,t,{emitStatus:s,config:n}){n.announceFailedHeartbeats&&!0===(null==e?void 0:e.error)?s(Object.assign(Object.assign({},e),{operation:K.PNHeartbeatOperation})):n.announceSuccessfulHeartbeats&&200===e.statusCode&&s(Object.assign(Object.assign({},e),{error:!1,operation:K.PNHeartbeatOperation,category:h.PNAcknowledgmentCategory}))})))))}}const Ye=new Ee("HEARTBEAT_INACTIVE"),Ze=new Ee("HEARTBEATING"),et=new Ee("HEARTBEAT_COOLDOWN"),tt=new Ee("HEARTBEAT_STOPPED"),st=new Ee("HEARTBEAT_FAILED");Ye.on(Be.type,((e,t)=>Ze.with({channels:t.payload.channels,groups:t.payload.groups}))),Ze.onEnter((e=>xe(e.channels,e.groups))),Ze.onExit((()=>xe.cancel)),Ze.on(ze.type,((e,t)=>et.with({channels:e.channels,groups:e.groups},[Ge(Object.assign({},t.payload))]))),Ze.on(Be.type,((e,t)=>Ze.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),Ze.on(We.type,((e,t)=>Ze.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))},[qe(t.payload.channels,t.payload.groups)]))),Ze.on(Je.type,((e,t)=>st.with(Object.assign({},e),[...t.payload.status?[Ge(Object.assign({},t.payload.status))]:[]]))),Ze.on(He.type,((e,t)=>tt.with({channels:e.channels,groups:e.groups},[...t.payload.isOffline?[]:[qe(e.channels,e.groups)]]))),Ze.on(Ve.type,((e,t)=>Ye.with(void 0,[...t.payload.isOffline?[]:[qe(e.channels,e.groups)]]))),et.onEnter((()=>Le())),et.onExit((()=>Le.cancel)),et.on(Xe.type,((e,t)=>Ze.with({channels:e.channels,groups:e.groups}))),et.on(Be.type,((e,t)=>Ze.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),et.on(We.type,((e,t)=>Ze.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))},[qe(t.payload.channels,t.payload.groups)]))),et.on(He.type,((e,t)=>tt.with({channels:e.channels,groups:e.groups},[...t.payload.isOffline?[]:[qe(e.channels,e.groups)]]))),et.on(Ve.type,((e,t)=>Ye.with(void 0,[...t.payload.isOffline?[]:[qe(e.channels,e.groups)]]))),tt.on(Be.type,((e,t)=>tt.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),tt.on(We.type,((e,t)=>tt.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))}))),tt.on(Ke.type,((e,t)=>Ze.with({channels:e.channels,groups:e.groups}))),tt.on(Ve.type,((e,t)=>Ye.with(void 0))),st.on(Be.type,((e,t)=>Ze.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),st.on(We.type,((e,t)=>Ze.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))},[qe(t.payload.channels,t.payload.groups)]))),st.on(Ke.type,((e,t)=>Ze.with({channels:e.channels,groups:e.groups}))),st.on(He.type,((e,t)=>tt.with({channels:e.channels,groups:e.groups},[...t.payload.isOffline?[]:[qe(e.channels,e.groups)]]))),st.on(Ve.type,((e,t)=>Ye.with(void 0,[...t.payload.isOffline?[]:[qe(e.channels,e.groups)]])));class nt{get _engine(){return this.engine}constructor(e){this.dependencies=e,this.channels=[],this.groups=[],this.engine=new Te(e.config.logger()),this.dispatcher=new Qe(this.engine,e),e.config.logger().debug("PresenceEventEngine","Create presence event engine."),this._unsubscribeEngine=this.engine.subscribe((e=>{"invocationDispatched"===e.type&&this.dispatcher.dispatch(e.invocation)})),this.engine.start(Ye,void 0)}join({channels:e,groups:t}){this.channels=[...this.channels,...(null!=e?e:[]).filter((e=>!this.channels.includes(e)))],this.groups=[...this.groups,...(null!=t?t:[]).filter((e=>!this.groups.includes(e)))],0===this.channels.length&&0===this.groups.length||this.engine.transition(Be(this.channels.slice(0),this.groups.slice(0)))}leave({channels:e,groups:t}){e&&(this.channels=this.channels.filter((t=>!e.includes(t)))),t&&(this.groups=this.groups.filter((e=>!t.includes(e)))),this.dependencies.presenceState&&(null==e||e.forEach((e=>delete this.dependencies.presenceState[e])),null==t||t.forEach((e=>delete this.dependencies.presenceState[e]))),this.engine.transition(We(null!=e?e:[],null!=t?t:[]))}leaveAll(e=!1){this.dependencies.presenceState&&(this.channels.forEach((e=>delete this.dependencies.presenceState[e])),this.groups.forEach((e=>delete this.dependencies.presenceState[e]))),this.channels=[],this.groups=[],this.engine.transition(Ve(e))}reconnect(){this.engine.transition(Ke())}disconnect(e=!1){this.engine.transition(He(e))}dispose(){this.disconnect(!0),this._unsubscribeEngine(),this.dispatcher.dispose()}}const it=Ie("SUBSCRIPTION_CHANGED",((e,t,s=!1)=>({channels:e,groups:t,isOffline:s}))),rt=Ie("SUBSCRIPTION_RESTORED",((e,t,s,n)=>({channels:e,groups:t,cursor:{timetoken:s,region:null!=n?n:0}}))),at=Ie("HANDSHAKE_SUCCESS",(e=>e)),ot=Ie("HANDSHAKE_FAILURE",(e=>e)),ct=Ie("RECEIVE_SUCCESS",((e,t)=>({cursor:e,events:t}))),ut=Ie("RECEIVE_FAILURE",(e=>e)),lt=Ie("DISCONNECT",((e=!1)=>({isOffline:e}))),ht=Ie("RECONNECT",((e,t)=>({cursor:{timetoken:null!=e?e:"",region:null!=t?t:0}}))),dt=Ie("UNSUBSCRIBE_ALL",(()=>({}))),pt=new Ee("UNSUBSCRIBED"),gt=new Ee("HANDSHAKING"),bt=new Ee("HANDSHAKE_STOPPED"),yt=new Ee("HANDSHAKE_FAILED"),mt=new Ee("RECEIVING"),ft=new Ee("RECEIVE_STOPPED"),vt=new Ee("RECEIVE_FAILED");pt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,onDemand:!0}))),pt.on(rt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region},onDemand:!0})));const Ot=Re("HANDSHAKE",((e,t,s)=>({channels:e,groups:t,onDemand:s}))),St=Re("RECEIVE_MESSAGES",((e,t,s,n)=>({channels:e,groups:t,cursor:s,onDemand:n}))),jt=Me("EMIT_MESSAGES",((e,t)=>({cursor:e,events:t}))),wt=Me("EMIT_STATUS",(e=>e));gt.onEnter((e=>{var t;return Ot(e.channels,e.groups,null!==(t=e.onDemand)&&void 0!==t&&t)})),gt.onExit((()=>Ot.cancel)),gt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,onDemand:!0}))),gt.on(at.type,((e,{payload:t})=>{var s,n,i,r,a;return mt.with({channels:e.channels,groups:e.groups,cursor:{timetoken:(null===(s=e.cursor)||void 0===s?void 0:s.timetoken)?null===(n=e.cursor)||void 0===n?void 0:n.timetoken:t.timetoken,region:t.region},referenceTimetoken:I(t.timetoken,null===(i=e.cursor)||void 0===i?void 0:i.timetoken)},[wt({category:h.PNConnectedCategory,affectedChannels:e.channels.slice(0),affectedChannelGroups:e.groups.slice(0),operation:K.PNSubscribeOperation,currentTimetoken:(null===(r=e.cursor)||void 0===r?void 0:r.timetoken)?null===(a=e.cursor)||void 0===a?void 0:a.timetoken:t.timetoken})])})),gt.on(ot.type,((e,t)=>{var s;return yt.with(Object.assign(Object.assign({},e),{reason:t.payload}),[wt({category:h.PNConnectionErrorCategory,error:null===(s=t.payload.status)||void 0===s?void 0:s.category})])})),gt.on(lt.type,((e,t)=>{var s;if(t.payload.isOffline){const t=G.create(new Error("Network connection error")).toPubNubError(K.PNSubscribeOperation);return yt.with(Object.assign(Object.assign({},e),{reason:t}),[wt({category:h.PNConnectionErrorCategory,error:null===(s=t.status)||void 0===s?void 0:s.category})])}return bt.with(Object.assign({},e))})),gt.on(rt.type,((e,{payload:t})=>{var s;return 0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||(null===(s=null==e?void 0:e.cursor)||void 0===s?void 0:s.region)||0},onDemand:!0})})),gt.on(dt.type,(e=>pt.with())),bt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):bt.with({channels:t.channels,groups:t.groups,cursor:e.cursor}))),bt.on(ht.type,((e,{payload:t})=>gt.with(Object.assign(Object.assign({},e),{cursor:t.cursor||e.cursor,onDemand:!0})))),bt.on(rt.type,((e,{payload:t})=>{var s;return 0===t.channels.length&&0===t.groups.length?pt.with(void 0):bt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||(null===(s=e.cursor)||void 0===s?void 0:s.region)||0}})})),bt.on(dt.type,(e=>pt.with())),yt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,onDemand:!0}))),yt.on(ht.type,((e,{payload:t})=>gt.with(Object.assign(Object.assign({},e),{cursor:t.cursor||e.cursor,onDemand:!0})))),yt.on(rt.type,((e,{payload:t})=>{var s,n;return 0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region?t.cursor.region:null!==(n=null===(s=null==e?void 0:e.cursor)||void 0===s?void 0:s.region)&&void 0!==n?n:0},onDemand:!0})})),yt.on(dt.type,(e=>pt.with())),mt.onEnter((e=>{var t;return St(e.channels,e.groups,e.cursor,null!==(t=e.onDemand)&&void 0!==t&&t)})),mt.onExit((()=>St.cancel)),mt.on(ct.type,((e,{payload:t})=>mt.with({channels:e.channels,groups:e.groups,cursor:t.cursor,referenceTimetoken:I(t.cursor.timetoken)},[jt(e.cursor,t.events)]))),mt.on(it.type,((e,{payload:t})=>{var s;if(0===t.channels.length&&0===t.groups.length){let e;return t.isOffline&&(e=null===(s=G.create(new Error("Network connection error")).toPubNubError(K.PNSubscribeOperation).status)||void 0===s?void 0:s.category),pt.with(void 0,[wt(Object.assign({category:t.isOffline?h.PNDisconnectedUnexpectedlyCategory:h.PNDisconnectedCategory,operation:K.PNUnsubscribeOperation},e?{error:e}:{}))])}return mt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,referenceTimetoken:e.referenceTimetoken,onDemand:!0},[wt({category:h.PNSubscriptionChangedCategory,affectedChannels:t.channels.slice(0),affectedChannelGroups:t.groups.slice(0),currentTimetoken:e.cursor.timetoken})])})),mt.on(rt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0,[wt({category:h.PNDisconnectedCategory})]):mt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||e.cursor.region},referenceTimetoken:I(e.cursor.timetoken,`${t.cursor.timetoken}`,e.referenceTimetoken),onDemand:!0},[wt({category:h.PNSubscriptionChangedCategory,affectedChannels:t.channels.slice(0),affectedChannelGroups:t.groups.slice(0),currentTimetoken:t.cursor.timetoken})]))),mt.on(ut.type,((e,{payload:t})=>{var s;return vt.with(Object.assign(Object.assign({},e),{reason:t}),[wt({category:h.PNDisconnectedUnexpectedlyCategory,error:null===(s=t.status)||void 0===s?void 0:s.category})])})),mt.on(lt.type,((e,t)=>{var s;if(t.payload.isOffline){const t=G.create(new Error("Network connection error")).toPubNubError(K.PNSubscribeOperation);return vt.with(Object.assign(Object.assign({},e),{reason:t}),[wt({category:h.PNDisconnectedUnexpectedlyCategory,operation:K.PNSubscribeOperation,error:null===(s=t.status)||void 0===s?void 0:s.category})])}return ft.with(Object.assign({},e),[wt({category:h.PNDisconnectedCategory,operation:K.PNSubscribeOperation})])})),mt.on(dt.type,(e=>pt.with(void 0,[wt({category:h.PNDisconnectedCategory,operation:K.PNUnsubscribeOperation})]))),ft.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):ft.with({channels:t.channels,groups:t.groups,cursor:e.cursor}))),ft.on(rt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):ft.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||e.cursor.region}}))),ft.on(ht.type,((e,{payload:t})=>{var s;return gt.with({channels:e.channels,groups:e.groups,cursor:{timetoken:t.cursor.timetoken?null===(s=t.cursor)||void 0===s?void 0:s.timetoken:e.cursor.timetoken,region:t.cursor.region||e.cursor.region},onDemand:!0})})),ft.on(dt.type,(()=>pt.with(void 0))),vt.on(ht.type,((e,{payload:t})=>{var s;return gt.with({channels:e.channels,groups:e.groups,cursor:{timetoken:t.cursor.timetoken?null===(s=t.cursor)||void 0===s?void 0:s.timetoken:e.cursor.timetoken,region:t.cursor.region||e.cursor.region},onDemand:!0})})),vt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,onDemand:!0}))),vt.on(rt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?pt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||e.cursor.region},onDemand:!0}))),vt.on(dt.type,(e=>pt.with(void 0)));class kt extends _e{constructor(e,t){super(t,t.config.logger()),this.on(Ot.type,Fe(((t,s,n)=>r(this,[t,s,n],void 0,(function*(t,s,{handshake:n,presenceState:i,config:r}){s.throwIfAborted();try{const a=yield n(Object.assign(Object.assign({abortSignal:s,channels:t.channels,channelGroups:t.groups,filterExpression:r.filterExpression},r.maintainPresenceState&&{state:i}),{onDemand:t.onDemand}));return e.transition(at(a))}catch(t){if(t instanceof d){if(t.status&&t.status.category==h.PNCancelledCategory)return;return e.transition(ot(t))}}}))))),this.on(St.type,Fe(((t,s,n)=>r(this,[t,s,n],void 0,(function*(t,s,{receiveMessages:n,config:i}){s.throwIfAborted();try{const r=yield n({abortSignal:s,channels:t.channels,channelGroups:t.groups,timetoken:t.cursor.timetoken,region:t.cursor.region,filterExpression:i.filterExpression,onDemand:t.onDemand});e.transition(ct(r.cursor,r.messages))}catch(t){if(t instanceof d){if(t.status&&t.status.category==h.PNCancelledCategory)return;if(!s.aborted)return e.transition(ut(t))}}}))))),this.on(jt.type,Fe(((e,t,s)=>r(this,[e,t,s],void 0,(function*({cursor:e,events:t},s,{emitMessages:n}){t.length>0&&n(e,t)}))))),this.on(wt.type,Fe(((e,t,s)=>r(this,[e,t,s],void 0,(function*(e,t,{emitStatus:s}){return s(e)})))))}}class Ct{get _engine(){return this.engine}constructor(e){this.channels=[],this.groups=[],this.dependencies=e,this.engine=new Te(e.config.logger()),this.dispatcher=new kt(this.engine,e),e.config.logger().debug("EventEngine","Create subscribe event engine."),this._unsubscribeEngine=this.engine.subscribe((e=>{"invocationDispatched"===e.type&&this.dispatcher.dispatch(e.invocation)})),this.engine.start(pt,void 0)}get subscriptionTimetoken(){const e=this.engine.currentState;if(!e)return;let t,s="0";if(e.label===mt.label){const e=this.engine.currentContext;s=e.cursor.timetoken,t=e.referenceTimetoken}return _(s,null!=t?t:"0")}subscribe({channels:e,channelGroups:t,timetoken:s,withPresence:n}){var i;const r=null==e?void 0:e.some((e=>!this.channels.includes(e))),a=null==t?void 0:t.some((e=>!this.groups.includes(e))),o=r||a;if(this.channels=[...this.channels,...null!=e?e:[]],this.groups=[...this.groups,...null!=t?t:[]],n&&(this.channels.map((e=>this.channels.push(`${e}-pnpres`))),this.groups.map((e=>this.groups.push(`${e}-pnpres`)))),s)this.engine.transition(rt(Array.from(new Set([...this.channels,...null!=e?e:[]])),Array.from(new Set([...this.groups,...null!=t?t:[]])),s));else if(o)this.engine.transition(it(Array.from(new Set([...this.channels,...null!=e?e:[]])),Array.from(new Set([...this.groups,...null!=t?t:[]]))));else{this.dependencies.config.logger().debug("EventEngine","Skipping state transition - all channels/groups already subscribed. Emitting SubscriptionChanged event.");const e=this.engine.currentState,t=this.engine.currentContext;let s="0";if((null==e?void 0:e.label)===mt.label&&t){s=null===(i=t.cursor)||void 0===i?void 0:i.timetoken}this.dependencies.emitStatus({category:h.PNSubscriptionChangedCategory,affectedChannels:Array.from(new Set(this.channels)),affectedChannelGroups:Array.from(new Set(this.groups)),currentTimetoken:s})}this.dependencies.join&&this.dependencies.join({channels:Array.from(new Set(this.channels.filter((e=>!e.endsWith("-pnpres"))))),groups:Array.from(new Set(this.groups.filter((e=>!e.endsWith("-pnpres")))))})}unsubscribe({channels:e=[],channelGroups:t=[]}){const s=N(this.channels,[...e,...e.map((e=>`${e}-pnpres`))]),n=N(this.groups,[...t,...t.map((e=>`${e}-pnpres`))]);if(new Set(this.channels).size!==new Set(s).size||new Set(this.groups).size!==new Set(n).size){const i=E(this.channels,e),r=E(this.groups,t);this.dependencies.presenceState&&(null==i||i.forEach((e=>delete this.dependencies.presenceState[e])),null==r||r.forEach((e=>delete this.dependencies.presenceState[e]))),this.channels=s,this.groups=n,this.engine.transition(it(Array.from(new Set(this.channels.slice(0))),Array.from(new Set(this.groups.slice(0))))),this.dependencies.leave&&this.dependencies.leave({channels:i.slice(0),groups:r.slice(0)})}}unsubscribeAll(e=!1){const t=this.getSubscribedChannelGroups(),s=this.getSubscribedChannels();this.channels=[],this.groups=[],this.dependencies.presenceState&&Object.keys(this.dependencies.presenceState).forEach((e=>{delete this.dependencies.presenceState[e]})),this.engine.transition(it(this.channels.slice(0),this.groups.slice(0),e)),this.dependencies.leaveAll&&this.dependencies.leaveAll({channels:s,groups:t,isOffline:e})}reconnect({timetoken:e,region:t}){const s=this.getSubscribedChannels(),n=this.getSubscribedChannels();this.engine.transition(ht(e,t)),this.dependencies.presenceReconnect&&this.dependencies.presenceReconnect({channels:n,groups:s})}disconnect(e=!1){const t=this.getSubscribedChannels(),s=this.getSubscribedChannels();this.engine.transition(lt(e)),this.dependencies.presenceDisconnect&&this.dependencies.presenceDisconnect({channels:s,groups:t,isOffline:e})}getSubscribedChannels(){return Array.from(new Set(this.channels.slice(0)))}getSubscribedChannelGroups(){return Array.from(new Set(this.groups.slice(0)))}dispose(){this.disconnect(!0),this._unsubscribeEngine(),this.dispatcher.dispose()}}class Pt extends pe{constructor(e){var t;const s=null!==(t=e.sendByPost)&&void 0!==t&&t;super({method:s?ue.POST:ue.GET,compressible:s}),this.parameters=e,this.parameters.sendByPost=s}operation(){return K.PNPublishOperation}validate(){const{message:e,channel:t,keySet:{publishKey:s}}=this.parameters;return t?e?s?void 0:"Missing 'publishKey'":"Missing 'message'":"Missing 'channel'"}parse(e){return r(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[2]}}))}get path(){const{message:e,channel:t,keySet:s}=this.parameters,n=this.prepareMessagePayload(e);return`/publish/${s.publishKey}/${s.subscribeKey}/0/${C(t)}/0${this.parameters.sendByPost?"":`/${C(n)}`}`}get queryParameters(){const{customMessageType:e,meta:t,replicate:s,storeInHistory:n,ttl:i}=this.parameters,r={};return e&&(r.custom_message_type=e),void 0!==n&&(r.store=n?"1":"0"),void 0!==i&&(r.ttl=i),void 0===s||s||(r.norep="true"),t&&"object"==typeof t&&(r.meta=JSON.stringify(t)),r}get headers(){var e;return this.parameters.sendByPost?Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"}):super.headers}get body(){return this.prepareMessagePayload(this.parameters.message)}prepareMessagePayload(e){const{crypto:t}=this.parameters;if(!t)return JSON.stringify(e)||"";const s=t.encrypt(JSON.stringify(e));return JSON.stringify("string"==typeof s?s:u(s))}}class Nt extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNSignalOperation}validate(){const{message:e,channel:t,keySet:{publishKey:s}}=this.parameters;return t?e?s?void 0:"Missing 'publishKey'":"Missing 'message'":"Missing 'channel'"}parse(e){return r(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[2]}}))}get path(){const{keySet:{publishKey:e,subscribeKey:t},channel:s,message:n}=this.parameters,i=JSON.stringify(n);return`/signal/${e}/${t}/0/${C(s)}/0/${C(i)}`}get queryParameters(){const{customMessageType:e}=this.parameters,t={};return e&&(t.custom_message_type=e),t}}class Et extends me{operation(){return K.PNReceiveMessagesOperation}validate(){const e=super.validate();return e||(this.parameters.timetoken?this.parameters.region?void 0:"region can not be empty":"timetoken can not be empty")}get path(){const{keySet:{subscribeKey:e},channels:t=[]}=this.parameters;return`/v2/subscribe/${e}/${P(t.sort(),",")}/0`}get queryParameters(){const{channelGroups:e,filterExpression:t,timetoken:s,region:n,heartbeat:i,onDemand:r}=this.parameters,a={ee:""};return r&&(a["on-demand"]=1),e&&e.length>0&&(a["channel-group"]=e.sort().join(",")),t&&t.length>0&&(a["filter-expr"]=t),"string"==typeof s?s&&"0"!==s&&s.length>0&&(a.tt=s):s&&s>0&&(a.tt=s),n&&(a.tr=n),i&&(a.heartbeat=i),a}}class Tt extends me{operation(){return K.PNHandshakeOperation}get path(){const{keySet:{subscribeKey:e},channels:t=[]}=this.parameters;return`/v2/subscribe/${e}/${P(t.sort(),",")}/0`}get queryParameters(){const{channelGroups:e,heartbeat:t,filterExpression:s,state:n,onDemand:i}=this.parameters,r={ee:""};return i&&(r["on-demand"]=1),e&&e.length>0&&(r["channel-group"]=e.sort().join(",")),s&&s.length>0&&(r["filter-expr"]=s),n&&Object.keys(n).length>0&&(r.state=JSON.stringify(n)),t&&(r.heartbeat=t),r}}var _t;!function(e){e[e.Channel=0]="Channel",e[e.ChannelGroup=1]="ChannelGroup"}(_t||(_t={}));class It{constructor({channels:e,channelGroups:t}){this.isEmpty=!0,this._channelGroups=new Set((null!=t?t:[]).filter((e=>e.length>0))),this._channels=new Set((null!=e?e:[]).filter((e=>e.length>0))),this.isEmpty=0===this._channels.size&&0===this._channelGroups.size}get length(){return this.isEmpty?0:this._channels.size+this._channelGroups.size}get channels(){return this.isEmpty?[]:Array.from(this._channels)}get channelGroups(){return this.isEmpty?[]:Array.from(this._channelGroups)}contains(e){return!this.isEmpty&&(this._channels.has(e)||this._channelGroups.has(e))}with(e){return new It({channels:[...this._channels,...e._channels],channelGroups:[...this._channelGroups,...e._channelGroups]})}without(e){return new It({channels:[...this._channels].filter((t=>!e._channels.has(t))),channelGroups:[...this._channelGroups].filter((t=>!e._channelGroups.has(t)))})}add(e){return e._channelGroups.size>0&&(this._channelGroups=new Set([...this._channelGroups,...e._channelGroups])),e._channels.size>0&&(this._channels=new Set([...this._channels,...e._channels])),this.isEmpty=0===this._channels.size&&0===this._channelGroups.size,this}remove(e){return e._channelGroups.size>0&&(this._channelGroups=new Set([...this._channelGroups].filter((t=>!e._channelGroups.has(t))))),e._channels.size>0&&(this._channels=new Set([...this._channels].filter((t=>!e._channels.has(t))))),this}removeAll(){return this._channels.clear(),this._channelGroups.clear(),this.isEmpty=!0,this}toString(){return`SubscriptionInput { channels: [${this.channels.join(", ")}], channelGroups: [${this.channelGroups.join(", ")}], is empty: ${this.isEmpty?"true":"false"}} }`}}class Mt{constructor(e,t,s,n){this._isSubscribed=!1,this.clones={},this.parents=[],this._id=ie.createUUID(),this.referenceTimetoken=n,this.subscriptionInput=t,this.options=s,this.client=e}get id(){return this._id}get isLastClone(){return 1===Object.keys(this.clones).length}get isSubscribed(){return!!this._isSubscribed||this.parents.length>0&&this.parents.some((e=>e.isSubscribed))}set isSubscribed(e){this.isSubscribed!==e&&(this._isSubscribed=e)}addParentState(e){this.parents.includes(e)||this.parents.push(e)}removeParentState(e){const t=this.parents.indexOf(e);-1!==t&&this.parents.splice(t,1)}storeClone(e,t){this.clones[e]||(this.clones[e]=t)}}class Rt{constructor(e,t="Subscription"){this.subscriptionType=t,this.id=ie.createUUID(),this.eventDispatcher=new ve,this._state=e}get state(){return this._state}get channels(){return this.state.subscriptionInput.channels.slice(0)}get channelGroups(){return this.state.subscriptionInput.channelGroups.slice(0)}set onMessage(e){this.eventDispatcher.onMessage=e}set onPresence(e){this.eventDispatcher.onPresence=e}set onSignal(e){this.eventDispatcher.onSignal=e}set onObjects(e){this.eventDispatcher.onObjects=e}set onMessageAction(e){this.eventDispatcher.onMessageAction=e}set onFile(e){this.eventDispatcher.onFile=e}set onDataSync(e){this.eventDispatcher.onDataSync=e}addListener(e){this.eventDispatcher.addListener(e)}removeListener(e){this.eventDispatcher.removeListener(e)}removeAllListeners(){this.eventDispatcher.removeAllListeners()}handleEvent(e,t){var s;if((!this.state.cursor||e>this.state.cursor)&&(this.state.cursor=e),this.state.referenceTimetoken&&t.data.timetoken({messageType:"text",message:`Event timetoken (${t.data.timetoken}) is older than reference timetoken (${this.state.referenceTimetoken}) for ${this.id} subscription object. Ignoring event.`})));if((null===(s=this.state.options)||void 0===s?void 0:s.filter)&&!this.state.options.filter(t))return void this.state.client.logger.trace(this.subscriptionType,`Event filtered out by filter function for ${this.id} subscription object. Ignoring event.`);const n=Object.values(this.state.clones);n.length>0&&this.state.client.logger.trace(this.subscriptionType,`Notify ${this.id} subscription object clones (count: ${n.length}) about received event.`),n.forEach((e=>e.eventDispatcher.handleEvent(t)))}dispose(){const e=Object.keys(this.state.clones);e.length>1?(this.state.client.logger.debug(this.subscriptionType,`Remove subscription object clone on dispose: ${this.id}`),delete this.state.clones[this.id]):1===e.length&&this.state.clones[this.id]&&(this.state.client.logger.debug(this.subscriptionType,`Unsubscribe subscription object on dispose: ${this.id}`),this.unsubscribe())}invalidate(e=!1){this.state._isSubscribed=!1,e&&(delete this.state.clones[this.id],0===Object.keys(this.state.clones).length&&(this.state.client.logger.trace(this.subscriptionType,"Last clone removed. Reset shared subscription state."),this.state.subscriptionInput.removeAll(),this.state.parents=[]))}subscribe(e){this.state.isSubscribed?this.state.client.logger.trace(this.subscriptionType,"Already subscribed. Ignoring subscribe request."):(this.state.client.logger.debug(this.subscriptionType,(()=>e?{messageType:"object",message:e,details:"Subscribe with parameters:"}:{messageType:"text",message:"Subscribe"})),this.state.isSubscribed=!0,this.updateSubscription({subscribing:!0,timetoken:null==e?void 0:e.timetoken}))}unsubscribe(){if(!this.state._isSubscribed||this.state.isSubscribed){if(!this.state._isSubscribed&&this.state.parents.length>0&&this.state.isSubscribed)return void this.state.client.logger.warn(this.subscriptionType,(()=>({messageType:"object",details:"Subscription is subscribed as part of a subscription set. Remove from active sets to unsubscribe:",message:this.state.parents.filter((e=>e.isSubscribed))})));if(!this.state._isSubscribed)return void this.state.client.logger.trace(this.subscriptionType,"Not subscribed. Ignoring unsubscribe request.")}this.state.client.logger.debug(this.subscriptionType,"Unsubscribe"),this.state.isSubscribed=!1,delete this.state.cursor,this.updateSubscription({subscribing:!1})}updateSubscription(e){var t,s;(null==e?void 0:e.timetoken)&&((null===(t=this.state.cursor)||void 0===t?void 0:t.timetoken)&&"0"!==(null===(s=this.state.cursor)||void 0===s?void 0:s.timetoken)?"0"!==e.timetoken&&e.timetoken>this.state.cursor.timetoken&&(this.state.cursor.timetoken=e.timetoken):this.state.cursor={timetoken:e.timetoken});const n=e.subscriptions&&e.subscriptions.length>0?e.subscriptions:void 0;e.subscribing?this.register(Object.assign(Object.assign({},e.timetoken?{cursor:this.state.cursor}:{}),n?{subscriptions:n}:{})):this.unregister(n)}}class Dt extends Mt{constructor(e){const t=new It({});e.subscriptions.forEach((e=>t.add(e.state.subscriptionInput))),super(e.client,t,e.options,e.client.subscriptionTimetoken),this.subscriptions=e.subscriptions}addSubscription(e){this.subscriptions.includes(e)||(e.state.addParentState(this),this.subscriptions.push(e),this.subscriptionInput.add(e.state.subscriptionInput))}removeSubscription(e,t){const s=this.subscriptions.indexOf(e);-1!==s&&(this.subscriptions.splice(s,1),t||e.state.removeParentState(this),this.subscriptionInput.remove(e.state.subscriptionInput))}removeAllSubscriptions(){this.subscriptions.forEach((e=>e.state.removeParentState(this))),this.subscriptions.splice(0,this.subscriptions.length),this.subscriptionInput.removeAll()}}class Ut extends Rt{constructor(e){let t;if("client"in e){let s=[];!e.subscriptions&&e.entities?e.entities.forEach((t=>s.push(t.subscription(e.options)))):e.subscriptions&&(s=e.subscriptions),t=new Dt({client:e.client,subscriptions:s,options:e.options}),s.forEach((e=>e.state.addParentState(t))),t.client.logger.debug("SubscriptionSet",(()=>({messageType:"object",details:"Create subscription set with parameters:",message:Object.assign({subscriptions:t.subscriptions},e.options?e.options:{})})))}else t=e.state,t.client.logger.debug("SubscriptionSet","Create subscription set clone");super(t,"SubscriptionSet"),this.state.storeClone(this.id,this),t.subscriptions.forEach((e=>e.addParentSet(this)))}get state(){return super.state}get subscriptions(){return this.state.subscriptions.slice(0)}handleEvent(e,t){var s;this.state.subscriptionInput.contains(null!==(s=t.data.subscription)&&void 0!==s?s:t.data.channel)&&(this.state._isSubscribed?(super.handleEvent(e,t),this.state.subscriptions.length>0&&this.state.client.logger.trace(this.subscriptionType,`Notify ${this.id} subscription set subscriptions (count: ${this.state.subscriptions.length}) about received event.`),this.state.subscriptions.forEach((s=>s.handleEvent(e,t)))):this.state.client.logger.trace(this.subscriptionType,`Subscription set ${this.id} is not subscribed. Ignoring event.`))}subscriptionInput(e=!1){let t=this.state.subscriptionInput;return this.state.subscriptions.forEach((s=>{e&&s.state.entity.subscriptionsCount>0&&(t=t.without(s.state.subscriptionInput))})),t}cloneEmpty(){return new Ut({state:this.state})}dispose(){const e=this.state.isLastClone;this.state.subscriptions.forEach((t=>{t.removeParentSet(this),e&&t.state.removeParentState(this.state)})),super.dispose()}invalidate(e=!1){(e?this.state.subscriptions.slice(0):this.state.subscriptions).forEach((t=>{e&&(t.state.entity.decreaseSubscriptionCount(this.state.id),t.removeParentSet(this)),t.invalidate(e)})),e&&this.state.removeAllSubscriptions(),super.invalidate()}addSubscription(e){this.addSubscriptions([e])}addSubscriptions(e){const t=[],s=[];this.state.client.logger.debug(this.subscriptionType,(()=>{const t=[],s=[];return e.forEach((e=>{this.state.subscriptions.includes(e)?t.push(e):s.push(e)})),{messageType:"object",details:`Add subscriptions to ${this.id} (subscriptions count: ${this.state.subscriptions.length+s.length}):`,message:{addedSubscriptions:s,ignoredSubscriptions:t}}})),e.filter((e=>!this.state.subscriptions.includes(e))).forEach((e=>{e.state.isSubscribed?s.push(e):t.push(e),e.addParentSet(this),this.state.addSubscription(e)})),0===s.length&&0===t.length||!this.state.isSubscribed||(s.forEach((({state:e})=>e.entity.increaseSubscriptionCount(this.state.id))),t.length>0&&this.updateSubscription({subscribing:!0,subscriptions:t}))}removeSubscription(e){this.removeSubscriptions([e])}removeSubscriptions(e){const t=[];this.state.client.logger.debug(this.subscriptionType,(()=>{const t=[],s=[];return e.forEach((e=>{this.state.subscriptions.includes(e)?s.push(e):t.push(e)})),{messageType:"object",details:`Remove subscriptions from ${this.id} (subscriptions count: ${this.state.subscriptions.length}):`,message:{removedSubscriptions:s,ignoredSubscriptions:t}}})),e.filter((e=>this.state.subscriptions.includes(e))).forEach((e=>{e.state.isSubscribed&&t.push(e),e.removeParentSet(this),this.state.removeSubscription(e,e.parentSetsCount>1)})),0!==t.length&&this.state.isSubscribed&&this.updateSubscription({subscribing:!1,subscriptions:t})}addSubscriptionSet(e){this.addSubscriptions(e.subscriptions)}removeSubscriptionSet(e){this.removeSubscriptions(e.subscriptions)}register(e){var t;const s=null!==(t=e.subscriptions)&&void 0!==t?t:this.state.subscriptions;s.forEach((({state:e})=>e.entity.increaseSubscriptionCount(this.state.id))),this.state.client.logger.trace(this.subscriptionType,(()=>({messageType:"text",message:`Register subscription for real-time events: ${this}`}))),this.state.client.registerEventHandleCapable(this,e.cursor,s)}unregister(e){const t=null!=e?e:this.state.subscriptions;t.forEach((({state:e})=>e.entity.decreaseSubscriptionCount(this.state.id))),this.state.client.logger.trace(this.subscriptionType,(()=>e?{messageType:"object",message:{subscription:this,subscriptions:e},details:"Unregister subscriptions of subscription set from real-time events:"}:{messageType:"text",message:`Unregister subscription from real-time events: ${this}`})),this.state.client.unregisterEventHandleCapable(this,t)}toString(){const e=this.state;return`${this.subscriptionType} { id: ${this.id}, stateId: ${e.id}, clonesCount: ${Object.keys(this.state.clones).length}, isSubscribed: ${e.isSubscribed}, subscriptions: [${e.subscriptions.map((e=>e.toString())).join(", ")}] }`}}class At extends Mt{constructor(e){var t,s;const n=e.entity.subscriptionNames(null!==(s=null===(t=e.options)||void 0===t?void 0:t.receivePresenceEvents)&&void 0!==s&&s),i=new It({[e.entity.subscriptionType==_t.Channel?"channels":"channelGroups"]:n});super(e.client,i,e.options,e.client.subscriptionTimetoken),this.entity=e.entity}}class $t extends Rt{constructor(e){"client"in e?e.client.logger.debug("Subscription",(()=>({messageType:"object",details:"Create subscription with parameters:",message:Object.assign({entity:e.entity},e.options?e.options:{})}))):e.state.client.logger.debug("Subscription","Create subscription clone"),super("state"in e?e.state:new At(e)),this.parents=[],this.handledUpdates=[],this.state.storeClone(this.id,this)}get state(){return super.state}get parentSetsCount(){return this.parents.length}handleEvent(e,t){var s,n;if(this.state.isSubscribed&&this.state.subscriptionInput.contains(null!==(s=t.data.subscription)&&void 0!==s?s:t.data.channel)){if(this.parentSetsCount>0){const e=R(t.data);if(this.handledUpdates.includes(e))return void this.state.client.logger.trace(this.subscriptionType,`Event (${e}) already handled by ${this.id}. Ignoring.`);this.handledUpdates.push(e),this.handledUpdates.length>10&&this.handledUpdates.shift()}this.state.subscriptionInput.contains(null!==(n=t.data.subscription)&&void 0!==n?n:t.data.channel)&&super.handleEvent(e,t)}}subscriptionInput(e=!1){return e&&this.state.entity.subscriptionsCount>0?new It({}):this.state.subscriptionInput}cloneEmpty(){return new $t({state:this.state})}dispose(){this.parentSetsCount>0?this.state.client.logger.debug(this.subscriptionType,(()=>({messageType:"text",message:`'${this.state.entity.subscriptionNames()}' subscription still in use. Ignore dispose request.`}))):(this.handledUpdates.splice(0,this.handledUpdates.length),super.dispose())}invalidate(e=!1){e&&this.state.entity.decreaseSubscriptionCount(this.state.id),this.handledUpdates.splice(0,this.handledUpdates.length),super.invalidate(e)}addParentSet(e){this.parents.includes(e)||(this.parents.push(e),this.state.client.logger.trace(this.subscriptionType,`Add parent subscription set for ${this.id}: ${e.id}. Parent subscription set count: ${this.parentSetsCount}`))}removeParentSet(e){const t=this.parents.indexOf(e);-1!==t&&(this.parents.splice(t,1),this.state.client.logger.trace(this.subscriptionType,`Remove parent subscription set from ${this.id}: ${e.id}. Parent subscription set count: ${this.parentSetsCount}`)),0===this.parentSetsCount&&this.handledUpdates.splice(0,this.handledUpdates.length)}addSubscription(e){this.state.client.logger.debug(this.subscriptionType,(()=>({messageType:"text",message:`Create set with subscription: ${e}`})));const t=new Ut({client:this.state.client,subscriptions:[this,e],options:this.state.options});return this.state.isSubscribed||e.state.isSubscribed?(this.state.client.logger.trace(this.subscriptionType,"Subscribe resulting set because the receiver is already subscribed."),t.subscribe(),t):t}register(e){this.state.entity.increaseSubscriptionCount(this.state.id),this.state.client.logger.trace(this.subscriptionType,(()=>({messageType:"text",message:`Register subscription for real-time events: ${this}`}))),this.state.client.registerEventHandleCapable(this,e.cursor)}unregister(e){this.state.entity.decreaseSubscriptionCount(this.state.id),this.state.client.logger.trace(this.subscriptionType,(()=>({messageType:"text",message:`Unregister subscription from real-time events: ${this}`}))),this.handledUpdates.splice(0,this.handledUpdates.length),this.state.client.unregisterEventHandleCapable(this)}toString(){const e=this.state;return`${this.subscriptionType} { id: ${this.id}, stateId: ${e.id}, entity: ${e.entity.subscriptionNames(!1).pop()}, clonesCount: ${Object.keys(e.clones).length}, isSubscribed: ${e.isSubscribed}, parentSetsCount: ${this.parentSetsCount}, cursor: ${e.cursor?e.cursor.timetoken:"not set"}, referenceTimetoken: ${e.referenceTimetoken?e.referenceTimetoken:"not set"} }`}}class Ft extends pe{constructor(e){var t,s,n,i;super(),this.parameters=e,null!==(t=(n=this.parameters).channels)&&void 0!==t||(n.channels=[]),null!==(s=(i=this.parameters).channelGroups)&&void 0!==s||(i.channelGroups=[])}operation(){return K.PNGetStateOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroups:s}=this.parameters;if(!e)return"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e),{channels:s=[],channelGroups:n=[]}=this.parameters,i={channels:{}};return 1===s.length&&0===n.length?i.channels[s[0]]=t.payload:i.channels=t.payload,i}))}get path(){const{keySet:{subscribeKey:e},uuid:t,channels:s}=this.parameters;return`/v2/presence/sub-key/${e}/channel/${P(null!=s?s:[],",")}/uuid/${C(null!=t?t:"")}`}get queryParameters(){const{channelGroups:e}=this.parameters;return e&&0!==e.length?{"channel-group":e.join(",")}:{}}}class xt extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNSetStateOperation}validate(){const{keySet:{subscribeKey:e},state:t,channels:s=[],channelGroups:n=[]}=this.parameters;return e?void 0===t?"Missing State":0===(null==s?void 0:s.length)&&0===(null==n?void 0:n.length)?"Please provide a list of channels and/or channel-groups":void 0:"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){return{state:this.deserializeResponse(e).payload}}))}get path(){const{keySet:{subscribeKey:e},uuid:t,channels:s}=this.parameters;return`/v2/presence/sub-key/${e}/channel/${P(null!=s?s:[],",")}/uuid/${C(t)}/data`}get queryParameters(){const{channelGroups:e,state:t}=this.parameters,s={state:JSON.stringify(t)};return e&&0!==e.length&&(s["channel-group"]=e.join(",")),s}}class qt extends pe{constructor(e){super({cancellable:!0}),this.parameters=e}operation(){return K.PNHeartbeatOperation}validate(){const{keySet:{subscribeKey:e},channels:t=[],channelGroups:s=[]}=this.parameters;return e?0===t.length&&0===s.length?"Please provide a list of channels and/or channel-groups":void 0:"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channels:t}=this.parameters;return`/v2/presence/sub-key/${e}/channel/${P(null!=t?t:[],",")}/heartbeat`}get queryParameters(){const{channelGroups:e,state:t,heartbeat:s}=this.parameters,n={heartbeat:`${s}`};return e&&0!==e.length&&(n["channel-group"]=e.join(",")),void 0!==t&&(n.state=JSON.stringify(t)),n}}class Gt extends pe{constructor(e){super(),this.parameters=e,this.parameters.channelGroups&&(this.parameters.channelGroups=Array.from(new Set(this.parameters.channelGroups))),this.parameters.channels&&(this.parameters.channels=Array.from(new Set(this.parameters.channels)))}operation(){return K.PNUnsubscribeOperation}validate(){const{keySet:{subscribeKey:e},channels:t=[],channelGroups:s=[]}=this.parameters;return e?0===t.length&&0===s.length?"At least one `channel` or `channel group` should be provided.":void 0:"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){var e;const{keySet:{subscribeKey:t},channels:s}=this.parameters;return`/v2/presence/sub-key/${t}/channel/${P(null!==(e=null==s?void 0:s.sort())&&void 0!==e?e:[],",")}/leave`}get queryParameters(){const{channelGroups:e}=this.parameters;return e&&0!==e.length?{"channel-group":e.sort().join(",")}:{}}}class Lt extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNWhereNowOperation}validate(){if(!this.parameters.keySet.subscribeKey)return"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return t.payload?{channels:t.payload.channels}:{channels:[]}}))}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/presence/sub-key/${e}/uuid/${C(t)}`}}class Kt extends pe{constructor(e){var t,s,n,i,r,a,o,c;super(),this.parameters=e,null!==(t=(r=this.parameters).queryParameters)&&void 0!==t||(r.queryParameters={}),null!==(s=(a=this.parameters).includeUUIDs)&&void 0!==s||(a.includeUUIDs=true),null!==(n=(o=this.parameters).includeState)&&void 0!==n||(o.includeState=false),null!==(i=(c=this.parameters).limit)&&void 0!==i||(c.limit=1e3)}operation(){const{channels:e=[],channelGroups:t=[]}=this.parameters;return 0===e.length&&0===t.length?K.PNGlobalHereNowOperation:K.PNHereNowOperation}validate(){if(!this.parameters.keySet.subscribeKey)return"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){var t,s;const n=this.deserializeResponse(e),i="occupancy"in n?1:n.payload.total_channels,r="occupancy"in n?n.occupancy:n.payload.total_occupancy,a={};let o={};const c=this.parameters.limit;let u=!1;if("occupancy"in n){const e=this.parameters.channels[0];o[e]={uuids:null!==(t=n.uuids)&&void 0!==t?t:[],occupancy:r}}else o=null!==(s=n.payload.channels)&&void 0!==s?s:{};return Object.keys(o).forEach((e=>{const t=o[e];a[e]={occupants:this.parameters.includeUUIDs?t.uuids.map((e=>"string"==typeof e?{uuid:e,state:null}:e)):[],name:e,occupancy:t.occupancy},u||t.occupancy!==c||(u=!0)})),{totalChannels:i,totalOccupancy:r,channels:a}}))}get path(){const{keySet:{subscribeKey:e},channels:t,channelGroups:s}=this.parameters;let n=`/v2/presence/sub-key/${e}`;return(t&&t.length>0||s&&s.length>0)&&(n+=`/channel/${P(null!=t?t:[],",")}`),n}get queryParameters(){const{channelGroups:e,includeUUIDs:t,includeState:s,limit:n,offset:i,queryParameters:r}=this.parameters;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},this.operation()===K.PNHereNowOperation?{limit:n}:{}),this.operation()===K.PNHereNowOperation&&null!=i&&i?{offset:i}:{}),t?{}:{disable_uuids:"1"}),null!=s&&s?{state:"1"}:{}),e&&e.length>0?{"channel-group":e.join(",")}:{}),r)}}class Ht extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNDeleteMessagesOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channel?void 0:"Missing channel":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v3/history/sub-key/${e}/channel/${C(t)}`}get queryParameters(){const{start:e,end:t}=this.parameters;return Object.assign(Object.assign({},e?{start:e}:{}),t?{end:t}:{})}}class Bt extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNMessageCounts}validate(){const{keySet:{subscribeKey:e},channels:t,timetoken:s,channelTimetokens:n}=this.parameters;return e?t?s&&n?"`timetoken` and `channelTimetokens` are incompatible together":s||n?n&&n.length>1&&n.length!==t.length?"Length of `channelTimetokens` and `channels` do not match":void 0:"`timetoken` or `channelTimetokens` need to be set":"Missing channels":"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){return{channels:this.deserializeResponse(e).channels}}))}get path(){return`/v3/history/sub-key/${this.parameters.keySet.subscribeKey}/message-counts/${P(this.parameters.channels)}`}get queryParameters(){let{channelTimetokens:e}=this.parameters;return this.parameters.timetoken&&(e=[this.parameters.timetoken]),Object.assign(Object.assign({},1===e.length?{timetoken:e[0]}:{}),e.length>1?{channelsTimetoken:e.join(",")}:{})}}class Wt extends pe{constructor(e){var t,s,n;super(),this.parameters=e,e.count?e.count=Math.min(e.count,100):e.count=100,null!==(t=e.stringifiedTimeToken)&&void 0!==t||(e.stringifiedTimeToken=false),null!==(s=e.includeMeta)&&void 0!==s||(e.includeMeta=false),null!==(n=e.logVerbosity)&&void 0!==n||(e.logVerbosity=false)}operation(){return K.PNHistoryOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channel?void 0:"Missing channel":"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e),s=t[0],n=t[1],i=t[2];return Array.isArray(s)?{messages:s.map((e=>{const t=this.processPayload(e.message),s={entry:t.payload,timetoken:e.timetoken};return t.error&&(s.error=t.error),e.meta&&(s.meta=e.meta),s})),startTimeToken:n,endTimeToken:i}:{messages:[],startTimeToken:n,endTimeToken:i}}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/history/sub-key/${e}/channel/${C(t)}`}get queryParameters(){const{start:e,end:t,reverse:s,count:n,stringifiedTimeToken:i,includeMeta:r}=this.parameters;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:n,include_token:"true"},e?{start:e}:{}),t?{end:t}:{}),i?{string_message_token:"true"}:{}),null!=s?{reverse:s.toString()}:{}),r?{include_meta:"true"}:{})}processPayload(e){const{crypto:t,logVerbosity:s}=this.parameters;if(!t||"string"!=typeof e)return{payload:e};let n,i;try{const s=t.decrypt(e);n=s instanceof ArrayBuffer?JSON.parse(Wt.decoder.decode(s)):s}catch(t){s&&console.log("decryption error",t.message),n=e,i=`Error while decrypting message content: ${t.message}`}return{payload:n,error:i}}}var Vt;!function(e){e[e.Message=-1]="Message",e[e.Files=4]="Files"}(Vt||(Vt={}));class zt extends pe{constructor(e){var t,s,n,i,r;super(),this.parameters=e;const a=null!==(t=e.includeMessageActions)&&void 0!==t&&t,o=e.channels.length>1||a?25:100;e.count?e.count=Math.min(e.count,o):e.count=o,e.includeUuid?e.includeUUID=e.includeUuid:null!==(s=e.includeUUID)&&void 0!==s||(e.includeUUID=true),null!==(n=e.stringifiedTimeToken)&&void 0!==n||(e.stringifiedTimeToken=false),null!==(i=e.includeMessageType)&&void 0!==i||(e.includeMessageType=true),null!==(r=e.logVerbosity)&&void 0!==r||(e.logVerbosity=false)}operation(){return K.PNFetchMessagesOperation}validate(){const{keySet:{subscribeKey:e},channels:t,includeMessageActions:s}=this.parameters;return e?t?void 0!==s&&s&&t.length>1?"History can return actions data for a single channel only. Either pass a single channel or disable the includeMessageActions flag.":void 0:"Missing channels":"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){var t;const s=this.deserializeResponse(e),n=null!==(t=s.channels)&&void 0!==t?t:{},i={};return Object.keys(n).forEach((e=>{i[e]=n[e].map((t=>{null===t.message_type&&(t.message_type=Vt.Message);const s=this.processPayload(e,t),n=Object.assign(Object.assign({channel:e,timetoken:t.timetoken,message:s.payload,messageType:t.message_type},t.custom_message_type?{customMessageType:t.custom_message_type}:{}),{uuid:t.uuid});if(t.actions){const e=n;e.actions=t.actions,e.data=t.actions}return t.meta&&(n.meta=t.meta),s.error&&(n.error=s.error),n}))})),s.more?{channels:i,more:s.more}:{channels:i}}))}get path(){const{keySet:{subscribeKey:e},channels:t,includeMessageActions:s}=this.parameters;return`/v3/${s?"history-with-actions":"history"}/sub-key/${e}/channel/${P(t)}`}get queryParameters(){const{start:e,end:t,count:s,includeCustomMessageType:n,includeMessageType:i,includeMeta:r,includeUUID:a,stringifiedTimeToken:o}=this.parameters;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({max:s},e?{start:e}:{}),t?{end:t}:{}),o?{string_message_token:"true"}:{}),void 0!==r&&r?{include_meta:"true"}:{}),a?{include_uuid:"true"}:{}),null!=n?{include_custom_message_type:n?"true":"false"}:{}),i?{include_message_type:"true"}:{})}processPayload(e,t){const{crypto:s,logVerbosity:n}=this.parameters;if(!s||"string"!=typeof t.message)return{payload:t.message};let i,r;try{const e=s.decrypt(t.message);i=e instanceof ArrayBuffer?JSON.parse(zt.decoder.decode(e)):e}catch(e){n&&console.log("decryption error",e.message),i=t.message,r=`Error while decrypting message content: ${e.message}`}if(!r&&i&&t.message_type==Vt.Files&&"object"==typeof i&&this.isFileMessage(i)){const t=i;return{payload:{message:t.message,file:Object.assign(Object.assign({},t.file),{url:this.parameters.getFileUrl({channel:e,id:t.file.id,name:t.file.name})})},error:r}}return{payload:i,error:r}}isFileMessage(e){return void 0!==e.file}}class Jt extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNGetMessageActionsOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channel?void 0:"Missing message channel":"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);let s=null,n=null;return t.data.length>0&&(s=t.data[0].actionTimetoken,n=t.data[t.data.length-1].actionTimetoken),{data:t.data,more:t.more,start:s,end:n}}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v1/message-actions/${e}/channel/${C(t)}`}get queryParameters(){const{limit:e,start:t,end:s}=this.parameters;return Object.assign(Object.assign(Object.assign({},t?{start:t}:{}),s?{end:s}:{}),e?{limit:e}:{})}}class Xt extends pe{constructor(e){super({method:ue.POST}),this.parameters=e}operation(){return K.PNAddMessageActionOperation}validate(){const{keySet:{subscribeKey:e},action:t,channel:s,messageTimetoken:n}=this.parameters;return e?s?n?t?t.value?t.type?t.type.length>15?"Action.type value exceed maximum length of 15":void 0:"Missing Action.type":"Missing Action.value":"Missing Action":"Missing message timetoken":"Missing message channel":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((({data:e})=>({data:e})))}))}get path(){const{keySet:{subscribeKey:e},channel:t,messageTimetoken:s}=this.parameters;return`/v1/message-actions/${e}/channel/${C(t)}/message/${s}`}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){return JSON.stringify(this.parameters.action)}}class Qt extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNRemoveMessageActionOperation}validate(){const{keySet:{subscribeKey:e},channel:t,messageTimetoken:s,actionTimetoken:n}=this.parameters;return e?t?s?n?void 0:"Missing action timetoken":"Missing message timetoken":"Missing message action channel":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((({data:e})=>({data:e})))}))}get path(){const{keySet:{subscribeKey:e},channel:t,actionTimetoken:s,messageTimetoken:n}=this.parameters;return`/v1/message-actions/${e}/channel/${C(t)}/message/${n}/action/${s}`}}class Yt extends pe{constructor(e){var t,s;super(),this.parameters=e,null!==(t=(s=this.parameters).storeInHistory)&&void 0!==t||(s.storeInHistory=true)}operation(){return K.PNPublishFileMessageOperation}validate(){const{channel:e,fileId:t,fileName:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}parse(e){return r(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[2]}}))}get path(){const{message:e,channel:t,keySet:{publishKey:s,subscribeKey:n},fileId:i,fileName:r}=this.parameters,a=Object.assign({file:{name:r,id:i}},e?{message:e}:{});return`/v1/files/publish-file/${s}/${n}/0/${C(t)}/0/${C(this.prepareMessagePayload(a))}`}get queryParameters(){const{customMessageType:e,storeInHistory:t,ttl:s,meta:n}=this.parameters;return Object.assign(Object.assign(Object.assign({store:t?"1":"0"},e?{custom_message_type:e}:{}),s?{ttl:s}:{}),n&&"object"==typeof n?{meta:JSON.stringify(n)}:{})}prepareMessagePayload(e){const{crypto:t}=this.parameters;if(!t)return JSON.stringify(e)||"";const s=t.encrypt(JSON.stringify(e));return JSON.stringify("string"==typeof s?s:u(s))}}class Zt extends pe{constructor(e){super({method:ue.LOCAL}),this.parameters=e}operation(){return K.PNGetFileUrlOperation}validate(){const{channel:e,id:t,name:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}parse(e){return r(this,void 0,void 0,(function*(){return e.url}))}get path(){const{channel:e,id:t,name:s,keySet:{subscribeKey:n}}=this.parameters;return`/v1/files/${n}/channels/${C(e)}/files/${C(t)}/${C(s)}`}}class es extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNDeleteFileOperation}validate(){const{channel:e,id:t,name:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}get path(){const{keySet:{subscribeKey:e},id:t,channel:s,name:n}=this.parameters;return`/v1/files/${e}/channels/${C(s)}/files/${C(t)}/${C(n)}`}}class ts extends pe{constructor(e){var t,s;super(),this.parameters=e,null!==(t=(s=this.parameters).limit)&&void 0!==t||(s.limit=100)}operation(){return K.PNListFilesOperation}validate(){if(!this.parameters.channel)return"channel can't be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v1/files/${e}/channels/${C(t)}/files`}get queryParameters(){const{limit:e,next:t}=this.parameters;return Object.assign({limit:e},t?{next:t}:{})}}class ss extends pe{constructor(e){super({method:ue.POST}),this.parameters=e}operation(){return K.PNGenerateUploadUrlOperation}validate(){return this.parameters.channel?this.parameters.name?void 0:"'name' can't be empty":"channel can't be empty"}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return{id:t.data.id,name:t.data.name,url:t.file_upload_request.url,formFields:t.file_upload_request.form_fields}}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v1/files/${e}/channels/${C(t)}/generate-upload-url`}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){return JSON.stringify({name:this.parameters.name})}}class ns extends pe{constructor(e){super({method:ue.POST}),this.parameters=e;const t=e.file.mimeType;t&&(e.formFields=e.formFields.map((e=>"Content-Type"===e.name?{name:e.name,value:t}:e)))}operation(){return K.PNPublishFileOperation}validate(){const{fileId:e,fileName:t,file:s,uploadUrl:n}=this.parameters;return e?t?s?n?void 0:"Validation failed: file upload 'url' can't be empty":"Validation failed: 'file' can't be empty":"Validation failed: file 'name' can't be empty":"Validation failed: file 'id' can't be empty"}parse(e){return r(this,void 0,void 0,(function*(){return{status:e.status,message:e.body?ns.decoder.decode(e.body):"OK"}}))}request(){return Object.assign(Object.assign({},super.request()),{origin:new URL(this.parameters.uploadUrl).origin,timeout:300})}get path(){const{pathname:e,search:t}=new URL(this.parameters.uploadUrl);return`${e}${t}`}get body(){return this.parameters.file}get formData(){return this.parameters.formFields}}class is{constructor(e){var t;if(this.parameters=e,this.file=null===(t=this.parameters.PubNubFile)||void 0===t?void 0:t.create(e.file),!this.file)throw new Error("File upload error: unable to create File object.")}process(){return r(this,void 0,void 0,(function*(){let e,t;return this.generateFileUploadUrl().then((s=>(e=s.name,t=s.id,this.uploadFile(s)))).then((e=>{if(204!==e.status)throw new d("Upload to bucket was unsuccessful",{error:!0,statusCode:e.status,category:h.PNUnknownCategory,operation:K.PNPublishFileOperation,errorData:{message:e.message}})})).then((()=>this.publishFileMessage(t,e))).catch((e=>{if(e instanceof d)throw e;const t=e instanceof G?e:G.create(e);throw new d("File upload error.",t.toStatus(K.PNPublishFileOperation))}))}))}generateFileUploadUrl(){return r(this,void 0,void 0,(function*(){const e=new ss(Object.assign(Object.assign({},this.parameters),{name:this.file.name,keySet:this.parameters.keySet}));return this.parameters.sendRequest(e)}))}uploadFile(e){return r(this,void 0,void 0,(function*(){const{cipherKey:t,PubNubFile:s,crypto:n,cryptography:i}=this.parameters,{id:r,name:a,url:o,formFields:c}=e;return this.parameters.PubNubFile.supportsEncryptFile&&(!t&&n?this.file=yield n.encryptFile(this.file,s):t&&i&&(this.file=yield i.encryptFile(t,this.file,s))),this.parameters.sendRequest(new ns({fileId:r,fileName:a,file:this.file,uploadUrl:o,formFields:c}))}))}publishFileMessage(e,t){return r(this,void 0,void 0,(function*(){var s,n,i,r;let a,o={timetoken:"0"},c=this.parameters.fileUploadPublishRetryLimit,u=!1;do{try{o=yield this.parameters.publishFile(Object.assign(Object.assign({},this.parameters),{fileId:e,fileName:t})),u=!0}catch(e){e instanceof d&&(a=e),c-=1}}while(!u&&c>0);if(u)return{status:200,timetoken:o.timetoken,id:e,name:t};throw new d("Publish failed. You may want to execute that operation manually using pubnub.publishFile",{error:!0,category:null!==(n=null===(s=a.status)||void 0===s?void 0:s.category)&&void 0!==n?n:h.PNUnknownCategory,statusCode:null!==(r=null===(i=a.status)||void 0===i?void 0:i.statusCode)&&void 0!==r?r:0,channel:this.parameters.channel,id:e,name:t})}))}}class rs{constructor(e,t){this.subscriptionStateIds=[],this.client=t,this._nameOrId=e}get entityType(){return"Channel"}get subscriptionType(){return _t.Channel}subscriptionNames(e){return[this._nameOrId,...e&&!this._nameOrId.endsWith("-pnpres")?[`${this._nameOrId}-pnpres`]:[]]}subscription(e){return new $t({client:this.client,entity:this,options:e})}get subscriptionsCount(){return this.subscriptionStateIds.length}increaseSubscriptionCount(e){this.subscriptionStateIds.includes(e)||this.subscriptionStateIds.push(e)}decreaseSubscriptionCount(e){{const t=this.subscriptionStateIds.indexOf(e);t>=0&&this.subscriptionStateIds.splice(t,1)}}toString(){return`${this.entityType} { nameOrId: ${this._nameOrId}, subscriptionsCount: ${this.subscriptionsCount} }`}}const as=["default","__default__"],os=e=>{if(!e)return;const t=e.trim();return 0===t.length||as.includes(t.toLowerCase())?void 0:t};class cs extends rs{constructor(e,t,s){super(e,t),this._projection=os(s)}get id(){return this._nameOrId}get projection(){return this._projection}get subscriptionChannel(){return this._projection?`__${this._projection}__${this._nameOrId}`:this._nameOrId}subscriptionNames(e){return[this.subscriptionChannel]}subscription(e){{const t=this.entityForProjection(os(null==e?void 0:e.projection));return t!==this?t.subscription(e):super.subscription(e)}}entityForProjection(e){var t,s,n,i;if(e===this._projection)return this;const r=null!==(t=this.variants)&&void 0!==t?t:this.variants={};null!==(n=r[i=null!==(s=this._projection)&&void 0!==s?s:""])&&void 0!==n||(r[i]=this);const a=null!=e?e:"";let o=r[a];return o||(o=r[a]=this.withProjection(e),o.variants=r),o}toString(){var e;return`${this.entityType} { id: ${this._nameOrId}, projection: ${null!==(e=this._projection)&&void 0!==e?e:"none"}, channel: ${this.subscriptionChannel}, subscriptionsCount: ${this.subscriptionsCount} }`}}class us extends cs{get entityType(){return"DataSyncRelationship"}withProjection(e){return new us(this._nameOrId,this.client,e)}}class ls extends cs{get entityType(){return"DataSyncMembership"}withProjection(e){return new ls(this._nameOrId,this.client,e)}}class hs extends cs{get entityType(){return"DataSyncChannel"}withProjection(e){return new hs(this._nameOrId,this.client,e)}}class ds extends cs{get entityType(){return"DataSyncEntity"}withProjection(e){return new ds(this._nameOrId,this.client,e)}}class ps extends rs{get entityType(){return"ChannelMetadata"}get id(){return this._nameOrId}subscriptionNames(e){return[this.id]}}class gs extends cs{get entityType(){return"DataSyncUser"}withProjection(e){return new gs(this._nameOrId,this.client,e)}}class bs extends rs{get entityType(){return"ChannelGroups"}get name(){return this._nameOrId}get subscriptionType(){return _t.ChannelGroup}}class ys extends rs{get entityType(){return"UserMetadata"}get id(){return this._nameOrId}subscriptionNames(e){return[this.id]}}class ms extends rs{get entityType(){return"Channel"}get name(){return this._nameOrId}}class fs extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNRemoveChannelsFromGroupOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroup:s}=this.parameters;return e?s?t?void 0:"Missing channels":"Missing Channel Group":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${C(t)}`}get queryParameters(){return{remove:this.parameters.channels.join(",")}}}class vs extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNAddChannelsToGroupOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroup:s}=this.parameters;return e?s?t?void 0:"Missing channels":"Missing Channel Group":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${C(t)}`}get queryParameters(){return{add:this.parameters.channels.join(",")}}}class Os extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNChannelsForGroupOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channelGroup?void 0:"Missing Channel Group":"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){return{channels:this.deserializeResponse(e).payload.channels}}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${C(t)}`}}class Ss extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNRemoveGroupOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channelGroup?void 0:"Missing Channel Group":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${C(t)}/remove`}}class js extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNChannelGroupsOperation}validate(){if(!this.parameters.keySet.subscribeKey)return"Missing Subscribe Key"}parse(e){return r(this,void 0,void 0,(function*(){return{groups:this.deserializeResponse(e).payload.groups}}))}get path(){return`/v1/channel-registration/sub-key/${this.parameters.keySet.subscribeKey}/channel-group`}}class ws{constructor(e,t,s){this.sendRequest=s,this.logger=e,this.keySet=t}listChannels(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"List channel group channels with parameters:"})));const s=new Os(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`List channel group channels success. Received ${e.channels.length} channels.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}listGroups(e){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub","List all channel groups.");const t=new js({keySet:this.keySet}),s=e=>{e&&this.logger.debug("PubNub",`List all channel groups success. Received ${e.groups.length} groups.`)};return e?this.sendRequest(t,((t,n)=>{s(n),e(t,n)})):this.sendRequest(t).then((e=>(s(e),e)))}))}addChannels(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add channels to the channel group with parameters:"})));const s=new vs(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Add channels to the channel group success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}removeChannels(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove channels from the channel group with parameters:"})));const s=new fs(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Remove channels from the channel group success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}deleteGroup(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove a channel group with parameters:"})));const s=new Ss(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub",`Remove a channel group success. Removed '${e.channelGroup}' channel group.'`)};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}}class ks extends pe{constructor(e){var t,s;super(),this.parameters=e,"apns2"===this.parameters.pushGateway&&(null!==(t=(s=this.parameters).environment)&&void 0!==t||(s.environment="development")),this.parameters.count&&this.parameters.count>1e3&&(this.parameters.count=1e3)}operation(){throw Error("Should be implemented in subclass.")}validate(){const{keySet:{subscribeKey:e},action:t,device:s,pushGateway:n}=this.parameters;return e?s?"add"!==t&&"remove"!==t||"channels"in this.parameters&&0!==this.parameters.channels.length?n?"apns2"!==this.parameters.pushGateway||this.parameters.topic?void 0:"Missing APNS2 topic":"Missing GW Type (pushGateway: fcm or apns2)":"Missing Channels":"Missing Device ID (device)":"Missing Subscribe Key"}get path(){const{keySet:{subscribeKey:e},action:t,device:s,pushGateway:n}=this.parameters;let i="apns2"===n?`/v2/push/sub-key/${e}/devices-apns2/${s}`:`/v1/push/sub-key/${e}/devices/${s}`;return"remove-device"===t&&(i=`${i}/remove`),i}get queryParameters(){const{start:e,count:t}=this.parameters;let s=Object.assign(Object.assign({type:this.parameters.pushGateway},e?{start:e}:{}),t&&t>0?{count:t}:{});if("channels"in this.parameters&&(s[this.parameters.action]=this.parameters.channels.join(",")),"apns2"===this.parameters.pushGateway){const{environment:e,topic:t}=this.parameters;s=Object.assign(Object.assign({},s),{environment:e,topic:t})}return s}}class Cs extends ks{constructor(e){super(Object.assign(Object.assign({},e),{action:"remove"}))}operation(){return K.PNRemovePushNotificationEnabledChannelsOperation}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}}class Ps extends ks{constructor(e){super(Object.assign(Object.assign({},e),{action:"list"}))}operation(){return K.PNPushNotificationEnabledChannelsOperation}parse(e){return r(this,void 0,void 0,(function*(){return{channels:this.deserializeResponse(e)}}))}}class Ns extends ks{constructor(e){super(Object.assign(Object.assign({},e),{action:"add"}))}operation(){return K.PNAddPushNotificationEnabledChannelsOperation}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}}class Es extends ks{constructor(e){super(Object.assign(Object.assign({},e),{action:"remove-device"}))}operation(){return K.PNRemoveAllPushNotificationsOperation}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return r(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}}class Ts{constructor(e,t,s){this.sendRequest=s,this.logger=e,this.keySet=t}listChannels(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"List push-enabled channels with parameters:"})));const s=new Ps(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`List push-enabled channels success. Received ${e.channels.length} channels.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}addChannels(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add push-enabled channels with parameters:"})));const s=new Ns(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Add push-enabled channels success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}removeChannels(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove push-enabled channels with parameters:"})));const s=new Cs(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Remove push-enabled channels success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}deleteDevice(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove push notifications for device with parameters:"})));const s=new Es(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Remove push notifications for device success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}}class _s extends pe{constructor(e){var t,s,n,i,r,a;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(r=e.include).customFields)&&void 0!==s||(r.customFields=false),null!==(n=(a=e.include).totalCount)&&void 0!==n||(a.totalCount=false),null!==(i=e.limit)&&void 0!==i||(e.limit=100)}operation(){return K.PNGetAllChannelMetadataOperation}get path(){return`/v2/objects/${this.parameters.keySet.subscribeKey}/channels`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";return r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e)),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({include:["status","type",...e.customFields?["custom"]:[]].join(","),count:`${e.totalCount}`},s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class Is extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNRemoveChannelMetadataOperation}validate(){if(!this.parameters.channel)return"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${C(t)}`}}class Ms extends pe{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).channelFields)&&void 0!==a||(b.channelFields=false),null!==(o=(y=e.include).customChannelFields)&&void 0!==o||(y.customChannelFields=false),null!==(c=(m=e.include).channelStatusField)&&void 0!==c||(m.channelStatusField=false),null!==(u=(f=e.include).channelTypeField)&&void 0!==u||(f.channelTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return K.PNGetMembershipsOperation}validate(){if(!this.parameters.uuid)return"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${C(t)}/channels`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=[];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.channelFields&&a.push("channel"),e.channelStatusField&&a.push("channel.status"),e.channelTypeField&&a.push("channel.type"),e.customChannelFields&&a.push("channel.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class Rs extends pe{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super({method:ue.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).channelFields)&&void 0!==a||(b.channelFields=false),null!==(o=(y=e.include).customChannelFields)&&void 0!==o||(y.customChannelFields=false),null!==(c=(m=e.include).channelStatusField)&&void 0!==c||(m.channelStatusField=false),null!==(u=(f=e.include).channelTypeField)&&void 0!==u||(f.channelTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return K.PNSetMembershipsOperation}validate(){const{uuid:e,channels:t}=this.parameters;return e?t&&0!==t.length?void 0:"Channels cannot be empty":"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${C(t)}/channels`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=["channel.status","channel.type","status"];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.channelFields&&a.push("channel"),e.channelStatusField&&a.push("channel.status"),e.channelTypeField&&a.push("channel.type"),e.customChannelFields&&a.push("channel.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){const{channels:e,type:t}=this.parameters;return JSON.stringify({[`${t}`]:e.map((e=>"string"==typeof e?{channel:{id:e}}:{channel:{id:e.id},status:e.status,type:e.type,custom:e.custom}))})}}class Ds extends pe{constructor(e){var t,s,n,i;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(i=e.include).customFields)&&void 0!==s||(i.customFields=false),null!==(n=e.limit)&&void 0!==n||(e.limit=100)}operation(){return K.PNGetAllUUIDMetadataOperation}get path(){return`/v2/objects/${this.parameters.keySet.subscribeKey}/uuids`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";return r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e)),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({include:["status","type",...e.customFields?["custom"]:[]].join(",")},void 0!==e.totalCount?{count:`${e.totalCount}`}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class Us extends pe{constructor(e){var t,s,n;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true)}operation(){return K.PNGetChannelMetadataOperation}validate(){if(!this.parameters.channel)return"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${C(t)}`}get queryParameters(){return{include:["status","type",...this.parameters.include.customFields?["custom"]:[]].join(",")}}}class As extends pe{constructor(e){var t,s,n;super({method:ue.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true)}operation(){return K.PNSetChannelMetadataOperation}validate(){return this.parameters.channel?this.parameters.data?void 0:"Data cannot be empty":"Channel cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json"})}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${C(t)}`}get queryParameters(){return{include:["status","type",...this.parameters.include.customFields?["custom"]:[]].join(",")}}get body(){return JSON.stringify(this.parameters.data)}}class $s extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e,this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return K.PNRemoveUUIDMetadataOperation}validate(){if(!this.parameters.uuid)return"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${C(t)}`}}class Fs extends pe{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).UUIDFields)&&void 0!==a||(b.UUIDFields=false),null!==(o=(y=e.include).customUUIDFields)&&void 0!==o||(y.customUUIDFields=false),null!==(c=(m=e.include).UUIDStatusField)&&void 0!==c||(m.UUIDStatusField=false),null!==(u=(f=e.include).UUIDTypeField)&&void 0!==u||(f.UUIDTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100)}operation(){return K.PNSetMembersOperation}validate(){if(!this.parameters.channel)return"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${C(t)}/uuids`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=[];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.UUIDFields&&a.push("uuid"),e.UUIDStatusField&&a.push("uuid.status"),e.UUIDTypeField&&a.push("uuid.type"),e.customUUIDFields&&a.push("uuid.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class xs extends pe{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super({method:ue.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).UUIDFields)&&void 0!==a||(b.UUIDFields=false),null!==(o=(y=e.include).customUUIDFields)&&void 0!==o||(y.customUUIDFields=false),null!==(c=(m=e.include).UUIDStatusField)&&void 0!==c||(m.UUIDStatusField=false),null!==(u=(f=e.include).UUIDTypeField)&&void 0!==u||(f.UUIDTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100)}operation(){return K.PNSetMembersOperation}validate(){const{channel:e,uuids:t}=this.parameters;return e?t&&0!==t.length?void 0:"UUIDs cannot be empty":"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${C(t)}/uuids`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=["uuid.status","uuid.type","type"];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.UUIDFields&&a.push("uuid"),e.UUIDStatusField&&a.push("uuid.status"),e.UUIDTypeField&&a.push("uuid.type"),e.customUUIDFields&&a.push("uuid.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){const{uuids:e,type:t}=this.parameters;return JSON.stringify({[`${t}`]:e.map((e=>"string"==typeof e?{uuid:{id:e}}:{uuid:{id:e.id},status:e.status,type:e.type,custom:e.custom}))})}}class qs extends pe{constructor(e){var t,s,n;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return K.PNGetUUIDMetadataOperation}validate(){if(!this.parameters.uuid)return"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${C(t)}`}get queryParameters(){const{include:e}=this.parameters;return{include:["status","type",...e.customFields?["custom"]:[]].join(",")}}}class Gs extends pe{constructor(e){var t,s,n;super({method:ue.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return K.PNSetUUIDMetadataOperation}validate(){return this.parameters.uuid?this.parameters.data?void 0:"Data cannot be empty":"'uuid' cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json"})}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${C(t)}`}get queryParameters(){return{include:["status","type",...this.parameters.include.customFields?["custom"]:[]].join(",")}}get body(){return JSON.stringify(this.parameters.data)}}class Ls{constructor(e,t){this.keySet=e.keySet,this.configuration=e,this.sendRequest=t}get logger(){return this.configuration.logger()}getAllUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Get all UUID metadata objects with parameters:"}))),this._getAllUUIDMetadata(e,t)}))}_getAllUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0);const n=new Ds(Object.assign(Object.assign({},s),{keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Get all UUID metadata success. Received ${e.totalCount} UUID metadata objects.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}getUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.configuration.userId},details:`Get ${e&&"function"!=typeof e?"":" current"} UUID metadata object with parameters:`}))),this._getUUIDMetadata(e,t)}))}_getUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){var s;const n=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),n.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),n.uuid=n.userId),null!==(s=n.uuid)&&void 0!==s||(n.uuid=this.configuration.userId);const i=new qs(Object.assign(Object.assign({},n),{keySet:this.keySet})),r=e=>{e&&this.logger.debug("PubNub",`Get UUID metadata object success. Received '${n.uuid}' UUID metadata object.`)};return t?this.sendRequest(i,((e,s)=>{r(s),t(e,s)})):this.sendRequest(i).then((e=>(r(e),e)))}))}setUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set UUID metadata object with parameters:"}))),this._setUUIDMetadata(e,t)}))}_setUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){var s;e.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),e.uuid=e.userId),null!==(s=e.uuid)&&void 0!==s||(e.uuid=this.configuration.userId);const n=new Gs(Object.assign(Object.assign({},e),{keySet:this.keySet})),i=t=>{t&&this.logger.debug("PubNub",`Set UUID metadata object success. Updated '${e.uuid}' UUID metadata object.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}removeUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.configuration.userId},details:`Remove${e&&"function"!=typeof e?"":" current"} UUID metadata object with parameters:`}))),this._removeUUIDMetadata(e,t)}))}_removeUUIDMetadata(e,t){return r(this,void 0,void 0,(function*(){var s;const n=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),n.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),n.uuid=n.userId),null!==(s=n.uuid)&&void 0!==s||(n.uuid=this.configuration.userId);const i=new $s(Object.assign(Object.assign({},n),{keySet:this.keySet})),r=e=>{e&&this.logger.debug("PubNub",`Remove UUID metadata object success. Removed '${n.uuid}' UUID metadata object.`)};return t?this.sendRequest(i,((e,s)=>{r(s),t(e,s)})):this.sendRequest(i).then((e=>(r(e),e)))}))}getAllChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Get all Channel metadata objects with parameters:"}))),this._getAllChannelMetadata(e,t)}))}_getAllChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0);const n=new _s(Object.assign(Object.assign({},s),{keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Get all Channel metadata objects success. Received ${e.totalCount} Channel metadata objects.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}getChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Channel metadata object with parameters:"}))),this._getChannelMetadata(e,t)}))}_getChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){const s=new Us(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Get Channel metadata object success. Received '${e.channel}' Channel metadata object.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}setChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Channel metadata object with parameters:"}))),this._setChannelMetadata(e,t)}))}_setChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){const s=new As(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Set Channel metadata object success. Updated '${e.channel}' Channel metadata object.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}removeChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Channel metadata object with parameters:"}))),this._removeChannelMetadata(e,t)}))}_removeChannelMetadata(e,t){return r(this,void 0,void 0,(function*(){const s=new Is(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Remove Channel metadata object success. Removed '${e.channel}' Channel metadata object.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}getChannelMembers(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get channel members with parameters:"})));const s=new Fs(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Get channel members success. Received ${e.totalCount} channel members.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}setChannelMembers(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set channel members with parameters:"})));const s=new xs(Object.assign(Object.assign({},e),{type:"set",keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Set channel members success. There are ${e.totalCount} channel members now.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}removeChannelMembers(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove channel members with parameters:"})));const s=new xs(Object.assign(Object.assign({},e),{type:"delete",keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Remove channel members success. There are ${e.totalCount} channel members now.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}getMemberships(e,t){return r(this,void 0,void 0,(function*(){var s;const n=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),n.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),n.uuid=n.userId),null!==(s=n.uuid)&&void 0!==s||(n.uuid=this.configuration.userId),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},n),details:"Get memberships with parameters:"})));const i=new Ms(Object.assign(Object.assign({},n),{keySet:this.keySet})),r=e=>{e&&this.logger.debug("PubNub",`Get memberships success. Received ${e.totalCount} memberships.`)};return t?this.sendRequest(i,((e,s)=>{r(s),t(e,s)})):this.sendRequest(i).then((e=>(r(e),e)))}))}setMemberships(e,t){return r(this,void 0,void 0,(function*(){var s;e.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),e.uuid=e.userId),null!==(s=e.uuid)&&void 0!==s||(e.uuid=this.configuration.userId),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set memberships with parameters:"})));const n=new Rs(Object.assign(Object.assign({},e),{type:"set",keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Set memberships success. There are ${e.totalCount} memberships now.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}removeMemberships(e,t){return r(this,void 0,void 0,(function*(){var s;e.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),e.uuid=e.userId),null!==(s=e.uuid)&&void 0!==s||(e.uuid=this.configuration.userId),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove memberships with parameters:"})));const n=new Rs(Object.assign(Object.assign({},e),{type:"delete",keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Remove memberships success. There are ${e.totalCount} memberships now.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}fetchMemberships(e,t){return r(this,void 0,void 0,(function*(){var s,n;if(this.logger.warn("PubNub","'fetchMemberships' is deprecated. Use 'pubnub.objects.getChannelMembers' or 'pubnub.objects.getMemberships' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch memberships with parameters:"}))),"spaceId"in e){const n=e,i={channel:null!==(s=n.spaceId)&&void 0!==s?s:n.channel,filter:n.filter,limit:n.limit,page:n.page,include:Object.assign({},n.include),sort:n.sort?Object.fromEntries(Object.entries(n.sort).map((([e,t])=>[e.replace("user","uuid"),t]))):void 0},r=e=>({status:e.status,data:e.data.map((e=>({user:e.uuid,custom:e.custom,updated:e.updated,eTag:e.eTag}))),totalCount:e.totalCount,next:e.next,prev:e.prev});return t?this.getChannelMembers(i,((e,s)=>{t(e,s?r(s):s)})):this.getChannelMembers(i).then(r)}const i=e,r={uuid:null!==(n=i.userId)&&void 0!==n?n:i.uuid,filter:i.filter,limit:i.limit,page:i.page,include:Object.assign({},i.include),sort:i.sort?Object.fromEntries(Object.entries(i.sort).map((([e,t])=>[e.replace("space","channel"),t]))):void 0},a=e=>({status:e.status,data:e.data.map((e=>({space:e.channel,custom:e.custom,updated:e.updated,eTag:e.eTag}))),totalCount:e.totalCount,next:e.next,prev:e.prev});return t?this.getMemberships(r,((e,s)=>{t(e,s?a(s):s)})):this.getMemberships(r).then(a)}))}addMemberships(e,t){return r(this,void 0,void 0,(function*(){var s,n,i,r,a,o;if(this.logger.warn("PubNub","'addMemberships' is deprecated. Use 'pubnub.objects.setChannelMembers' or 'pubnub.objects.setMemberships' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add memberships with parameters:"}))),"spaceId"in e){const r=e,a={channel:null!==(s=r.spaceId)&&void 0!==s?s:r.channel,uuids:null!==(i=null===(n=r.users)||void 0===n?void 0:n.map((e=>"string"==typeof e?e:{id:e.userId,custom:e.custom})))&&void 0!==i?i:r.uuids,limit:0};return t?this.setChannelMembers(a,t):this.setChannelMembers(a)}const c=e,u={uuid:null!==(r=c.userId)&&void 0!==r?r:c.uuid,channels:null!==(o=null===(a=c.spaces)||void 0===a?void 0:a.map((e=>"string"==typeof e?e:{id:e.spaceId,custom:e.custom})))&&void 0!==o?o:c.channels,limit:0};return t?this.setMemberships(u,t):this.setMemberships(u)}))}}class Ks extends pe{constructor(e){super({method:ue.POST}),this.parameters=e}operation(){return K.PNCreateDataSyncUserOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"User data cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.user+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/users`}get body(){const{id:e,class:t,classLevel:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),void 0!==t?{entityClass:t}:{}),{entityClassVersion:n.classVersion}),void 0!==s?{entityClassLevel:s}:{}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}function Hs(e){return"string"==typeof e?e:Object.entries(null!=e?e:{}).map((([e,t])=>null!==t?`${e}:${t}`:e))}function Bs(e){const{add:t,replace:s,remove:n,move:i,copy:r,test:a}=e,o=[];if(t)for(const[e,s]of Object.entries(t))o.push({op:"add",path:e,value:s});if(s)for(const[e,t]of Object.entries(s))o.push({op:"replace",path:e,value:t});if(n)for(const e of n)o.push({op:"remove",path:e});if(i)for(const{from:e,path:t}of i)o.push({op:"move",from:e,path:t});if(r)for(const{from:e,path:t}of r)o.push({op:"copy",from:e,path:t});if(a)for(const[e,t]of Object.entries(a))o.push({op:"test",path:e,value:t});return o}class Ws extends pe{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return K.PNGetDataSyncUsersOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/users`}get queryParameters(){const{class:e,classVersion:t,classLevel:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Hs(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},e?{entity_class:e}:{}),void 0!==t?{entity_class_version:`${t}`}:{}),s?{entity_class_level:s}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class Vs extends pe{constructor(e){super({method:ue.PUT}),this.parameters=e}operation(){return K.PNSetDataSyncUserOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"User data cannot be empty":"User id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.user+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${C(t)}`}get body(){const{data:e}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityClassVersion:e.classVersion},void 0!==e.status?{status:e.status}:{}),void 0!==e.payload?{payload:e.payload}:{})})}}class zs extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNRemoveDataSyncUserOperation}validate(){if(!this.parameters.id)return"User id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return r(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${C(t)}`}}class Js extends pe{constructor(e){super({method:ue.PATCH}),this.parameters=e}operation(){return K.PNUpdateDataSyncUserOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"User id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${C(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Bs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class Xs extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNGetDataSyncUserOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"User id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${C(t)}`}}class Qs extends pe{constructor(e){super({method:ue.POST}),this.parameters=e}operation(){return K.PNCreateDataSyncChannelOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Channel data cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.channel+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/channels`}get body(){const{id:e,class:t,classLevel:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),void 0!==t?{entityClass:t}:{}),{entityClassVersion:n.classVersion}),void 0!==s?{entityClassLevel:s}:{}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}class Ys extends pe{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return K.PNGetDataSyncChannelsOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/channels`}get queryParameters(){const{class:e,classVersion:t,classLevel:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Hs(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},e?{entity_class:e}:{}),void 0!==t?{entity_class_version:`${t}`}:{}),s?{entity_class_level:s}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class Zs extends pe{constructor(e){super({method:ue.PUT}),this.parameters=e}operation(){return K.PNSetDataSyncChannelOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Channel data cannot be empty":"Channel id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.channel+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${C(t)}`}get body(){const{data:e}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityClassVersion:e.classVersion},void 0!==e.status?{status:e.status}:{}),void 0!==e.payload?{payload:e.payload}:{})})}}class en extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNRemoveDataSyncChannelOperation}validate(){if(!this.parameters.id)return"Channel id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return r(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${C(t)}`}}class tn extends pe{constructor(e){super({method:ue.PATCH}),this.parameters=e}operation(){return K.PNUpdateDataSyncChannelOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Channel id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${C(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Bs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class sn extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNGetDataSyncChannelOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Channel id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${C(t)}`}}class nn extends pe{constructor(e){super({method:ue.POST}),this.parameters=e}operation(){return K.PNCreateDataSyncMembershipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.userId?this.parameters.channelId?this.parameters.data?this.parameters.data.classVersion?void 0:"Membership class version cannot be empty":"Membership data cannot be empty":"Channel id cannot be empty":"User id cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.membership+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships`}get body(){const{id:e,userId:t,channelId:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),{userId:t,channelId:s,relationshipClassVersion:n.classVersion}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}class rn extends pe{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return K.PNGetDataSyncMembershipsOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/memberships`}get queryParameters(){const{userId:e,channelId:t,classVersion:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Hs(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},e?{user_id:e}:{}),t?{channel_id:t}:{}),void 0!==s?{relationship_class_version:`${s}`}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class an extends pe{constructor(e){super({method:ue.PUT}),this.parameters=e}operation(){return K.PNSetDataSyncMembershipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.userId?this.parameters.channelId?this.parameters.data?this.parameters.data.classVersion?void 0:"Membership class version cannot be empty":"Membership data cannot be empty":"Channel id cannot be empty":"User id cannot be empty":"Membership id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.membership+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${C(t)}`}get body(){const{userId:e,channelId:t,data:s}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({userId:e,channelId:t,relationshipClassVersion:s.classVersion},void 0!==s.status?{status:s.status}:{}),void 0!==s.payload?{payload:s.payload}:{})})}}class on extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNRemoveDataSyncMembershipOperation}validate(){if(!this.parameters.id)return"Membership id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return r(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${C(t)}`}}class cn extends pe{constructor(e){super({method:ue.PATCH}),this.parameters=e}operation(){return K.PNUpdateDataSyncMembershipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Membership id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${C(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Bs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class un extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNGetDataSyncMembershipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Membership id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${C(t)}`}}class ln extends pe{constructor(e){super({method:ue.POST}),this.parameters=e}operation(){return K.PNCreateDataSyncRelationshipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.entityAId?this.parameters.entityBId?this.parameters.class?this.parameters.data?this.parameters.data.classVersion?void 0:"Relationship class version cannot be empty":"Relationship data cannot be empty":"Relationship class cannot be empty":"Entity B id cannot be empty":"Entity A id cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.relationship+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships`}get body(){const{id:e,class:t,entityAId:s,entityBId:n,data:i}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),{entityAId:s,entityBId:n,relationshipClass:t,relationshipClassVersion:i.classVersion}),void 0!==i.status?{status:i.status}:{}),void 0!==i.payload?{payload:i.payload}:{})})}}class hn extends pe{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return K.PNGetDataSyncRelationshipsOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.class)return"Relationship class cannot be empty"}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/relationships`}get queryParameters(){const{class:e,classVersion:t,entityAId:s,entityBId:n,cursor:i,limit:r,filter:a,filterFast:o,sort:c}=this.parameters,u=Hs(c);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({relationship_class:e},void 0!==t?{relationship_class_version:`${t}`}:{}),s?{entity_a_id:s}:{}),n?{entity_b_id:n}:{}),i?{cursor:i}:{}),r?{limit:`${r}`}:{}),a?{filter:a}:{}),o?{filter_fast:o}:{}),u.length?{sort:u}:{})}}class dn extends pe{constructor(e){super({method:ue.PUT}),this.parameters=e}operation(){return K.PNSetDataSyncRelationshipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.entityAId?this.parameters.entityBId?this.parameters.data?this.parameters.data.classVersion?void 0:"Relationship class version cannot be empty":"Relationship data cannot be empty":"Entity B id cannot be empty":"Entity A id cannot be empty":"Relationship id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.relationship+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${C(t)}`}get body(){const{entityAId:e,entityBId:t,data:s}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityAId:e,entityBId:t,relationshipClassVersion:s.classVersion},void 0!==s.status?{status:s.status}:{}),void 0!==s.payload?{payload:s.payload}:{})})}}class pn extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNRemoveDataSyncRelationshipOperation}validate(){if(!this.parameters.id)return"Relationship id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return r(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${C(t)}`}}class gn extends pe{constructor(e){super({method:ue.PATCH}),this.parameters=e}operation(){return K.PNUpdateDataSyncRelationshipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Relationship id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${C(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Bs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class bn extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNGetDataSyncRelationshipOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Relationship id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${C(t)}`}}class yn extends pe{constructor(e){super({method:ue.POST}),this.parameters=e}operation(){return K.PNCreateDataSyncEntityOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.class?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Entity data cannot be empty":"Entity class cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.entity+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/entities`}get body(){const{id:e,class:t,classLevel:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),{entityClass:t,entityClassVersion:n.classVersion}),void 0!==s?{entityClassLevel:s}:{}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}class mn extends pe{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return K.PNGetDataSyncEntitiesOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.class)return"Entity class cannot be empty"}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/entities`}get queryParameters(){const{class:e,classVersion:t,classLevel:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Hs(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({entity_class:e},void 0!==t?{entity_class_version:`${t}`}:{}),s?{entity_class_level:s}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class fn extends pe{constructor(e){super({method:ue.PUT}),this.parameters=e}operation(){return K.PNSetDataSyncEntityOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Entity data cannot be empty":"Entity id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.entity+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${C(t)}`}get body(){const{data:e}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityClassVersion:e.classVersion},void 0!==e.status?{status:e.status}:{}),void 0!==e.payload?{payload:e.payload}:{})})}}class vn extends pe{constructor(e){super({method:ue.DELETE}),this.parameters=e}operation(){return K.PNRemoveDataSyncEntityOperation}validate(){if(!this.parameters.id)return"Entity id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return r(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${C(t)}`}}class On extends pe{constructor(e){super({method:ue.PATCH}),this.parameters=e}operation(){return K.PNUpdateDataSyncEntityOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Entity id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${C(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Bs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class Sn extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNGetDataSyncEntityOperation}parse(e){return r(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Entity id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${C(t)}`}}class jn{constructor(e,t){this.keySet=e.keySet,this.configuration=e,this.sendRequest=t}get logger(){return this.configuration.logger()}createEntity(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Entity with parameters:"})));const s=new yn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getEntity(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Entity with parameters:"})));const s=new Sn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getEntities(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Entities with parameters:"})));const s=new mn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}setEntity(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Entity with parameters:"})));const s=new fn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateEntity(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Entity with parameters:"})));const s=new On(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeEntity(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Entity with parameters:"})));const s=new vn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createRelationship(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Relationship with parameters:"})));const s=new ln(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getRelationship(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Relationship with parameters:"})));const s=new bn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getRelationships(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Relationships with parameters:"})));const s=new hn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}setRelationship(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Relationship with parameters:"})));const s=new dn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateRelationship(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Relationship with parameters:"})));const s=new gn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeRelationship(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Relationship with parameters:"})));const s=new pn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createUser(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create User with parameters:"})));const s=new Ks(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getUser(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get User with parameters:"})));const s=new Xs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getUsers(e,t){return r(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},s),details:"Get Users with parameters:"})));const n=new Ws(Object.assign(Object.assign({},s),{keySet:this.keySet}));return t?this.sendRequest(n,t):this.sendRequest(n)}))}setUser(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set User with parameters:"})));const s=new Vs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateUser(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update User with parameters:"})));const s=new Js(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeUser(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove User with parameters:"})));const s=new zs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createChannel(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Channel with parameters:"})));const s=new Qs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getChannel(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Channel with parameters:"})));const s=new sn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getChannels(e,t){return r(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},s),details:"Get Channels with parameters:"})));const n=new Ys(Object.assign(Object.assign({},s),{keySet:this.keySet}));return t?this.sendRequest(n,t):this.sendRequest(n)}))}setChannel(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Channel with parameters:"})));const s=new Zs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateChannel(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Channel with parameters:"})));const s=new tn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeChannel(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Channel with parameters:"})));const s=new en(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createMembership(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Membership with parameters:"})));const s=new nn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getMembership(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Membership with parameters:"})));const s=new un(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getMemberships(e,t){return r(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},s),details:"Get Memberships with parameters:"})));const n=new rn(Object.assign(Object.assign({},s),{keySet:this.keySet}));return t?this.sendRequest(n,t):this.sendRequest(n)}))}setMembership(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Membership with parameters:"})));const s=new an(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateMembership(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Membership with parameters:"})));const s=new cn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeMembership(e,t){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Membership with parameters:"})));const s=new on(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}}class wn extends pe{constructor(){super()}operation(){return K.PNTimeOperation}parse(e){return r(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[0]}}))}get path(){return"/time/0"}}class kn extends pe{constructor(e){super(),this.parameters=e}operation(){return K.PNDownloadFileOperation}validate(){const{channel:e,id:t,name:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}parse(e){return r(this,void 0,void 0,(function*(){const{cipherKey:t,crypto:s,cryptography:n,name:i,PubNubFile:r}=this.parameters,a=e.headers["content-type"];let o,c=e.body;return r.supportsEncryptFile&&(t||s)&&(t&&n?c=yield n.decrypt(t,c):!t&&s&&(o=yield s.decryptFile(r.create({data:c,name:i,mimeType:a}),r))),o||r.create({data:c,name:i,mimeType:a})}))}get path(){const{keySet:{subscribeKey:e},channel:t,id:s,name:n}=this.parameters;return`/v1/files/${e}/channels/${C(t)}/files/${C(s)}/${C(n)}`}}class Cn{static notificationPayload(e,t){return new Pe(e,t)}static generateUUID(){return ie.createUUID()}constructor(e){if(this.eventHandleCapable={},this.entities={},this._configuration=e.configuration,this.cryptography=e.cryptography,this.tokenManager=e.tokenManager,this.transport=e.transport,this.crypto=e.crypto,this.logger.debug("PubNub",(()=>({messageType:"object",message:e.configuration,details:"Create with configuration:",ignoredKeys:(e,t)=>"function"==typeof t[e]||e.startsWith("_")||"keySet"===e||k(e)}))),this._objects=new Ls(this._configuration,this.sendRequest.bind(this)),this._dataSync=new jn(this._configuration,this.sendRequest.bind(this)),this._channelGroups=new ws(this._configuration.logger(),this._configuration.keySet,this.sendRequest.bind(this)),this._push=new Ts(this._configuration.logger(),this._configuration.keySet,this.sendRequest.bind(this)),this.eventDispatcher=new ve,this._configuration.enableEventEngine){this.logger.debug("PubNub","Using new subscription loop management.");let e=this._configuration.getHeartbeatInterval();this.presenceState={},e&&(this.presenceEventEngine=new nt({heartbeat:(e,t)=>(this.logger.trace("PresenceEventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Heartbeat with parameters:"}))),this.heartbeat(e,t)),leave:e=>{this.logger.trace("PresenceEventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),this.makeUnsubscribe(e,(()=>{}))},heartbeatDelay:()=>{let t=()=>{};const s=new Promise(((s,n)=>{if(e=this._configuration.getHeartbeatInterval(),e){const i=setTimeout((()=>{s()}),1e3*e);t=()=>{clearTimeout(i),n(new d("Heartbeat interval has been cancelled."))}}else n(new d("Heartbeat interval has been reset."))}));return{cancelDelay:t,promise:s}},emitStatus:e=>this.emitStatus(e),config:this._configuration,presenceState:this.presenceState})),this.eventEngine=new Ct({handshake:e=>(this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Handshake with parameters:",ignoredKeys:["abortSignal","crypto","timeout","keySet","getFileUrl"]}))),this.subscribeHandshake(e)),receiveMessages:e=>(this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Receive messages with parameters:",ignoredKeys:["abortSignal","crypto","timeout","keySet","getFileUrl"]}))),this.subscribeReceiveMessages(e)),join:e=>{var t,s;this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Join with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("EventEngine","Ignoring 'join' announcement request."):this.join(e)},leave:e=>{var t,s;this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("EventEngine","Ignoring 'leave' announcement request."):this.leave(e)},leaveAll:e=>{this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave all with parameters:"}))),this.leaveAll(e)},presenceReconnect:e=>{this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Reconnect with parameters:"}))),this.presenceReconnect(e)},presenceDisconnect:e=>{this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Disconnect with parameters:"}))),this.presenceDisconnect(e)},presenceState:this.presenceState,config:this._configuration,emitMessages:(e,t)=>{try{this.logger.debug("EventEngine",(()=>({messageType:"object",message:t.map((e=>{const t=e.type===ge.Message||e.type===ge.Signal?R(e.data.message):void 0;return t?{type:e.type,data:Object.assign(Object.assign({},e.data),{pn_mfp:t})}:e})),details:"Received events:"}))),t.forEach((t=>this.emitEvent(e,t)))}catch(e){const t={error:!0,category:h.PNUnknownCategory,errorData:e,statusCode:0};this.emitStatus(t)}},emitStatus:e=>this.emitStatus(e)})}else this.logger.debug("PubNub","Using legacy subscription loop management."),this.subscriptionManager=new je(this._configuration,((e,t)=>{try{this.emitEvent(e,t)}catch(e){const t={error:!0,category:h.PNUnknownCategory,errorData:e,statusCode:0};this.emitStatus(t)}}),this.emitStatus.bind(this),((e,t)=>{this.logger.trace("SubscriptionManager",(()=>({messageType:"object",message:Object.assign({},e),details:"Subscribe with parameters:",ignoredKeys:["crypto","timeout","keySet","getFileUrl"]}))),this.makeSubscribe(e,t)}),((e,t)=>(this.logger.trace("SubscriptionManager",(()=>({messageType:"object",message:Object.assign({},e),details:"Heartbeat with parameters:",ignoredKeys:["crypto","timeout","keySet","getFileUrl"]}))),this.heartbeat(e,t))),((e,t)=>{this.logger.trace("SubscriptionManager",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),this.makeUnsubscribe(e,t)}),this.time.bind(this))}get configuration(){return this._configuration}get _config(){return this.configuration}get authKey(){var e;return null!==(e=this._configuration.authKey)&&void 0!==e?e:void 0}getAuthKey(){return this.authKey}setAuthKey(e){this.logger.debug("PubNub","Auth key updated."),this._configuration.setAuthKey(e),this.onAuthenticationChange&&this.onAuthenticationChange(e)}get userId(){return this._configuration.userId}set userId(e){if(!e||"string"!=typeof e||0===e.trim().length){const e=new Error("Missing or invalid userId parameter. Provide a valid string userId");throw this.logger.error("PubNub",(()=>({messageType:"error",message:e}))),e}this.logger.debug("PubNub",`Set user ID: ${e}`),this._configuration.userId=e,this.onUserIdChange&&this.onUserIdChange(this._configuration.userId)}getUserId(){return this._configuration.userId}setUserId(e){this.userId=e}get filterExpression(){var e;return null!==(e=this._configuration.getFilterExpression())&&void 0!==e?e:void 0}getFilterExpression(){return this.filterExpression}set filterExpression(e){this.logger.debug("PubNub",`Set filter expression: ${e}`),this._configuration.setFilterExpression(e)}setFilterExpression(e){this.logger.debug("PubNub",`Set filter expression: ${e}`),this.filterExpression=e}get cipherKey(){return this._configuration.getCipherKey()}set cipherKey(e){this._configuration.setCipherKey(e)}setCipherKey(e){this.logger.debug("PubNub","Cipher key updated."),this.cipherKey=e}set heartbeatInterval(e){var t;this.logger.debug("PubNub",`Set heartbeat interval: ${e}`),this._configuration.setHeartbeatInterval(e),this.onHeartbeatIntervalChange&&this.onHeartbeatIntervalChange(null!==(t=this._configuration.getHeartbeatInterval())&&void 0!==t?t:0)}setHeartbeatInterval(e){this.heartbeatInterval=e}get logger(){return this._configuration.logger()}getVersion(){return this._configuration.getVersion()}_addPnsdkSuffix(e,t){this.logger.debug("PubNub",`Add '${e}' 'pnsdk' suffix: ${t}`),this._configuration._addPnsdkSuffix(e,t)}getUUID(){return this.userId}setUUID(e){this.logger.warn("PubNub","'setUserId` is deprecated, please use 'setUserId' or 'userId' setter instead."),this.logger.debug("PubNub",`Set UUID: ${e}`),this.userId=e}get customEncrypt(){return this._configuration.getCustomEncrypt()}get customDecrypt(){return this._configuration.getCustomDecrypt()}channel(e){let t=this.entities[`${e}_ch`];return t||(t=this.entities[`${e}_ch`]=new ms(e,this)),t}channelGroup(e){let t=this.entities[`${e}_chg`];return t||(t=this.entities[`${e}_chg`]=new bs(e,this)),t}channelMetadata(e){let t=this.entities[`${e}_chm`];return t||(t=this.entities[`${e}_chm`]=new ps(e,this)),t}userMetadata(e){let t=this.entities[`${e}_um`];return t||(t=this.entities[`${e}_um`]=new ys(e,this)),t}dataSyncUser(e){let t=this.entities[`${e}_dsu`];return t||(t=this.entities[`${e}_dsu`]=new gs(e,this)),t}dataSyncChannel(e){let t=this.entities[`${e}_dsc`];return t||(t=this.entities[`${e}_dsc`]=new hs(e,this)),t}dataSyncMembership(e){let t=this.entities[`${e}_dsm`];return t||(t=this.entities[`${e}_dsm`]=new ls(e,this)),t}dataSyncEntity(e){let t=this.entities[`${e}_dse`];return t||(t=this.entities[`${e}_dse`]=new ds(e,this)),t}dataSyncRelationship(e){let t=this.entities[`${e}_dsr`];return t||(t=this.entities[`${e}_dsr`]=new us(e,this)),t}subscriptionSet(e){var t,s;{const n=[];return null===(t=e.channels)||void 0===t||t.forEach((e=>n.push(this.channel(e)))),null===(s=e.channelGroups)||void 0===s||s.forEach((e=>n.push(this.channelGroup(e)))),new Ut({client:this,entities:n,options:e.subscriptionOptions})}}sendRequest(e,t){return r(this,void 0,void 0,(function*(){const s=e.validate();if(s){const e=(n=s,p(Object.assign({message:n},{}),h.PNValidationErrorCategory));if(this.logger.error("PubNub",(()=>({messageType:"error",message:e}))),t)return t(e,null);throw new d("Validation failed, check status for details",e)}var n;const i=e.request(),r=e.operation();i.formData&&i.formData.length>0||r===K.PNDownloadFileOperation?i.timeout=this._configuration.getFileTimeout():r===K.PNSubscribeOperation||r===K.PNReceiveMessagesOperation?i.timeout=this._configuration.getSubscribeTimeout():i.timeout=this._configuration.getTransactionTimeout();const a={error:!1,operation:r,category:h.PNAcknowledgmentCategory,statusCode:0},[o,c]=this.transport.makeSendable(i);return e.cancellationController=c||null,o.then((t=>{if(a.statusCode=t.status,200!==t.status&&204!==t.status){const e=Cn.decoder.decode(t.body),s=t.headers["content-type"];if(s||-1!==s.indexOf("javascript")||-1!==s.indexOf("json")){const t=JSON.parse(e);"object"==typeof t&&"error"in t&&t.error&&"object"==typeof t.error&&(a.errorData=t.error)}else a.responseText=e}return e.parse(t)})).then((e=>t?t(a,e):e)).catch((e=>{const s=e instanceof G?e:G.create(e);if(t)return s.category!==h.PNCancelledCategory&&this.logger.error("PubNub",(()=>({messageType:"error",message:s.toPubNubError(r,"REST API request processing error, check status for details")}))),t(s.toStatus(r),null);const n=s.toPubNubError(r,"REST API request processing error, check status for details");throw s.category!==h.PNCancelledCategory&&this.logger.error("PubNub",(()=>({messageType:"error",message:n}))),n}))}))}destroy(e=!1){this.logger.info("PubNub","Destroying PubNub client."),this._globalSubscriptionSet&&(this._globalSubscriptionSet.invalidate(!0),this._globalSubscriptionSet=void 0),Object.values(this.eventHandleCapable).forEach((e=>e.invalidate(!0))),this.eventHandleCapable={},this.subscriptionManager?(this.subscriptionManager.unsubscribeAll(e),this.subscriptionManager.disconnect()):this.eventEngine&&this.eventEngine.unsubscribeAll(e),this.presenceEventEngine&&this.presenceEventEngine.leaveAll(e)}stop(){this.logger.warn("PubNub","'stop' is deprecated, please use 'destroy' instead."),this.destroy()}publish(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Publish with parameters:"})));const s=!1===e.replicate&&!1===e.storeInHistory,n=new Pt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule()})),i=e=>{e&&this.logger.debug("PubNub",`${s?"Fire":"Publish"} success with timetoken: ${e.timetoken}`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}}))}signal(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Signal with parameters:"})));const s=new Nt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Publish success with timetoken: ${e.timetoken}`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}fire(e,t){return r(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fire with parameters:"}))),null!=t||(t=()=>{}),this.publish(Object.assign(Object.assign({},e),{replicate:!1,storeInHistory:!1}),t)}))}get globalSubscriptionSet(){return this._globalSubscriptionSet||(this._globalSubscriptionSet=this.subscriptionSet({})),this._globalSubscriptionSet}get subscriptionTimetoken(){return this.subscriptionManager?this.subscriptionManager.subscriptionTimetoken:this.eventEngine?this.eventEngine.subscriptionTimetoken:void 0}getSubscribedChannels(){return this.subscriptionManager?this.subscriptionManager.subscribedChannels:this.eventEngine?this.eventEngine.getSubscribedChannels():[]}getSubscribedChannelGroups(){return this.subscriptionManager?this.subscriptionManager.subscribedChannelGroups:this.eventEngine?this.eventEngine.getSubscribedChannelGroups():[]}registerEventHandleCapable(e,t,s){{let n;this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign(Object.assign({subscription:e},t?{cursor:t}:[]),s?{subscriptions:s}:{}),details:"Register event handle capable:"}))),this.eventHandleCapable[e.state.id]||(this.eventHandleCapable[e.state.id]=e),s&&0!==s.length?(n=new It({}),s.forEach((e=>n.add(e.subscriptionInput(!1))))):n=e.subscriptionInput(!1);const i={};i.channels=n.channels,i.channelGroups=n.channelGroups,t&&(i.timetoken=t.timetoken),this.subscriptionManager?this.subscriptionManager.subscribe(i):this.eventEngine&&this.eventEngine.subscribe(i)}}unregisterEventHandleCapable(e,t){{if(!this.eventHandleCapable[e.state.id])return;const s=[];this.logger.trace("PubNub",(()=>({messageType:"object",message:{subscription:e,subscriptions:t},details:"Unregister event handle capable:"})));let n,i=!t||0===t.length;if(!i&&e instanceof Ut&&e.subscriptions.length===(null==t?void 0:t.length)&&(i=e.subscriptions.every((e=>t.includes(e)))),i&&delete this.eventHandleCapable[e.state.id],t&&0!==t.length?(n=new It({}),t.forEach((e=>{const t=e.subscriptionInput(!0);t.isEmpty?s.push(e):n.add(t)}))):(n=e.subscriptionInput(!0),n.isEmpty&&s.push(e)),s.length>0&&this.logger.trace("PubNub",(()=>{const e=[];return s[0]instanceof Ut?s[0].subscriptions.forEach((t=>e.push(t.state.entity))):s.forEach((t=>e.push(t.state.entity))),{messageType:"object",message:{entities:e},details:"Can't unregister event handle capable because entities still in use:"}})),n.isEmpty)return;{const e=[],t=[];if(Object.values(this.eventHandleCapable).forEach((s=>{const i=s.subscriptionInput(!1),r=i.channelGroups,a=i.channels;e.push(...n.channelGroups.filter((e=>r.includes(e)))),t.push(...n.channels.filter((e=>a.includes(e))))})),(t.length>0||e.length>0)&&(this.logger.trace("PubNub",(()=>{const s=[],i=n=>{const i=n.subscriptionNames(!0),r=n.subscriptionType===_t.Channel?t:e;i.some((e=>r.includes(e)))&&s.push(n)};Object.values(this.eventHandleCapable).forEach((e=>{e instanceof Ut?e.subscriptions.forEach((e=>{i(e.state.entity)})):e instanceof $t&&i(e.state.entity)}));let r="Some entities still in use:";return t.length+e.length===n.length&&(r="Can't unregister event handle capable because entities still in use:"),{messageType:"object",message:{entities:s},details:r}})),n.remove(new It({channels:t,channelGroups:e})),n.isEmpty))return}const r={};r.channels=n.channels,r.channelGroups=n.channelGroups,this.subscriptionManager?this.subscriptionManager.unsubscribe(r):this.eventEngine&&this.eventEngine.unsubscribe(r)}}subscribe(e){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Subscribe with parameters:"})));const t=this.subscriptionSet(Object.assign(Object.assign({},e),{subscriptionOptions:{receivePresenceEvents:e.withPresence}}));this.globalSubscriptionSet.addSubscriptionSet(t),t.dispose();const s="number"==typeof e.timetoken?`${e.timetoken}`:e.timetoken;this.globalSubscriptionSet.subscribe({timetoken:s})}}makeSubscribe(e,t){{this._configuration.isSharedWorkerEnabled()||(e.onDemand=!1);const s=new fe(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)}));if(this.sendRequest(s,((e,n)=>{var i;this.subscriptionManager&&(null===(i=this.subscriptionManager.abort)||void 0===i?void 0:i.identifier)===s.requestIdentifier&&(this.subscriptionManager.abort=null),t(e,n)})),this.subscriptionManager){const e=()=>s.abort("Cancel long-poll subscribe request");e.identifier=s.requestIdentifier,this.subscriptionManager.abort=e}}}unsubscribe(e){{if(this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Unsubscribe with parameters:"}))),!this._globalSubscriptionSet)return void this.logger.debug("PubNub","There are no active subscriptions. Ignore.");const t=this.globalSubscriptionSet.subscriptions.filter((t=>{var s,n;const i=t.subscriptionInput(!1);if(i.isEmpty)return!1;for(const t of null!==(s=e.channels)&&void 0!==s?s:[])if(i.contains(t))return!0;for(const t of null!==(n=e.channelGroups)&&void 0!==n?n:[])if(i.contains(t))return!0}));t.length>0&&this.globalSubscriptionSet.removeSubscriptions(t)}}makeUnsubscribe(e,t){{let{channels:s,channelGroups:n}=e;if(this._configuration.getKeepPresenceChannelsInPresenceRequests()||(n&&(n=n.filter((e=>!e.endsWith("-pnpres")))),s&&(s=s.filter((e=>!e.endsWith("-pnpres"))))),0===(null!=n?n:[]).length&&0===(null!=s?s:[]).length)return t({error:!1,operation:K.PNUnsubscribeOperation,category:h.PNAcknowledgmentCategory,statusCode:200});this.sendRequest(new Gt({channels:s,channelGroups:n,keySet:this._configuration.keySet}),t)}}unsubscribeAll(){this.logger.debug("PubNub","Unsubscribe all channels and groups"),this._globalSubscriptionSet&&this._globalSubscriptionSet.invalidate(!1),Object.values(this.eventHandleCapable).forEach((e=>e.invalidate(!1))),this.eventHandleCapable={},this.subscriptionManager?this.subscriptionManager.unsubscribeAll():this.eventEngine&&this.eventEngine.unsubscribeAll()}disconnect(e=!1){this.logger.debug("PubNub",`Disconnect (while offline? ${e?"yes":"no"})`),this.subscriptionManager?this.subscriptionManager.disconnect():this.eventEngine&&this.eventEngine.disconnect(e)}reconnect(e){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Reconnect with parameters:"}))),this.subscriptionManager?this.subscriptionManager.reconnect():this.eventEngine&&this.eventEngine.reconnect(null!=e?e:{})}subscribeHandshake(e){return r(this,void 0,void 0,(function*(){{this._configuration.isSharedWorkerEnabled()||(e.onDemand=!1),e.heartbeat||(e.heartbeat=this._configuration.getPresenceTimeout());const t=new Tt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)})),s=e.abortSignal.subscribe((e=>{t.abort("Cancel subscribe handshake request")}));return this.sendRequest(t).then((e=>(s(),e.cursor)))}}))}subscribeReceiveMessages(e){return r(this,void 0,void 0,(function*(){{this._configuration.isSharedWorkerEnabled()||(e.onDemand=!1),e.heartbeat||(e.heartbeat=this._configuration.getPresenceTimeout());const t=new Et(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)})),s=e.abortSignal.subscribe((e=>{t.abort("Cancel long-poll subscribe request")}));return this.sendRequest(t).then((e=>(s(),e)))}}))}getMessageActions(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get message actions with parameters:"})));const s=new Jt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Get message actions success. Received ${e.data.length} message actions.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}addMessageAction(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add message action with parameters:"})));const s=new Xt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Message action add success. Message action added with timetoken: ${e.data.actionTimetoken}`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}removeMessageAction(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove message action with parameters:"})));const s=new Qt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Message action remove success. Removed message action with ${e.actionTimetoken} timetoken.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}fetchMessages(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch messages with parameters:"})));const s=new zt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)})),n=e=>{if(!e)return;const t=Object.values(e.channels).reduce(((e,t)=>e+t.length),0);this.logger.debug("PubNub",`Fetch messages success. Received ${t} messages.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}deleteMessages(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Delete messages with parameters:"})));const s=new Ht(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub","Delete messages success.")};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}messageCounts(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get messages count with parameters:"})));const s=new Bt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=t=>{if(!t)return;const s=Object.values(t.channels).reduce(((e,t)=>e+t),0);this.logger.debug("PubNub",`Get messages count success. There are ${s} messages since provided reference timetoken${e.channelTimetokens?e.channelTimetokens.join(","):""}.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}history(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch history with parameters:"})));const s=new Wt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule()})),n=e=>{e&&this.logger.debug("PubNub",`Fetch history success. Received ${e.messages.length} messages.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}hereNow(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Here now with parameters:"})));const s=new Kt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Here now success. There are ${e.totalOccupancy} participants in ${e.totalChannels} channels.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}whereNow(e,t){return r(this,void 0,void 0,(function*(){var s;{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Where now with parameters:"})));const n=new Lt({uuid:null!==(s=e.uuid)&&void 0!==s?s:this._configuration.userId,keySet:this._configuration.keySet}),i=e=>{e&&this.logger.debug("PubNub",`Where now success. Currently present in ${e.channels.length} channels.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}}))}getState(e,t){return r(this,void 0,void 0,(function*(){var s;{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get presence state with parameters:"})));const n=new Ft(Object.assign(Object.assign({},e),{uuid:null!==(s=e.uuid)&&void 0!==s?s:this._configuration.userId,keySet:this._configuration.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Get presence state success. Received presence state for ${Object.keys(e.channels).length} channels.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}}))}setState(e,t){return r(this,void 0,void 0,(function*(){var s,n;{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set presence state with parameters:"})));const{keySet:i,userId:r}=this._configuration,a=this._configuration.getPresenceTimeout();let o;if(this._configuration.enableEventEngine&&this.presenceState){const t=this.presenceState;null===(s=e.channels)||void 0===s||s.forEach((s=>t[s]=e.state)),"channelGroups"in e&&(null===(n=e.channelGroups)||void 0===n||n.forEach((s=>t[s]=e.state))),this.onPresenceStateChange&&this.onPresenceStateChange(this.presenceState)}o="withHeartbeat"in e&&e.withHeartbeat?new qt(Object.assign(Object.assign({},e),{keySet:i,heartbeat:a})):new xt(Object.assign(Object.assign({},e),{keySet:i,uuid:r}));const c=e=>{e&&this.logger.debug("PubNub","Set presence state success."+(o instanceof qt?" Presence state has been set using heartbeat endpoint.":""))};return this.subscriptionManager&&(this.subscriptionManager.setState(e),this.onPresenceStateChange&&this.onPresenceStateChange(this.subscriptionManager.presenceState)),t?this.sendRequest(o,((e,s)=>{c(s),t(e,s)})):this.sendRequest(o).then((e=>(c(e),e)))}}))}presence(e){var t;this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Change presence with parameters:"}))),null===(t=this.subscriptionManager)||void 0===t||t.changePresence(e)}heartbeat(e,t){return r(this,void 0,void 0,(function*(){var s;{this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Heartbeat with parameters:"})));let{channels:n,channelGroups:i}=e;if(i&&(i=i.filter((e=>!e.endsWith("-pnpres")))),n&&(n=n.filter((e=>!e.endsWith("-pnpres")))),0===(null!=i?i:[]).length&&0===(null!=n?n:[]).length){const e={error:!1,operation:K.PNHeartbeatOperation,category:h.PNAcknowledgmentCategory,statusCode:200};return this.logger.trace("PubNub","There are no active subscriptions. Ignore."),t?t(e,{}):Promise.resolve(e)}const r=new qt(Object.assign(Object.assign({},e),{channels:[...new Set(n)],channelGroups:[...new Set(i)],keySet:this._configuration.keySet})),a=e=>{e&&this.logger.trace("PubNub","Heartbeat success.")},o=null===(s=e.abortSignal)||void 0===s?void 0:s.subscribe((e=>{r.abort("Cancel long-poll subscribe request")}));return t?this.sendRequest(r,((e,s)=>{a(s),o&&o(),t(e,s)})):this.sendRequest(r).then((e=>(a(e),o&&o(),e)))}}))}join(e){var t,s;this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Join with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("PubNub","Ignoring 'join' announcement request."):this.presenceEventEngine?this.presenceEventEngine.join(e):this.heartbeat(Object.assign(Object.assign({channels:e.channels,channelGroups:e.groups},this._configuration.maintainPresenceState&&this.presenceState&&Object.keys(this.presenceState).length>0&&{state:this.presenceState}),{heartbeat:this._configuration.getPresenceTimeout()}),(()=>{}))}presenceReconnect(e){this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Presence reconnect with parameters:"}))),this.presenceEventEngine?this.presenceEventEngine.reconnect():this.heartbeat(Object.assign(Object.assign({channels:e.channels,channelGroups:e.groups},this._configuration.maintainPresenceState&&{state:this.presenceState}),{heartbeat:this._configuration.getPresenceTimeout()}),(()=>{}))}leave(e){var t,s,n;this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("PubNub","Ignoring 'leave' announcement request."):this.presenceEventEngine?null===(n=this.presenceEventEngine)||void 0===n||n.leave(e):this.makeUnsubscribe({channels:e.channels,channelGroups:e.groups},(()=>{}))}leaveAll(e={}){this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave all with parameters:"}))),this.presenceEventEngine?this.presenceEventEngine.leaveAll(!!e.isOffline):e.isOffline||this.makeUnsubscribe({channels:e.channels,channelGroups:e.groups},(()=>{}))}presenceDisconnect(e){this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Presence disconnect parameters:"}))),this.presenceEventEngine?this.presenceEventEngine.disconnect(!!e.isOffline):e.isOffline||this.makeUnsubscribe({channels:e.channels,channelGroups:e.groups},(()=>{}))}grantToken(e,t){return r(this,void 0,void 0,(function*(){throw new Error("Grant Token error: PAM module disabled")}))}revokeToken(e,t){return r(this,void 0,void 0,(function*(){throw new Error("Revoke Token error: PAM module disabled")}))}get token(){return this.tokenManager&&this.tokenManager.getToken()}getToken(){return this.token}set token(e){this.tokenManager&&this.tokenManager.setToken(e),this.onAuthenticationChange&&this.onAuthenticationChange(e)}setToken(e){this.logger.debug("PubNub","Access token updated."),this.token=e}parseToken(e){return this.logger.debug("PubNub","Parse access token."),this.tokenManager&&this.tokenManager.parseToken(e)}grant(e,t){return r(this,void 0,void 0,(function*(){throw new Error("Grant error: PAM module disabled")}))}audit(e,t){return r(this,void 0,void 0,(function*(){throw new Error("Grant Permissions error: PAM module disabled")}))}get objects(){return this._objects}get dataSync(){return this._dataSync}fetchUsers(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchUsers' is deprecated. Use 'pubnub.objects.getAllUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Fetch all User objects with parameters:"}))),this.objects._getAllUUIDMetadata(e,t)}))}fetchUser(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchUser' is deprecated. Use 'pubnub.objects.getUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.userId},details:`Fetch${e&&"function"!=typeof e?"":" current"} User object with parameters:`}))),this.objects._getUUIDMetadata(e,t)}))}createUser(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'createUser' is deprecated. Use 'pubnub.objects.setUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create User object with parameters:"}))),this.objects._setUUIDMetadata(e,t)}))}updateUser(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'updateUser' is deprecated. Use 'pubnub.objects.setUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update User object with parameters:"}))),this.objects._setUUIDMetadata(e,t)}))}removeUser(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'removeUser' is deprecated. Use 'pubnub.objects.removeUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.userId},details:`Remove${e&&"function"!=typeof e?"":" current"} User object with parameters:`}))),this.objects._removeUUIDMetadata(e,t)}))}fetchSpaces(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchSpaces' is deprecated. Use 'pubnub.objects.getAllChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Fetch all Space objects with parameters:"}))),this.objects._getAllChannelMetadata(e,t)}))}fetchSpace(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchSpace' is deprecated. Use 'pubnub.objects.getChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch Space object with parameters:"}))),this.objects._getChannelMetadata(e,t)}))}createSpace(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'createSpace' is deprecated. Use 'pubnub.objects.setChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Space object with parameters:"}))),this.objects._setChannelMetadata(e,t)}))}updateSpace(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'updateSpace' is deprecated. Use 'pubnub.objects.setChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Space object with parameters:"}))),this.objects._setChannelMetadata(e,t)}))}removeSpace(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'removeSpace' is deprecated. Use 'pubnub.objects.removeChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Space object with parameters:"}))),this.objects._removeChannelMetadata(e,t)}))}fetchMemberships(e,t){return r(this,void 0,void 0,(function*(){return this.objects.fetchMemberships(e,t)}))}addMemberships(e,t){return r(this,void 0,void 0,(function*(){return this.objects.addMemberships(e,t)}))}updateMemberships(e,t){return r(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'addMemberships' is deprecated. Use 'pubnub.objects.setChannelMembers' or 'pubnub.objects.setMemberships' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update memberships with parameters:"}))),this.objects.addMemberships(e,t)}))}removeMemberships(e,t){return r(this,void 0,void 0,(function*(){var s,n,i;{if(this.logger.warn("PubNub","'removeMemberships' is deprecated. Use 'pubnub.objects.removeMemberships' or 'pubnub.objects.removeChannelMembers' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove memberships with parameters:"}))),"spaceId"in e){const i=e,r={channel:null!==(s=i.spaceId)&&void 0!==s?s:i.channel,uuids:null!==(n=i.userIds)&&void 0!==n?n:i.uuids,limit:0};return t?this.objects.removeChannelMembers(r,t):this.objects.removeChannelMembers(r)}const r=e,a={uuid:r.userId,channels:null!==(i=r.spaceIds)&&void 0!==i?i:r.channels,limit:0};return t?this.objects.removeMemberships(a,t):this.objects.removeMemberships(a)}}))}get channelGroups(){return this._channelGroups}get push(){return this._push}sendFile(e,t){return r(this,void 0,void 0,(function*(){{if(!this._configuration.PubNubFile)throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform.");this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Send file with parameters:"})));const s=new is(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,PubNubFile:this._configuration.PubNubFile,fileUploadPublishRetryLimit:this._configuration.fileUploadPublishRetryLimit,file:e.file,sendRequest:this.sendRequest.bind(this),publishFile:this.publishFile.bind(this),crypto:this._configuration.getCryptoModule(),cryptography:this.cryptography?this.cryptography:void 0})),n={error:!1,operation:K.PNPublishFileOperation,category:h.PNAcknowledgmentCategory,statusCode:0},i=e=>{e&&this.logger.debug("PubNub",`Send file success. File shared with ${e.id} ID.`)};return s.process().then((e=>(n.statusCode=e.status,i(e),t?t(n,e):e))).catch((e=>{let s;throw e instanceof d?s=e.status:e instanceof G&&(s=e.toStatus(n.operation)),this.logger.error("PubNub",(()=>({messageType:"error",message:new d("File sending error. Check status for details",s)}))),t&&s&&t(s,null),new d("REST API request processing error. Check status for details",s)}))}}))}publishFile(e,t){return r(this,void 0,void 0,(function*(){{if(!this._configuration.PubNubFile)throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform.");this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Publish file message with parameters:"})));const s=new Yt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule()})),n=e=>{e&&this.logger.debug("PubNub",`Publish file message success. File message published with timetoken: ${e.timetoken}`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}listFiles(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"List files with parameters:"})));const s=new ts(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`List files success. There are ${e.count} uploaded files.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}getFileUrl(e){var t;{const s=this.transport.request(new Zt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})).request()),n=null!==(t=s.queryParameters)&&void 0!==t?t:{},i=Object.keys(n).map((e=>{const t=n[e];return Array.isArray(t)?t.map((t=>`${e}=${C(t)}`)).join("&"):`${e}=${C(t)}`})).join("&");return`${s.origin}${s.path}?${i}`}}downloadFile(e,t){return r(this,void 0,void 0,(function*(){{if(!this._configuration.PubNubFile)throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform.");this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Download file with parameters:"})));const s=new kn(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,PubNubFile:this._configuration.PubNubFile,cryptography:this.cryptography?this.cryptography:void 0,crypto:this._configuration.getCryptoModule()})),n=e=>{e&&this.logger.debug("PubNub","Download file success.")};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):yield this.sendRequest(s).then((e=>(n(e),e)))}}))}deleteFile(e,t){return r(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Delete file with parameters:"})));const s=new es(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Delete file success. Deleted file with ${e.id} ID.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}time(e){return r(this,void 0,void 0,(function*(){this.logger.debug("PubNub","Get service time.");const t=new wn,s=e=>{e&&this.logger.debug("PubNub",`Get service time success. Current timetoken: ${e.timetoken}`)};return e?this.sendRequest(t,((t,n)=>{s(n),e(t,n)})):this.sendRequest(t).then((e=>(s(e),e)))}))}emitStatus(e){var t;null===(t=this.eventDispatcher)||void 0===t||t.handleStatus(e)}emitEvent(e,t){var s;this._globalSubscriptionSet&&this._globalSubscriptionSet.handleEvent(e,t),null===(s=this.eventDispatcher)||void 0===s||s.handleEvent(t),Object.values(this.eventHandleCapable).forEach((s=>{s!==this._globalSubscriptionSet&&s.handleEvent(e,t)}))}set onStatus(e){this.eventDispatcher&&(this.eventDispatcher.onStatus=e)}set onMessage(e){this.eventDispatcher&&(this.eventDispatcher.onMessage=e)}set onPresence(e){this.eventDispatcher&&(this.eventDispatcher.onPresence=e)}set onSignal(e){this.eventDispatcher&&(this.eventDispatcher.onSignal=e)}set onObjects(e){this.eventDispatcher&&(this.eventDispatcher.onObjects=e)}set onMessageAction(e){this.eventDispatcher&&(this.eventDispatcher.onMessageAction=e)}set onFile(e){this.eventDispatcher&&(this.eventDispatcher.onFile=e)}set onDataSync(e){this.eventDispatcher&&(this.eventDispatcher.onDataSync=e)}addListener(e){this.eventDispatcher&&this.eventDispatcher.addListener(e)}removeListener(e){this.eventDispatcher&&this.eventDispatcher.removeListener(e)}removeAllListeners(){this.eventDispatcher&&this.eventDispatcher.removeAllListeners()}encrypt(e,t){this.logger.warn("PubNub","'encrypt' is deprecated. Use cryptoModule instead.");const s=this._configuration.getCryptoModule();if(!t&&s&&"string"==typeof e){const t=s.encrypt(e);return"string"==typeof t?t:u(t)}if(!this.crypto)throw new Error("Encryption error: cypher key not set");return this.crypto.encrypt(e,t)}decrypt(e,t){this.logger.warn("PubNub","'decrypt' is deprecated. Use cryptoModule instead.");const s=this._configuration.getCryptoModule();if(!t&&s){const t=s.decrypt(e);return t instanceof ArrayBuffer?JSON.parse((new TextDecoder).decode(t)):t}if(!this.crypto)throw new Error("Decryption error: cypher key not set");return this.crypto.decrypt(e,t)}encryptFile(e,t){return r(this,void 0,void 0,(function*(){var s;if("string"!=typeof e&&(t=e),!t)throw new Error("File encryption error. Source file is missing.");if(!this._configuration.PubNubFile)throw new Error("File encryption error. File constructor not configured.");if("string"!=typeof e&&!this._configuration.getCryptoModule())throw new Error("File encryption error. Crypto module not configured.");if("string"==typeof e){if(!this.cryptography)throw new Error("File encryption error. File encryption not available");return this.cryptography.encryptFile(e,t,this._configuration.PubNubFile)}return null===(s=this._configuration.getCryptoModule())||void 0===s?void 0:s.encryptFile(t,this._configuration.PubNubFile)}))}decryptFile(e,t){return r(this,void 0,void 0,(function*(){var s;if("string"!=typeof e&&(t=e),!t)throw new Error("File encryption error. Source file is missing.");if(!this._configuration.PubNubFile)throw new Error("File decryption error. File constructor not configured.");if("string"==typeof e&&!this._configuration.getCryptoModule())throw new Error("File decryption error. Crypto module not configured.");if("string"==typeof e){if(!this.cryptography)throw new Error("File decryption error. File decryption not available");return this.cryptography.decryptFile(e,t,this._configuration.PubNubFile)}return null===(s=this._configuration.getCryptoModule())||void 0===s?void 0:s.decryptFile(t,this._configuration.PubNubFile)}))}}Cn.decoder=new TextDecoder,Cn.OPERATIONS=K,Cn.CATEGORIES=h,Cn.Endpoint=X,Cn.ExponentialRetryPolicy=Q.ExponentialRetryPolicy,Cn.LinearRetryPolicy=Q.LinearRetryPolicy,Cn.NoneRetryPolicy=Q.None,Cn.LogLevel=V;class Pn{constructor(e,t){this.decode=e,this.base64ToBinary=t}decodeToken(e){let t="";e.length%4==3?t="=":e.length%4==2&&(t="==");const s=e.replace(/-/gi,"+").replace(/_/gi,"/")+t,n=this.decode(this.base64ToBinary(s));return"object"==typeof n?n:void 0}}class Nn extends Cn{constructor(e){var t;const s=void 0!==e.subscriptionWorkerUrl,i=W(e),r=Object.assign(Object.assign({},i),{sdkFamily:"Web"});r.PubNubFile=o;const a=re(r,(e=>{if(e.cipherKey){return new F({default:new $(Object.assign(Object.assign({},e),e.logger?{}:{logger:a.logger()})),cryptors:[new j({cipherKey:e.cipherKey})]})}}));let u,l;e.subscriptionWorkerLogVerbosity?e.subscriptionWorkerLogLevel=V.Debug:void 0===e.subscriptionWorkerLogLevel&&(e.subscriptionWorkerLogLevel=V.None),void 0!==e.subscriptionWorkerLogVerbosity&&a.logger().warn("Configuration","'subscriptionWorkerLogVerbosity' is deprecated. Use 'subscriptionWorkerLogLevel' instead."),a.getCryptoModule()&&(a.getCryptoModule().logger=a.logger()),u=new ce(new Pn((e=>B(n.decode(e))),c)),(a.getCipherKey()||a.secretKey)&&(l=new U({secretKey:a.secretKey,cipherKey:a.getCipherKey(),useRandomIVs:a.getUseRandomIVs(),customEncrypt:a.getCustomEncrypt(),customDecrypt:a.getCustomDecrypt(),logger:a.logger()}));let h,d=()=>{},p=()=>{},g=()=>{},b=()=>{};h=new A;let y=new de(a.logger(),r.transport);if(i.subscriptionWorkerUrl)try{const e=new H({clientIdentifier:a._instanceId,subscriptionKey:a.subscribeKey,userId:a.getUserId(),workerUrl:i.subscriptionWorkerUrl,sdkVersion:a.getVersion(),heartbeatInterval:a.getHeartbeatInterval(),announceSuccessfulHeartbeats:a.announceSuccessfulHeartbeats,announceFailedHeartbeats:a.announceFailedHeartbeats,workerOfflineClientsCheckInterval:r.subscriptionWorkerOfflineClientsCheckInterval,workerUnsubscribeOfflineClients:r.subscriptionWorkerUnsubscribeOfflineClients,workerLogLevel:r.subscriptionWorkerLogLevel,tokenManager:u,transport:y,logger:a.logger()});p=t=>e.onPresenceStateChange(t),d=t=>e.onHeartbeatIntervalChange(t),g=t=>e.onTokenChange(t),b=t=>e.onUserIdChange(t),y=e,i.subscriptionWorkerUnsubscribeOfflineClients&&"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("pagehide",(t=>{t.persisted||e.terminate()}),{once:!0})}catch(e){a.logger().error("PubNub",(()=>({messageType:"error",message:e})))}else s&&a.logger().warn("PubNub","SharedWorker not supported in this browser. Fallback to the original transport.");const m=new he({clientConfiguration:a,tokenManager:u,transport:y});if(super({configuration:a,transport:m,cryptography:h,tokenManager:u,crypto:l}),this.File=o,this.onHeartbeatIntervalChange=d,this.onAuthenticationChange=g,this.onPresenceStateChange=p,this.onUserIdChange=b,y instanceof H){y.emitStatus=this.emitStatus.bind(this);const e=this.disconnect.bind(this);this.disconnect=t=>{y.disconnect(),e()}}(null===(t=e.listenToBrowserNetworkEvents)||void 0===t||t)&&"undefined"!=typeof window&&window.addEventListener&&(window.addEventListener("offline",(()=>{this.networkDownDetected()})),window.addEventListener("online",(()=>{this.networkUpDetected()})))}networkDownDetected(){this.logger.debug("PubNub","Network down detected"),this.emitStatus({category:Nn.CATEGORIES.PNNetworkDownCategory}),this._configuration.restore?this.disconnect(!0):this.destroy(!0)}networkUpDetected(){this.logger.debug("PubNub","Network up detected"),this.emitStatus({category:Nn.CATEGORIES.PNNetworkUpCategory}),this.reconnect()}}return Nn.CryptoModule=F,Nn})); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).PubNub=t()}(this,(function(){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var s={exports:{}};!function(t){!function(e,s){var n=Math.pow(2,-24),i=Math.pow(2,32),r=Math.pow(2,53);var a={encode:function(e){var t,n=new ArrayBuffer(256),a=new DataView(n),o=0;function c(e){for(var s=n.byteLength,i=o+e;s>2,u=0;u>6),i.push(128|63&a)):a<55296?(i.push(224|a>>12),i.push(128|a>>6&63),i.push(128|63&a)):(a=(1023&a)<<10,a|=1023&t.charCodeAt(++n),a+=65536,i.push(240|a>>18),i.push(128|a>>12&63),i.push(128|a>>6&63),i.push(128|63&a))}return d(3,i.length),h(i);default:var p;if(Array.isArray(t))for(d(4,p=t.length),n=0;n>5!==e)throw"Invalid indefinite length element";return s}function y(e,t){for(var s=0;s>10),e.push(56320|1023&n))}}"function"!=typeof t&&(t=function(e){return e}),"function"!=typeof r&&(r=function(){return s});var m=function e(){var i,d,m=l(),f=m>>5,v=31&m;if(7===f)switch(v){case 25:return function(){var e=new ArrayBuffer(4),t=new DataView(e),s=h(),i=32768&s,r=31744&s,a=1023&s;if(31744===r)r=261120;else if(0!==r)r+=114688;else if(0!==a)return a*n;return t.setUint32(0,i<<16|r<<13|a<<13),t.getFloat32(0)}();case 26:return c(a.getFloat32(o),4);case 27:return c(a.getFloat64(o),8)}if((d=g(v))<0&&(f<2||6=0;)S+=d,O.push(u(d));var j=new Uint8Array(S),w=0;for(i=0;i=0;)y(k,d);else y(k,d);return String.fromCharCode.apply(null,k);case 4:var P;if(d<0)for(P=[];!p();)P.push(e());else for(P=new Array(d),i=0;ie.toString())).join(", ")}]}`}}h.encoder=new TextEncoder,h.decoder=new TextDecoder;class d{static create(e){return new d(e)}constructor(e){let t,s,n,i;if(e instanceof File)i=e,n=e.name,s=e.type,t=e.size;else if("data"in e){const r=e.data;s=e.mimeType,n=e.name,i=new File([r],n,{type:s}),t=i.size}if(void 0===i)throw new Error("Couldn't construct a file out of supplied options.");if(void 0===n)throw new Error("Couldn't guess filename out of the options. Please provide one.");t&&(this.contentLength=t),this.mimeType=s,this.data=i,this.name=n}toBuffer(){return a(this,void 0,void 0,(function*(){throw new Error("This feature is only supported in Node.js environments.")}))}toArrayBuffer(){return a(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{const s=new FileReader;s.addEventListener("load",(()=>{if(s.result instanceof ArrayBuffer)return e(s.result)})),s.addEventListener("error",(()=>t(s.error))),s.readAsArrayBuffer(this.data)}))}))}toString(){return a(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{const s=new FileReader;s.addEventListener("load",(()=>{if("string"==typeof s.result)return e(s.result)})),s.addEventListener("error",(()=>{t(s.error)})),s.readAsBinaryString(this.data)}))}))}toStream(){return a(this,void 0,void 0,(function*(){throw new Error("This feature is only supported in Node.js environments.")}))}toFile(){return a(this,void 0,void 0,(function*(){return this.data}))}toFileUri(){return a(this,void 0,void 0,(function*(){throw new Error("This feature is only supported in React Native environments.")}))}toBlob(){return a(this,void 0,void 0,(function*(){return this.data}))}}d.supportsBlob="undefined"!=typeof Blob,d.supportsFile="undefined"!=typeof File,d.supportsBuffer=!1,d.supportsStream=!1,d.supportsString=!0,d.supportsArrayBuffer=!0,d.supportsEncryptFile=!0,d.supportsFileUri=!1;function p(e){const t=e.replace(/==?$/,""),s=Math.floor(t.length/4*3),n=new ArrayBuffer(s),i=new Uint8Array(n);let r=0;function a(){const e=t.charAt(r++),s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(e);if(-1===s)throw new Error(`Illegal character at ${r}: ${t.charAt(r-1)}`);return s}for(let e=0;e>4,c=(15&s)<<4|n>>2,u=(3&n)<<6|r;i[e]=o,64!=n&&(i[e+1]=c),64!=r&&(i[e+2]=u)}return n}function g(e){let t="";const s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=new Uint8Array(e),i=n.byteLength,r=i%3,a=i-r;let o,c,u,l,h;for(let e=0;e>18,c=(258048&h)>>12,u=(4032&h)>>6,l=63&h,t+=s[o]+s[c]+s[u]+s[l];return 1==r?(h=n[a],o=(252&h)>>2,c=(3&h)<<4,t+=s[o]+s[c]+"=="):2==r&&(h=n[a]<<8|n[a+1],o=(64512&h)>>10,c=(1008&h)>>4,u=(15&h)<<2,t+=s[o]+s[c]+s[u]+"="),t}let b,y=!1;const m=e=>Array.from(e,(e=>e.toString(16).padStart(2,"0"))).join(""),f=e=>e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);class v{constructor(){this.keys=new Map}sha256(e){return a(this,void 0,void 0,(function*(){return new Uint8Array(yield this.subtle.digest("SHA-256",e))}))}aesCbcEncrypt(e,t,s){return a(this,void 0,void 0,(function*(){const n=yield this.importKey(e,"AES-CBC");return new Uint8Array(yield this.subtle.encrypt({name:"AES-CBC",iv:t},n,s))}))}aesCbcDecrypt(e,t,s){return a(this,void 0,void 0,(function*(){const n=yield this.importKey(e,"AES-CBC");return new Uint8Array(yield this.subtle.decrypt({name:"AES-CBC",iv:t},n,s))}))}aesGcmDecrypt(e,t,s){return a(this,void 0,void 0,(function*(){const n=yield this.importKey(e,"AES-GCM");return new Uint8Array(yield this.subtle.decrypt({name:"AES-GCM",iv:t,tagLength:128},n,s))}))}randomBytes(e){return crypto.getRandomValues(new Uint8Array(e))}get subtle(){if("undefined"==typeof crypto||!crypto.subtle)throw new c("Crypto error: crypto.subtle is unavailable. Encryption requires a secure context (HTTPS or localhost).");return crypto.subtle}importKey(e,t){const s=`${t}:${m(e)}`;let n=this.keys.get(s);if(!n){this.keys.size>=v.MAX_CACHED_KEYS&&this.keys.delete(this.keys.keys().next().value);const i="AES-CBC"===t?["encrypt","decrypt"]:["decrypt"];n=this.subtle.importKey("raw",e,t,!1,i),n.catch((()=>this.keys.delete(s))),this.keys.set(s,n)}return n}}v.MAX_CACHED_KEYS=8;class O{constructor({cipherKey:e}){this.cipherKey=e}encrypt(e){return a(this,void 0,void 0,(function*(){const t="string"==typeof e?O.encoder.encode(e):new Uint8Array(e);if(0===t.byteLength)throw new Error("encryption error. empty content");return this.encryptFileData(t)}))}encryptFileData(e){return a(this,void 0,void 0,(function*(){const t=this.getIv(),s=yield O.primitives.aesCbcEncrypt(yield this.getKey(),t,e instanceof Uint8Array?e:new Uint8Array(e));return{data:f(s),metadata:t.buffer}}))}decrypt(e){return a(this,void 0,void 0,(function*(){try{return yield this.decryptFileData(e)}catch(e){if(e instanceof Error&&e.message.startsWith("Decryption error"))throw e;throw new Error(`Decryption error: ${e instanceof Error?e.message:"unable to decrypt data"}`)}}))}decryptFileData(e){return a(this,void 0,void 0,(function*(){if("string"==typeof e.data)throw new Error("Decryption error: data for decryption should be ArrayBuffed.");const t=yield O.primitives.aesCbcDecrypt(yield this.getKey(),new Uint8Array(e.metadata),new Uint8Array(e.data));return f(t)}))}get identifier(){return"ACRH"}getIv(){return O.primitives.randomBytes(O.BLOCK_SIZE)}getKey(){return this.key||(this.key=O.primitives.sha256(O.encoder.encode(this.cipherKey)),this.key.catch((()=>this.key=void 0))),this.key}toString(){return"AesCbcCryptor {}"}}O.BLOCK_SIZE=16,O.encoder=new TextEncoder,O.decoder=new TextDecoder,O.primitives=new v;class S{constructor({cipherKey:e}){this.cipherKey=e}encrypt(e){throw new Error(S.ENCRYPTION_ERROR)}encryptFileData(e){return a(this,void 0,void 0,(function*(){throw new Error(S.ENCRYPTION_ERROR)}))}decrypt(e){return a(this,void 0,void 0,(function*(){return this.decryptFileData(e)}))}decryptFileData(e){return a(this,void 0,void 0,(function*(){if("string"==typeof e.data)throw new Error("Decryption error: data for decryption should be ArrayBuffed.");if(!e.metadata||e.metadata.byteLength!==S.METADATA_LENGTH)throw new Error("Decryption error: invalid metadata");if(e.data.byteLength<=S.TAG_LENGTH)throw new Error("Decryption error: empty content");try{const t=yield S.primitives.aesGcmDecrypt(yield this.getKey(),new Uint8Array(e.metadata),new Uint8Array(e.data));return f(t)}catch(e){throw new Error(S.DECRYPTION_ERROR)}}))}get identifier(){return S.IDENTIFIER}getKey(){return this.key||(this.key=S.primitives.sha256(S.encoder.encode(this.cipherKey)),this.key.catch((()=>this.key=void 0))),this.key}toString(){return"AesGcmCryptor {}"}}S.IDENTIFIER="ACGM",S.NONCE_LENGTH=12,S.TAG_LENGTH=16,S.METADATA_LENGTH=S.NONCE_LENGTH,S.DECRYPTION_ERROR="Decryption error: unable to decrypt data",S.ENCRYPTION_ERROR="Encryption error: AES-GCM cryptor (ACGM) supports decryption only.",S.encoder=new TextEncoder,S.primitives=new v;const j=new Set(["authKey","authKeys","secretKey","cipherKey","token","auth"]),w=e=>j.has(e),k=e=>encodeURIComponent(e).replace(/[!~*'()]/g,(e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)),P=(e,t)=>{const s=e.map((e=>k(e)));return s.length?s.join(","):null!=t?t:""},C=(e,t)=>{const s=Object.fromEntries(t.map((e=>[e,!1])));return e.filter((e=>!(t.includes(e)&&!s[e])||(s[e]=!0,!1)))},N=(e,t)=>[...e].filter((s=>t.includes(s)&&e.indexOf(s)===e.lastIndexOf(s)&&t.indexOf(s)===t.lastIndexOf(s))),E=e=>Object.keys(e).map((t=>{const s=e[t];return Array.isArray(s)?s.map((e=>`${t}=${k(e)}`)).join("&"):`${t}=${k(s)}`})).join("&"),T=(e,t)=>{if("0"===t||"0"===e)return;const s=R(`${Date.now()}0000`,t,!1);return R(e,s,!0)},I=(e,t,s)=>{if(e&&0!==e.length){if(t&&t.length>0&&"0"!==t){const n=R(e,t,!1);return R(null!=s?s:`${Date.now()}0000`,n.replace("-",""),Number(n)<0)}return s&&s.length>0&&"0"!==s?s:`${Date.now()}0000`}},R=(e,t,s)=>{t.startsWith("-")&&(t=t.replace("-",""),s=!1),t=t.padStart(17,"0");const n=e.slice(0,10),i=e.slice(10,17),r=t.slice(0,10),a=t.slice(10,17);let o=Number(n),c=Number(i);return o+=Number(r)*(s?1:-1),c+=Number(a)*(s?1:-1),c>=1e7?(o+=Math.floor(c/1e7),c%=1e7):c<0?o>0?(o-=1,c+=1e7):o<0&&(c*=-1):o<0&&c>0&&(o+=1,c=1e7-c),0!==o?`${o}${`${c}`.padStart(7,"0")}`:`${c}`},M=e=>{const t="string"!=typeof e?JSON.stringify(e):e,s=new Uint32Array(1);let n=0,i=t.length;for(;i-- >0;)s[0]=(s[0]<<5)-s[0]+t.charCodeAt(n++);return s[0].toString(16).padStart(8,"0")},D=(e,t)=>a(void 0,void 0,void 0,(function*(){const s=yield e.encrypt(JSON.stringify(t));return JSON.stringify("string"==typeof s?s:g(s))}));class _{constructor(e,t){this.configuration=e,this.primitives=t,this.logger=e.logger}set logger(e){this._logger=e,this.logger&&this.logger.debug("Crypto",(()=>({messageType:"object",message:this.configuration,details:"Create with configuration:",ignoredKeys:(e,t)=>"function"==typeof t[e]||"logger"===e||w(e)})))}get logger(){return this._logger}encrypt(e,t){return a(this,void 0,void 0,(function*(){return this.configuration.customEncrypt?(this.logger&&this.logger.warn("Crypto","'customEncrypt' is deprecated. Consult docs for better alternative."),this.configuration.customEncrypt(e)):this.pnEncrypt("string"==typeof e?e:JSON.stringify(e),t)}))}decrypt(e,t){return a(this,void 0,void 0,(function*(){return this.configuration.customDecrypt?(this.logger&&this.logger.warn("Crypto","'customDecrypt' is deprecated. Consult docs for better alternative."),this.configuration.customDecrypt(e)):this.pnDecrypt(e,t)}))}pnEncrypt(e,t){return a(this,void 0,void 0,(function*(){const s=null!=t?t:this.configuration.cipherKey;if(!s)return e;this.logger&&this.logger.debug("Crypto",(()=>({messageType:"object",message:{data:e},details:"Encrypt with parameters:",ignoredKeys:w})));const n=yield this.getKey(s),i=_.encoder.encode(e);if(this.configuration.useRandomIVs){const e=this.primitives.randomBytes(_.IV_LENGTH),t=yield this.primitives.aesCbcEncrypt(n,e,i);return g(this.concat(e,t))}const r=yield this.primitives.aesCbcEncrypt(n,_.staticIV,i);return g(f(r))||e}))}pnDecrypt(e,t){return a(this,void 0,void 0,(function*(){const s=null!=t?t:this.configuration.cipherKey;if(!s)return e;this.logger&&this.logger.debug("Crypto",(()=>({messageType:"object",message:{data:e},details:"Decrypt with parameters:",ignoredKeys:w})));try{const t=yield this.getKey(s),n=new Uint8Array(p(e));let i=_.staticIV,r=n;this.configuration.useRandomIVs&&(i=n.slice(0,_.IV_LENGTH),r=n.slice(_.IV_LENGTH));const a=yield this.primitives.aesCbcDecrypt(t,i,r);return JSON.parse((e=>{if(!(e=>{const t=e.length;let s=0;for(;s=194&&n<=223)i=1;else if(n>=224&&n<=239)i=2,224===n?r=160:237===n&&(a=159);else{if(!(n>=240&&n<=244))return!1;i=3,240===n?r=144:244===n&&(a=143)}if(s+i>=t)return!1;const o=e[s+1];if(oa)return!1;for(let t=2;t<=i;t++){const n=e[s+t];if(n<128||n>191)return!1}s+=i+1}return!0})(e))throw new Error("Malformed UTF-8 data");b||(b=new TextDecoder("utf-8",{ignoreBOM:!0}),y=0===b.decode(new Uint8Array([239,187,191])).length);const t=b.decode(e);return e.length>=3&&239===e[0]&&187===e[1]&&191===e[2]&&y?`\ufeff${t}`:t})(a))}catch(e){return this.logger&&this.logger.error("Crypto",(()=>({messageType:"error",message:e}))),null}}))}getKey(e){return e!==this.configuration.cipherKey?this.deriveKey(e):(this.configuredKey||(this.configuredKey=this.deriveKey(e),this.configuredKey.catch((()=>this.configuredKey=void 0))),this.configuredKey)}deriveKey(e){return a(this,void 0,void 0,(function*(){const t=yield this.primitives.sha256(_.encoder.encode(e));return _.encoder.encode(m(t).slice(0,32))}))}concat(e,t){const s=new Uint8Array(e.byteLength+t.byteLength);return s.set(e,0),s.set(t,e.byteLength),s.buffer}}_.encoder=new TextEncoder,_.staticIV=(new TextEncoder).encode("0123456789012345"),_.IV_LENGTH=16;class U{encrypt(e,t){return a(this,void 0,void 0,(function*(){if(!(t instanceof ArrayBuffer)&&"string"!=typeof t)throw new Error("Cannot encrypt this file. In browsers file encryption supports only string or ArrayBuffer");const s=yield this.getKey(e);return t instanceof ArrayBuffer?this.encryptArrayBuffer(s,t):this.encryptString(s,t)}))}encryptArrayBuffer(e,t){return a(this,void 0,void 0,(function*(){const s=crypto.getRandomValues(new Uint8Array(16));return this.concatArrayBuffer(s.buffer,yield crypto.subtle.encrypt({name:"AES-CBC",iv:s},e,t))}))}encryptString(e,t){return a(this,void 0,void 0,(function*(){const s=crypto.getRandomValues(new Uint8Array(16)),n=U.encoder.encode(t).buffer,i=yield crypto.subtle.encrypt({name:"AES-CBC",iv:s},e,n),r=this.concatArrayBuffer(s.buffer,i);return U.decoder.decode(r)}))}encryptFile(e,t,s){return a(this,void 0,void 0,(function*(){var n,i;if((null!==(n=t.contentLength)&&void 0!==n?n:0)<=0)throw new Error("encryption error. empty content");const r=yield this.getKey(e),a=yield t.toArrayBuffer(),o=yield this.encryptArrayBuffer(r,a);return s.create({name:t.name,mimeType:null!==(i=t.mimeType)&&void 0!==i?i:"application/octet-stream",data:o})}))}decrypt(e,t){return a(this,void 0,void 0,(function*(){if(!(t instanceof ArrayBuffer)&&"string"!=typeof t)throw new Error("Cannot decrypt this file. In browsers file decryption supports only string or ArrayBuffer");const s=yield this.getKey(e);return t instanceof ArrayBuffer?this.decryptArrayBuffer(s,t):this.decryptString(s,t)}))}decryptArrayBuffer(e,t){return a(this,void 0,void 0,(function*(){const s=t.slice(0,16);if(t.slice(U.IV_LENGTH).byteLength<=0)throw new Error("decryption error: empty content");return yield crypto.subtle.decrypt({name:"AES-CBC",iv:s},e,t.slice(U.IV_LENGTH))}))}decryptString(e,t){return a(this,void 0,void 0,(function*(){const s=U.encoder.encode(t).buffer,n=s.slice(0,16),i=s.slice(16),r=yield crypto.subtle.decrypt({name:"AES-CBC",iv:n},e,i);return U.decoder.decode(r)}))}decryptFile(e,t,s){return a(this,void 0,void 0,(function*(){const n=yield this.getKey(e),i=yield t.toArrayBuffer(),r=yield this.decryptArrayBuffer(n,i);return s.create({name:t.name,mimeType:t.mimeType,data:r})}))}getKey(e){return a(this,void 0,void 0,(function*(){const t=yield crypto.subtle.digest("SHA-256",U.encoder.encode(e)),s=Array.from(new Uint8Array(t)).map((e=>e.toString(16).padStart(2,"0"))).join(""),n=U.encoder.encode(s.slice(0,32)).buffer;return crypto.subtle.importKey("raw",n,"AES-CBC",!0,["encrypt","decrypt"])}))}concatArrayBuffer(e,t){const s=new Uint8Array(e.byteLength+t.byteLength);return s.set(new Uint8Array(e),0),s.set(new Uint8Array(t),e.byteLength),s.buffer}}U.IV_LENGTH=16,U.encoder=new TextEncoder,U.decoder=new TextDecoder;class A{constructor(e){this.config=e,this.cryptor=new _(Object.assign({},e),new v),this.fileCryptor=new U}set logger(e){this.cryptor.logger=e,!1===this.config.useRandomIVs&&e.warn("LegacyCryptor","Setting 'useRandomIVs' to false is insecure and should only be used to support legacy clients.\n Do not disable random IVs in new applications.")}encrypt(e){return a(this,void 0,void 0,(function*(){const t="string"==typeof e?e:A.decoder.decode(e);return{data:yield this.cryptor.encrypt(t),metadata:null}}))}encryptFile(e,t){return a(this,void 0,void 0,(function*(){var s;if(!this.config.cipherKey)throw new c("File encryption error: cipher key not set.");return this.fileCryptor.encryptFile(null===(s=this.config)||void 0===s?void 0:s.cipherKey,e,t)}))}decrypt(e){return a(this,void 0,void 0,(function*(){const t="string"==typeof e.data?e.data:g(e.data);return this.cryptor.decrypt(t)}))}decryptFile(e,t){return a(this,void 0,void 0,(function*(){if(!this.config.cipherKey)throw new c("File encryption error: cipher key not set.");return this.fileCryptor.decryptFile(this.config.cipherKey,e,t)}))}get identifier(){return""}toString(){return`LegacyCryptor { ${Object.entries(this.config).reduce(((e,[t,s])=>("logger"===t||w(t)||e.push(`${t}: ${"function"==typeof s?"":s}`),e)),[]).join(", ")} }`}}A.encoder=new TextEncoder,A.decoder=new TextDecoder;class $ extends h{set logger(e){if(this.defaultCryptor.identifier===$.LEGACY_IDENTIFIER)e.warn("CryptoModule","'legacyCryptoModule' is deprecated. Use 'aesCbcCryptoModule' instead for new applications."),this.defaultCryptor.logger=e;else{const t=this.cryptors.find((e=>e.identifier===$.LEGACY_IDENTIFIER));t&&(t.logger=e)}}static legacyCryptoModule(e){var t;if(!e.cipherKey)throw new c("Crypto module error: cipher key not set.");return e.logger&&(e.logger.warn("CryptoModule","'legacyCryptoModule' is deprecated. Use 'aesCbcCryptoModule' instead for new applications."),!1===e.useRandomIVs&&e.logger.warn("CryptoModule","Setting 'useRandomIVs' to false is insecure and should only be used to support legacy clients.\n Do not disable random IVs in new applications.")),new $({default:new A(Object.assign(Object.assign({},e),{useRandomIVs:null===(t=e.useRandomIVs)||void 0===t||t})),cryptors:[new O({cipherKey:e.cipherKey}),new S({cipherKey:e.cipherKey})]})}static aesCbcCryptoModule(e){var t;if(!e.cipherKey)throw new c("Crypto module error: cipher key not set.");return new $({default:new O({cipherKey:e.cipherKey}),cryptors:[new A(Object.assign(Object.assign({},e),{useRandomIVs:null===(t=e.useRandomIVs)||void 0===t||t})),new S({cipherKey:e.cipherKey})]})}static aesGcmCryptor(e){if(!e.cipherKey)throw new c("Crypto module error: cipher key not set.");return new S({cipherKey:e.cipherKey})}static withDefaultCryptor(e){return new this({default:e})}encrypt(e){return a(this,void 0,void 0,(function*(){const t=e instanceof ArrayBuffer&&this.defaultCryptor.identifier===$.LEGACY_IDENTIFIER?yield this.defaultCryptor.encrypt($.decoder.decode(e)):yield this.defaultCryptor.encrypt(e);if(!t.metadata)return t.data;if("string"==typeof t.data)throw new Error("Encryption error: encrypted data should be ArrayBuffed.");const s=this.getHeaderData(t);return this.concatArrayBuffer(s,t.data)}))}encryptFile(e,t){return a(this,void 0,void 0,(function*(){if(this.defaultCryptor.identifier===F.LEGACY_IDENTIFIER)return this.defaultCryptor.encryptFile(e,t);const s=yield this.getFileData(e),n=yield this.defaultCryptor.encryptFileData(s);if("string"==typeof n.data)throw new Error("Encryption error: encrypted data should be ArrayBuffed.");return t.create({name:e.name,mimeType:"application/octet-stream",data:this.concatArrayBuffer(this.getHeaderData(n),n.data)})}))}decrypt(e){return a(this,void 0,void 0,(function*(){const t="string"==typeof e?p(e):e,s=F.tryParse(t),n=this.getCryptor(s),i=s.length>0?t.slice(s.length-s.metadataLength,s.length):null;if(t.slice(s.length).byteLength<=0)throw new Error("Decryption error: empty content");return n.decrypt({data:t.slice(s.length),metadata:i})}))}decryptFile(e,t){return a(this,void 0,void 0,(function*(){const s=yield e.data.arrayBuffer(),n=F.tryParse(s),i=this.getCryptor(n);if((null==i?void 0:i.identifier)===F.LEGACY_IDENTIFIER)return i.decryptFile(e,t);const r=(yield this.getFileData(s)).slice(n.length-n.metadataLength,n.length);return t.create({name:e.name,data:yield i.decryptFileData({data:s.slice(n.length),metadata:r})})}))}getCryptorFromId(e){const t=this.getAllCryptors().find((t=>e===t.identifier));if(t)return t;throw Error("Unknown cryptor error")}getCryptor(e){if("string"==typeof e){const t=this.getAllCryptors().find((t=>t.identifier===e));if(t)return t;throw new Error("Unknown cryptor error")}if(e instanceof x)return this.getCryptorFromId(e.identifier)}getHeaderData(e){if(!e.metadata)return;const t=F.from(this.defaultCryptor.identifier,e.metadata),s=new Uint8Array(t.length);let n=0;return s.set(t.data,n),n+=t.length-e.metadata.byteLength,s.set(new Uint8Array(e.metadata),n),s.buffer}concatArrayBuffer(e,t){const s=new Uint8Array(e.byteLength+t.byteLength);return s.set(new Uint8Array(e),0),s.set(new Uint8Array(t),e.byteLength),s.buffer}getFileData(e){return a(this,void 0,void 0,(function*(){if(e instanceof ArrayBuffer)return e;if(e instanceof d)return e.toArrayBuffer();throw new Error("Cannot decrypt/encrypt file. In browsers file encrypt/decrypt supported for string, ArrayBuffer or Blob")}))}}$.LEGACY_IDENTIFIER="";class F{static from(e,t){if(e!==F.LEGACY_IDENTIFIER)return new x(e,t.byteLength)}static tryParse(e){const t=new Uint8Array(e);let s,n,i=null;if(t.byteLength>=4&&(s=t.slice(0,4),this.decoder.decode(s)!==F.SENTINEL))return $.LEGACY_IDENTIFIER;if(!(t.byteLength>=5))throw new Error("Decryption error: invalid header version");if(i=t[4],i>F.MAX_VERSION)throw new Error("Decryption error: Unknown cryptor error");let r=5+F.IDENTIFIER_LENGTH;if(!(t.byteLength>=r))throw new Error("Decryption error: invalid crypto identifier");n=t.slice(5,r);let a=null;if(!(t.byteLength>=r+1))throw new Error("Decryption error: invalid metadata length");return a=t[r],r+=1,255===a&&t.byteLength>=r+2&&(a=new Uint16Array(t.slice(r,r+2)).reduce(((e,t)=>(e<<8)+t),0)),new x(this.decoder.decode(n),a)}}F.SENTINEL="PNED",F.LEGACY_IDENTIFIER="",F.IDENTIFIER_LENGTH=4,F.VERSION=1,F.MAX_VERSION=1,F.decoder=new TextDecoder;class x{constructor(e,t){this._identifier=e,this._metadataLength=t}get identifier(){return this._identifier}set identifier(e){this._identifier=e}get metadataLength(){return this._metadataLength}set metadataLength(e){this._metadataLength=e}get version(){return F.VERSION}get length(){return F.SENTINEL.length+1+F.IDENTIFIER_LENGTH+(this.metadataLength<255?1:3)+this.metadataLength}get data(){let e=0;const t=new Uint8Array(this.length),s=new TextEncoder;t.set(s.encode(F.SENTINEL)),e+=F.SENTINEL.length,t[e]=this.version,e++,this.identifier&&t.set(s.encode(this.identifier),e);const n=this.metadataLength;return e+=F.IDENTIFIER_LENGTH,n<255?t[e]=n:t.set([255,n>>8,255&n],e),t}}x.IDENTIFIER_LENGTH=4,x.SENTINEL="PNED";class q extends Error{static create(e,t){return q.isErrorObject(e)?q.createFromError(e):q.createFromServiceResponse(e,t)}static createFromError(e){let t=o.PNUnknownCategory,s="Unknown error",n="Error";if(!e)return new q(s,t,0);if(e instanceof q)return e;if(q.isErrorObject(e)&&(s=e.message,n=e.name),"AbortError"===n||-1!==s.indexOf("Aborted"))t=o.PNCancelledCategory,s="Request cancelled";else if(-1!==s.toLowerCase().indexOf("timeout"))t=o.PNTimeoutCategory,s="Request timeout";else if(-1!==s.toLowerCase().indexOf("network"))t=o.PNNetworkIssuesCategory,s="Network issues";else if("TypeError"===n)t=-1!==s.indexOf("Load failed")||-1!=s.indexOf("Failed to fetch")?o.PNNetworkIssuesCategory:o.PNBadRequestCategory;else if("FetchError"===n){const n=e.code;["ECONNREFUSED","ENETUNREACH","ENOTFOUND","ECONNRESET","EAI_AGAIN"].includes(n)&&(t=o.PNNetworkIssuesCategory),"ECONNREFUSED"===n?s="Connection refused":"ENETUNREACH"===n?s="Network not reachable":"ENOTFOUND"===n?s="Server not found":"ECONNRESET"===n?s="Connection reset by peer":"EAI_AGAIN"===n?s="Name resolution error":"ETIMEDOUT"===n?(t=o.PNTimeoutCategory,s="Request timeout"):s=`Unknown system error: ${e}`}else"Request timeout"===s&&(t=o.PNTimeoutCategory);return new q(s,t,0,e)}static createFromServiceResponse(e,t){let s,n=o.PNUnknownCategory,i="Unknown error",{status:r}=e;if(null!=t||(t=e.body),402===r?i="Not available for used key set. Contact support@pubnub.com":404===r?i="Resource not found":400===r?(n=o.PNBadRequestCategory,i="Bad request"):403===r?(n=o.PNAccessDeniedCategory,i="Access denied"):r>=500&&(n=o.PNServerErrorCategory,i="Internal server error"),"object"==typeof e&&0===Object.keys(e).length&&(n=o.PNMalformedResponseCategory,i="Malformed response (network issues)",r=400),t&&t.byteLength>0){const n=(new TextDecoder).decode(t);if(-1!==e.headers["content-type"].indexOf("text/javascript")||-1!==e.headers["content-type"].indexOf("application/json"))try{const e=JSON.parse(n);"object"==typeof e&&(Array.isArray(e)?"number"==typeof e[0]&&0===e[0]&&e.length>1&&"string"==typeof e[1]&&(s=e[1]):("error"in e&&(1===e.error||!0===e.error)&&"status"in e&&"number"==typeof e.status&&"message"in e&&"service"in e?(s=e,r=e.status):s=e,"error"in e&&e.error instanceof Error&&(s=e.error)))}catch(e){s=n}else if(-1!==e.headers["content-type"].indexOf("xml")){const e=/(.*)<\/Message>/gi.exec(n);i=e?`Upload to bucket failed: ${e[1]}`:"Upload to bucket failed."}else s=n}return new q(i,n,r,s)}constructor(e,t,s,n){super(e),this.category=t,this.statusCode=s,this.errorData=n,this.name="PubNubAPIError"}toStatus(e){return{error:!0,category:this.category,operation:e,statusCode:this.statusCode,errorData:this.errorData,toJSON:function(){let e;const t=this.errorData;if(t)try{if("object"==typeof t){const s=Object.assign(Object.assign(Object.assign(Object.assign({},"name"in t?{name:t.name}:{}),"message"in t?{message:t.message}:{}),"stack"in t?{stack:t.stack}:{}),t);e=JSON.parse(JSON.stringify(s,q.circularReplacer()))}else e=t}catch(t){e={error:"Could not serialize the error object"}}const s=r(this,["toJSON"]);return JSON.stringify(Object.assign(Object.assign({},s),{errorData:e}))}}}toPubNubError(e,t){return new c(null!=t?t:this.message,this.toStatus(e))}static circularReplacer(){const e=new WeakSet;return function(t,s){if("object"==typeof s&&null!==s){if(e.has(s))return"[Circular]";e.add(s)}return s}}static isErrorObject(e){return!(!e||"object"!=typeof e)&&(e instanceof Error||("name"in e&&"message"in e&&"string"==typeof e.name&&"string"==typeof e.message||"[object Error]"===Object.prototype.toString.call(e)))}}var G;!function(e){e.PNPublishOperation="PNPublishOperation",e.PNSignalOperation="PNSignalOperation",e.PNSubscribeOperation="PNSubscribeOperation",e.PNUnsubscribeOperation="PNUnsubscribeOperation",e.PNWhereNowOperation="PNWhereNowOperation",e.PNHereNowOperation="PNHereNowOperation",e.PNGlobalHereNowOperation="PNGlobalHereNowOperation",e.PNSetStateOperation="PNSetStateOperation",e.PNGetStateOperation="PNGetStateOperation",e.PNHeartbeatOperation="PNHeartbeatOperation",e.PNAddMessageActionOperation="PNAddActionOperation",e.PNRemoveMessageActionOperation="PNRemoveMessageActionOperation",e.PNGetMessageActionsOperation="PNGetMessageActionsOperation",e.PNTimeOperation="PNTimeOperation",e.PNHistoryOperation="PNHistoryOperation",e.PNDeleteMessagesOperation="PNDeleteMessagesOperation",e.PNFetchMessagesOperation="PNFetchMessagesOperation",e.PNMessageCounts="PNMessageCountsOperation",e.PNGetAllUUIDMetadataOperation="PNGetAllUUIDMetadataOperation",e.PNGetUUIDMetadataOperation="PNGetUUIDMetadataOperation",e.PNSetUUIDMetadataOperation="PNSetUUIDMetadataOperation",e.PNRemoveUUIDMetadataOperation="PNRemoveUUIDMetadataOperation",e.PNGetAllChannelMetadataOperation="PNGetAllChannelMetadataOperation",e.PNGetChannelMetadataOperation="PNGetChannelMetadataOperation",e.PNSetChannelMetadataOperation="PNSetChannelMetadataOperation",e.PNRemoveChannelMetadataOperation="PNRemoveChannelMetadataOperation",e.PNGetMembersOperation="PNGetMembersOperation",e.PNSetMembersOperation="PNSetMembersOperation",e.PNGetMembershipsOperation="PNGetMembershipsOperation",e.PNSetMembershipsOperation="PNSetMembershipsOperation",e.PNCreateDataSyncEntityOperation="PNCreateDataSyncEntityOperation",e.PNGetDataSyncEntityOperation="PNGetDataSyncEntityOperation",e.PNGetDataSyncEntitiesOperation="PNGetDataSyncEntitiesOperation",e.PNSetDataSyncEntityOperation="PNSetDataSyncEntityOperation",e.PNUpdateDataSyncEntityOperation="PNUpdateDataSyncEntityOperation",e.PNRemoveDataSyncEntityOperation="PNRemoveDataSyncEntityOperation",e.PNCreateDataSyncRelationshipOperation="PNCreateDataSyncRelationshipOperation",e.PNGetDataSyncRelationshipOperation="PNGetDataSyncRelationshipOperation",e.PNGetDataSyncRelationshipsOperation="PNGetDataSyncRelationshipsOperation",e.PNSetDataSyncRelationshipOperation="PNSetDataSyncRelationshipOperation",e.PNUpdateDataSyncRelationshipOperation="PNUpdateDataSyncRelationshipOperation",e.PNRemoveDataSyncRelationshipOperation="PNRemoveDataSyncRelationshipOperation",e.PNCreateDataSyncUserOperation="PNCreateDataSyncUserOperation",e.PNGetDataSyncUserOperation="PNGetDataSyncUserOperation",e.PNGetDataSyncUsersOperation="PNGetDataSyncUsersOperation",e.PNSetDataSyncUserOperation="PNSetDataSyncUserOperation",e.PNUpdateDataSyncUserOperation="PNUpdateDataSyncUserOperation",e.PNRemoveDataSyncUserOperation="PNRemoveDataSyncUserOperation",e.PNCreateDataSyncChannelOperation="PNCreateDataSyncChannelOperation",e.PNGetDataSyncChannelOperation="PNGetDataSyncChannelOperation",e.PNGetDataSyncChannelsOperation="PNGetDataSyncChannelsOperation",e.PNSetDataSyncChannelOperation="PNSetDataSyncChannelOperation",e.PNUpdateDataSyncChannelOperation="PNUpdateDataSyncChannelOperation",e.PNRemoveDataSyncChannelOperation="PNRemoveDataSyncChannelOperation",e.PNCreateDataSyncMembershipOperation="PNCreateDataSyncMembershipOperation",e.PNGetDataSyncMembershipOperation="PNGetDataSyncMembershipOperation",e.PNGetDataSyncMembershipsOperation="PNGetDataSyncMembershipsOperation",e.PNSetDataSyncMembershipOperation="PNSetDataSyncMembershipOperation",e.PNUpdateDataSyncMembershipOperation="PNUpdateDataSyncMembershipOperation",e.PNRemoveDataSyncMembershipOperation="PNRemoveDataSyncMembershipOperation",e.PNListFilesOperation="PNListFilesOperation",e.PNGenerateUploadUrlOperation="PNGenerateUploadUrlOperation",e.PNPublishFileOperation="PNPublishFileOperation",e.PNPublishFileMessageOperation="PNPublishFileMessageOperation",e.PNGetFileUrlOperation="PNGetFileUrlOperation",e.PNDownloadFileOperation="PNDownloadFileOperation",e.PNDeleteFileOperation="PNDeleteFileOperation",e.PNAddPushNotificationEnabledChannelsOperation="PNAddPushNotificationEnabledChannelsOperation",e.PNRemovePushNotificationEnabledChannelsOperation="PNRemovePushNotificationEnabledChannelsOperation",e.PNPushNotificationEnabledChannelsOperation="PNPushNotificationEnabledChannelsOperation",e.PNRemoveAllPushNotificationsOperation="PNRemoveAllPushNotificationsOperation",e.PNChannelGroupsOperation="PNChannelGroupsOperation",e.PNRemoveGroupOperation="PNRemoveGroupOperation",e.PNChannelsForGroupOperation="PNChannelsForGroupOperation",e.PNAddChannelsToGroupOperation="PNAddChannelsToGroupOperation",e.PNRemoveChannelsFromGroupOperation="PNRemoveChannelsFromGroupOperation",e.PNAccessManagerGrant="PNAccessManagerGrant",e.PNAccessManagerGrantToken="PNAccessManagerGrantToken",e.PNAccessManagerAudit="PNAccessManagerAudit",e.PNAccessManagerRevokeToken="PNAccessManagerRevokeToken",e.PNHandshakeOperation="PNHandshakeOperation",e.PNReceiveMessagesOperation="PNReceiveMessagesOperation"}(G||(G={}));var L=G;class K{constructor(e){this.configuration=e,this.subscriptionWorkerReady=!1,this.accessTokensMap={},this.workerEventsQueue=[],this.callbacks=new Map,this.setupSubscriptionWorker()}set emitStatus(e){this._emitStatus=e}onUserIdChange(e){this.configuration.userId=e,this.scheduleEventPost({type:"client-update",heartbeatInterval:this.configuration.heartbeatInterval,clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,workerLogLevel:this.configuration.workerLogLevel})}onPresenceStateChange(e){this.scheduleEventPost({type:"client-presence-state-update",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,workerLogLevel:this.configuration.workerLogLevel,state:e})}onHeartbeatIntervalChange(e){this.configuration.heartbeatInterval=e,this.scheduleEventPost({type:"client-update",heartbeatInterval:this.configuration.heartbeatInterval,clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,workerLogLevel:this.configuration.workerLogLevel})}onTokenChange(e){const t={type:"client-update",heartbeatInterval:this.configuration.heartbeatInterval,clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,workerLogLevel:this.configuration.workerLogLevel};this.parsedAccessToken(e).then((s=>{t.preProcessedToken=s,t.accessToken=e})).then((()=>this.scheduleEventPost(t)))}disconnect(){this.scheduleEventPost({type:"client-disconnect",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,workerLogLevel:this.configuration.workerLogLevel})}terminate(){this.scheduleEventPost({type:"client-unregister",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,workerLogLevel:this.configuration.workerLogLevel})}makeSendable(e){if(!e.path.startsWith("/v2/subscribe")&&!e.path.endsWith("/heartbeat")&&!e.path.endsWith("/leave"))return this.configuration.transport.makeSendable(e);let t;this.configuration.logger.debug("SubscriptionWorkerMiddleware","Process request with SharedWorker transport.");const s={type:"send-request",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,request:e,workerLogLevel:this.configuration.workerLogLevel};return e.cancellable&&(t={abort:()=>{const t={type:"cancel-request",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,identifier:e.identifier,workerLogLevel:this.configuration.workerLogLevel};this.scheduleEventPost(t)}}),[new Promise(((t,n)=>{this.callbacks.set(e.identifier,{resolve:t,reject:n}),this.parsedAccessTokenForRequest(e).then((e=>s.preProcessedToken=e)).then((()=>this.scheduleEventPost(s)))})),t]}request(e){return e}scheduleEventPost(e,t=!1){const s=this.sharedSubscriptionWorker;s?s.port.postMessage(e):t?this.workerEventsQueue.splice(0,0,e):this.workerEventsQueue.push(e)}flushScheduledEvents(){const e=this.sharedSubscriptionWorker;if(!e||0===this.workerEventsQueue.length)return;const t=[];for(let e=0;e!t.includes(e))),this.workerEventsQueue.forEach((t=>e.port.postMessage(t))),this.workerEventsQueue=[]}get sharedSubscriptionWorker(){return this.subscriptionWorkerReady?this.subscriptionWorker:null}setupSubscriptionWorker(){if("undefined"!=typeof SharedWorker){try{this.subscriptionWorker=new SharedWorker(this.configuration.workerUrl,`/pubnub-${this.configuration.sdkVersion}`)}catch(e){throw this.configuration.logger.error("SubscriptionWorkerMiddleware",(()=>({messageType:"error",message:e}))),e}this.subscriptionWorker.port.start(),this.scheduleEventPost({type:"client-register",clientIdentifier:this.configuration.clientIdentifier,subscriptionKey:this.configuration.subscriptionKey,userId:this.configuration.userId,heartbeatInterval:this.configuration.heartbeatInterval,workerOfflineClientsCheckInterval:this.configuration.workerOfflineClientsCheckInterval,workerUnsubscribeOfflineClients:this.configuration.workerUnsubscribeOfflineClients,workerLogLevel:this.configuration.workerLogLevel},!0),this.subscriptionWorker.port.onmessage=e=>this.handleWorkerEvent(e),this.shouldAnnounceNewerSharedWorkerVersionAvailability()&&localStorage.setItem("PNSubscriptionSharedWorkerVersion",this.configuration.sdkVersion),window.addEventListener("storage",(e=>{"PNSubscriptionSharedWorkerVersion"===e.key&&e.newValue&&this._emitStatus&&this.isNewerSharedWorkerVersion(e.newValue)&&this._emitStatus({error:!1,category:o.PNSharedWorkerUpdatedCategory})}))}}handleWorkerEvent(e){const{data:t}=e;if("shared-worker-ping"===t.type||"shared-worker-connected"===t.type||"shared-worker-console-log"===t.type||"shared-worker-console-dir"===t.type||t.clientIdentifier===this.configuration.clientIdentifier)if("shared-worker-connected"===t.type)this.configuration.logger.trace("SharedWorker","Ready for events processing."),this.subscriptionWorkerReady=!0,this.flushScheduledEvents();else if("shared-worker-console-log"===t.type)this.configuration.logger.debug("SharedWorker",(()=>"string"==typeof t.message||"number"==typeof t.message||"boolean"==typeof t.message?{messageType:"text",message:t.message}:t.message));else if("shared-worker-console-dir"===t.type)this.configuration.logger.debug("SharedWorker",(()=>({messageType:"object",message:t.data,details:t.message?t.message:void 0})));else if("shared-worker-ping"===t.type){const{subscriptionKey:e,clientIdentifier:t}=this.configuration;this.scheduleEventPost({type:"client-pong",subscriptionKey:e,clientIdentifier:t,workerLogLevel:this.configuration.workerLogLevel})}else if("request-process-success"===t.type||"request-process-error"===t.type)if(this.callbacks.has(t.identifier)){const{resolve:e,reject:s}=this.callbacks.get(t.identifier);this.callbacks.delete(t.identifier),"request-process-success"===t.type?e({status:t.response.status,url:t.url,headers:t.response.headers,body:t.response.body}):s(this.errorFromRequestSendingError(t))}else this._emitStatus&&t.url.indexOf("/v2/presence")>=0&&t.url.indexOf("/heartbeat")>=0&&("request-process-success"===t.type&&this.configuration.announceSuccessfulHeartbeats?this._emitStatus({statusCode:t.response.status,error:!1,operation:L.PNHeartbeatOperation,category:o.PNAcknowledgmentCategory}):"request-process-error"===t.type&&this.configuration.announceFailedHeartbeats&&this._emitStatus(this.errorFromRequestSendingError(t).toStatus(L.PNHeartbeatOperation)))}parsedAccessTokenForRequest(e){return a(this,void 0,void 0,(function*(){var t;return this.parsedAccessToken(e.queryParameters?null!==(t=e.queryParameters.auth)&&void 0!==t?t:"":void 0)}))}parsedAccessToken(e){return a(this,void 0,void 0,(function*(){if(e)return this.accessTokensMap[e]?this.accessTokensMap[e]:this.stringifyAccessToken(e).then((([t,s])=>{if(t&&s)return(this.accessTokensMap={[e]:{token:s,expiration:t.timestamp+60*t.ttl}})[e]}))}))}stringifyAccessToken(e){return a(this,void 0,void 0,(function*(){if(!this.configuration.tokenManager)return[void 0,void 0];const t=this.configuration.tokenManager.parseToken(e);if(!t)return[void 0,void 0];const s=e=>e?Object.entries(e).sort((([e],[t])=>e.localeCompare(t))).map((([e,t])=>Object.entries(t||{}).sort((([e],[t])=>e.localeCompare(t))).map((([t,s])=>{return`${e}:${t}=${s?(n=s,Object.entries(n).filter((([e,t])=>t)).map((([e])=>e[0])).sort().join("")):""}`;var n})).join(","))).join(";"):"";let n=[s(t.resources),s(t.patterns),t.authorized_uuid].filter(Boolean).join("|");if("undefined"!=typeof crypto&&crypto.subtle){const e=yield crypto.subtle.digest("SHA-256",(new TextEncoder).encode(n));n=String.fromCharCode(...Array.from(new Uint8Array(e)))}return[t,"undefined"!=typeof btoa?btoa(n):n]}))}errorFromRequestSendingError(e){let t=o.PNUnknownCategory,s="Unknown error";if(e.error)"NETWORK_ISSUE"===e.error.type?t=o.PNNetworkIssuesCategory:"TIMEOUT"===e.error.type?t=o.PNTimeoutCategory:"ABORTED"===e.error.type&&(t=o.PNCancelledCategory),s=`${e.error.message} (${e.identifier})`;else if(e.response){const{url:t,response:s}=e;return q.create({url:t,headers:s.headers,body:s.body,status:s.status},s.body)}return new q(s,t,0,new Error(s))}shouldAnnounceNewerSharedWorkerVersionAvailability(){const e=localStorage.getItem("PNSubscriptionSharedWorkerVersion");return!e||!this.isNewerSharedWorkerVersion(e)}isNewerSharedWorkerVersion(e){const[t,s,n]=this.configuration.sdkVersion.split(".").map(Number),[i,r,a]=e.split(".").map(Number);return i>t||r>s||a>n}}function H(e,t=0){const s=e=>"object"==typeof e&&null!==e&&e.constructor===Object,n=e=>"number"==typeof e&&isFinite(e);if(!s(e))return e;const i={};return Object.keys(e).forEach((r=>{const a=(e=>"string"==typeof e||e instanceof String)(r);let o=r;const c=e[r];if(t<2)if(a&&r.indexOf(",")>=0){o=r.split(",").map(Number).reduce(((e,t)=>e+String.fromCharCode(t)),"")}else(n(r)||a&&!isNaN(Number(r)))&&(o=String.fromCharCode(n(r)?r:parseInt(r,10)));i[o]=s(c)?H(c,t+1):c})),i}const V=e=>{var t,s,n,i,r,a;return e.subscriptionWorkerUrl&&"undefined"==typeof SharedWorker&&(e.subscriptionWorkerUrl=null),Object.assign(Object.assign({},(e=>{var t,s,n,i,r,a,o,u,l,h,d,p,g,b,y;const m=Object.assign({},e);if(null!==(t=m.ssl)&&void 0!==t||(m.ssl=!0),null!==(s=m.transactionalRequestTimeout)&&void 0!==s||(m.transactionalRequestTimeout=15),null!==(n=m.subscribeRequestTimeout)&&void 0!==n||(m.subscribeRequestTimeout=310),null!==(i=m.fileRequestTimeout)&&void 0!==i||(m.fileRequestTimeout=300),null!==(r=m.restore)&&void 0!==r||(m.restore=!0),null!==(a=m.useInstanceId)&&void 0!==a||(m.useInstanceId=!1),null!==(o=m.suppressLeaveEvents)&&void 0!==o||(m.suppressLeaveEvents=!1),null!==(u=m.requestMessageCountThreshold)&&void 0!==u||(m.requestMessageCountThreshold=100),null!==(l=m.autoNetworkDetection)&&void 0!==l||(m.autoNetworkDetection=!1),null!==(h=m.enableEventEngine)&&void 0!==h||(m.enableEventEngine=!0),null!==(d=m.maintainPresenceState)&&void 0!==d||(m.maintainPresenceState=!0),null!==(p=m.useSmartHeartbeat)&&void 0!==p||(m.useSmartHeartbeat=!1),null!==(g=m.keepAlive)&&void 0!==g||(m.keepAlive=!1),m.userId&&m.uuid)throw new c("PubNub client configuration error: use only 'userId'");if(null!==(b=m.userId)&&void 0!==b||(m.userId=m.uuid),!m.userId)throw new c("PubNub client configuration error: 'userId' not set");if(0===(null===(y=m.userId)||void 0===y?void 0:y.trim().length))throw new c("PubNub client configuration error: 'userId' is empty");m.origin||(m.origin=Array.from({length:20},((e,t)=>`ps${t+1}.pndsn.com`)));const f={subscribeKey:m.subscribeKey,publishKey:m.publishKey,secretKey:m.secretKey};void 0!==m.presenceTimeout&&(m.presenceTimeout>320?(m.presenceTimeout=320,console.warn("WARNING: Presence timeout is larger than the maximum. Using maximum value: ",320)):m.presenceTimeout<=0&&(console.warn("WARNING: Presence timeout should be larger than zero."),delete m.presenceTimeout)),void 0!==m.presenceTimeout?m.heartbeatInterval=m.presenceTimeout/2-1:m.presenceTimeout=300;let v=!1,O=!0,S=5,j=!1,w=100,k=!0;return void 0!==m.dedupeOnSubscribe&&"boolean"==typeof m.dedupeOnSubscribe&&(j=m.dedupeOnSubscribe),void 0!==m.maximumCacheSize&&"number"==typeof m.maximumCacheSize&&(w=m.maximumCacheSize),void 0!==m.useRequestId&&"boolean"==typeof m.useRequestId&&(k=m.useRequestId),void 0!==m.announceSuccessfulHeartbeats&&"boolean"==typeof m.announceSuccessfulHeartbeats&&(v=m.announceSuccessfulHeartbeats),void 0!==m.announceFailedHeartbeats&&"boolean"==typeof m.announceFailedHeartbeats&&(O=m.announceFailedHeartbeats),void 0!==m.fileUploadPublishRetryLimit&&"number"==typeof m.fileUploadPublishRetryLimit&&(S=m.fileUploadPublishRetryLimit),Object.assign(Object.assign({},m),{keySet:f,dedupeOnSubscribe:j,maximumCacheSize:w,useRequestId:k,announceSuccessfulHeartbeats:v,announceFailedHeartbeats:O,fileUploadPublishRetryLimit:S})})(e)),{listenToBrowserNetworkEvents:null===(t=e.listenToBrowserNetworkEvents)||void 0===t||t,subscriptionWorkerUrl:e.subscriptionWorkerUrl,subscriptionWorkerOfflineClientsCheckInterval:null!==(s=e.subscriptionWorkerOfflineClientsCheckInterval)&&void 0!==s?s:10,subscriptionWorkerUnsubscribeOfflineClients:null!==(n=e.subscriptionWorkerUnsubscribeOfflineClients)&&void 0!==n&&n,subscriptionWorkerLogVerbosity:null!==(i=e.subscriptionWorkerLogVerbosity)&&void 0!==i&&i,transport:null!==(r=e.transport)&&void 0!==r?r:"fetch",keepAlive:null===(a=e.keepAlive)||void 0===a||a})};var W;!function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Info=2]="Info",e[e.Warn=3]="Warn",e[e.Error=4]="Error",e[e.None=5]="None"}(W||(W={}));class B{debug(e){this.log(e)}error(e){this.log(e)}info(e){this.log(e)}trace(e){this.log(e)}warn(e){this.log(e)}toString(){return"ConsoleLogger {}"}log(e){const t=W[e.level],s=t.toLowerCase();console["trace"===s?"debug":s](`${e.timestamp.toISOString()} PubNub-${e.pubNubId} ${t.padEnd(5," ")}${e.location?` ${e.location}`:""} ${this.logMessage(e)}`)}logMessage(e){if("text"===e.messageType)return e.message;if("object"===e.messageType)return`${e.details?`${e.details}\n`:""}${this.formattedObject(e)}`;if("network-request"===e.messageType){const t=!!e.canceled||!!e.failed,s=e.minimumLevel!==W.Trace||t?void 0:this.formattedHeaders(e),n=e.message,i=n.queryParameters&&Object.keys(n.queryParameters).length>0?E(n.queryParameters):void 0,r=`${n.origin}${n.path}${i?`?${i}`:""}`,a=t?void 0:this.formattedBody(e);let o="Sending";t&&(o=`${e.canceled?"Canceled":"Failed"}${e.details?` (${e.details})`:""}`);const c=((null==a?void 0:a.formData)?"FormData":"Method").length;return`${o} HTTP request:\n ${this.paddedString("Method",c)}: ${n.method}\n ${this.paddedString("URL",c)}: ${r}${s?`\n ${this.paddedString("Headers",c)}:\n${s}`:""}${(null==a?void 0:a.formData)?`\n ${this.paddedString("FormData",c)}:\n${a.formData}`:""}${(null==a?void 0:a.body)?`\n ${this.paddedString("Body",c)}:\n${a.body}`:""}`}if("network-response"===e.messageType){const t=e.minimumLevel===W.Trace?this.formattedHeaders(e):void 0,s=this.formattedBody(e),n=((null==s?void 0:s.formData)?"Headers":"Status").length,i=e.message;return`Received HTTP response:\n ${this.paddedString("URL",n)}: ${i.url}\n ${this.paddedString("Status",n)}: ${i.status}${e.details?`\n ${this.paddedString("Details",n)}: ${e.details}`:""}${t?`\n ${this.paddedString("Headers",n)}:\n${t}`:""}${(null==s?void 0:s.body)?`\n ${this.paddedString("Body",n)}:\n${s.body}`:""}`}if("error"===e.messageType){const t=this.formattedErrorStatus(e),s=e.message;return`${s.name}: ${s.message}${t?`\n${t}`:""}`}return""}formattedObject(e){const t=(s,n=1,i=!1)=>{const r=10===n,a=" ".repeat(2*n),o=[],c=(t,s)=>!!e.ignoredKeys&&("function"==typeof e.ignoredKeys?e.ignoredKeys(t,s):e.ignoredKeys.includes(t));if("string"==typeof s)o.push(`${a}- ${s}`);else if("number"==typeof s)o.push(`${a}- ${s}`);else if("boolean"==typeof s)o.push(`${a}- ${s}`);else if(null===s)o.push(`${a}- null`);else if(void 0===s)o.push(`${a}- undefined`);else if("function"==typeof s)o.push(`${a}- `);else if("object"==typeof s)if(Array.isArray(s)||"function"!=typeof s.toString||0===s.toString().indexOf("[object"))if(Array.isArray(s))for(const e of s){const s=i?"":a;if(null===e)o.push(`${s}- null`);else if(void 0===e)o.push(`${s}- undefined`);else if("function"==typeof e)o.push(`${s}- `);else if("object"==typeof e){const i=Array.isArray(e),a=r?"...":t(e,n+1,!i);o.push(`${s}-${i&&!r?"\n":" "}${a}`)}else o.push(`${s}- ${e}`);i=!1}else{const e=s,u=Object.keys(e),l=u.reduce(((t,s)=>Math.max(t,c(s,e)?t:s.length)),0);for(const s of u){if(c(s,e))continue;const u=i?"":a,h=e[s],d=s.padEnd(l," ");if(null===h)o.push(`${u}${d}: null`);else if(void 0===h)o.push(`${u}${d}: undefined`);else if("function"==typeof h)o.push(`${u}${d}: `);else if("object"==typeof h){const e=Array.isArray(h),s=e&&0===h.length,i=!(e||h instanceof String||0!==Object.keys(h).length),a=!e&&"function"==typeof h.toString&&0!==h.toString().indexOf("[object"),c=r?"...":s?"[]":i?"{}":t(h,n+1,a);o.push(`${u}${d}:${r||a||s||i?" ":"\n"}${c}`)}else o.push(`${u}${d}: ${h}`);i=!1}}else o.push(`${i?"":a}${s.toString()}`),i=!1;return o.join("\n")};return t(e.message)}formattedHeaders(e){if(!e.message.headers)return;const t=e.message.headers,s=Object.keys(t).reduce(((e,t)=>Math.max(e,t.length)),0);return Object.keys(t).map((e=>` - ${e.toLowerCase().padEnd(s," ")}: ${t[e]}`)).join("\n")}formattedBody(e){var t;if(!e.message.headers)return;let s,n;const i=e.message.headers,r=null!==(t=i["content-type"])&&void 0!==t?t:i["Content-Type"],a="formData"in e.message?e.message.formData:void 0,o=e.message.body;if(a){const e=a.reduce(((e,{key:t})=>Math.max(e,t.length)),0);s=a.map((({key:t,value:s})=>` - ${t.padEnd(e," ")}: ${s}`)).join("\n")}return o?(n="string"==typeof o?` ${o}`:o instanceof ArrayBuffer||"[object ArrayBuffer]"===Object.prototype.toString.call(o)?!r||-1===r.indexOf("javascript")&&-1===r.indexOf("json")?` ArrayBuffer { byteLength: ${o.byteLength} }`:` ${B.decoder.decode(o)}`:` File { name: ${o.name}${o.contentLength?`, contentLength: ${o.contentLength}`:""}${o.mimeType?`, mimeType: ${o.mimeType}`:""} }`,{body:n,formData:s}):{formData:s}}formattedErrorStatus(e){if(!e.message.status)return;const t=e.message.status,s=t.errorData;let n;if(B.isError(s))n=` ${s.name}: ${s.message}`,s.stack&&(n+=`\n${s.stack.split("\n").map((e=>` ${e}`)).join("\n")}`);else if(s)try{n=` ${JSON.stringify(s)}`}catch(e){n=` ${s}`}return` Category : ${t.category}\n Operation : ${t.operation}\n Status : ${t.statusCode}${n?`\n Error data:\n${n}`:""}`}paddedString(e,t){return e.padEnd(t-e.length," ")}static isError(e){return!!e&&(e instanceof Error||"[object Error]"===Object.prototype.toString.call(e))}}B.decoder=new TextDecoder;const z=[404,409];var J;!function(e){e.Unknown="UnknownEndpoint",e.MessageSend="MessageSendEndpoint",e.Subscribe="SubscribeEndpoint",e.Presence="PresenceEndpoint",e.Files="FilesEndpoint",e.MessageStorage="MessageStorageEndpoint",e.ChannelGroups="ChannelGroupsEndpoint",e.DevicePushNotifications="DevicePushNotificationsEndpoint",e.AppContext="AppContextEndpoint",e.MessageReactions="MessageReactionsEndpoint"}(J||(J={}));class Y{static None(){return{shouldRetry:(e,t,s,n,i)=>!1,getDelay:(e,t)=>-1,validate:()=>!0}}static LinearRetryPolicy(e){var t;return{delay:e.delay,maximumRetry:e.maximumRetry,excluded:null!==(t=e.excluded)&&void 0!==t?t:[],shouldRetry(e,t,s,n,i){return Q(e,t,s,null!=n?n:0,this.maximumRetry,this.excluded,i)},getDelay(e,t){let s=-1;return t&&void 0!==t.headers["retry-after"]&&(s=parseInt(t.headers["retry-after"],10)),-1===s&&(s=this.delay),1e3*(s+Math.random())},validate(){if(this.delay<2)throw new Error("Delay can not be set less than 2 seconds for retry")}}}static ExponentialRetryPolicy(e){var t;return{minimumDelay:e.minimumDelay,maximumDelay:e.maximumDelay,maximumRetry:e.maximumRetry,excluded:null!==(t=e.excluded)&&void 0!==t?t:[],shouldRetry(e,t,s,n,i){return Q(e,t,s,null!=n?n:0,this.maximumRetry,this.excluded,i)},getDelay(e,t){let s=-1;return t&&void 0!==t.headers["retry-after"]&&(s=parseInt(t.headers["retry-after"],10)),-1===s&&(s=Math.min(this.minimumDelay*Math.pow(2,e),this.maximumDelay)),1e3*(s+Math.random())},validate(){if(this.minimumDelay<2)throw new Error("Minimum delay can not be set less than 2 seconds for retry")}}}}const Q=(e,t,s,n,i,r,a)=>{var c;if(s&&(s===o.PNCancelledCategory||s===o.PNBadRequestCategory||s===o.PNAccessDeniedCategory))return!1;if(X(e,r))return!1;if(n>i)return!1;const u=null!==(c=null==t?void 0:t.status)&&void 0!==c?c:a;return(void 0===u||!z.includes(u))&&(!t||(429===t.status||t.status>=500))},X=(e,t)=>!!(t&&t.length>0)&&t.includes(Z(e)),Z=e=>{let t=J.Unknown;return e.path.startsWith("/v2/subscribe")?t=J.Subscribe:e.path.startsWith("/publish/")||e.path.startsWith("/signal/")?t=J.MessageSend:e.path.startsWith("/v2/presence")?t=J.Presence:e.path.startsWith("/v2/history")||e.path.startsWith("/v3/history")?t=J.MessageStorage:e.path.startsWith("/v1/message-actions/")?t=J.MessageReactions:e.path.startsWith("/v1/channel-registration/")||e.path.startsWith("/v2/objects/")?t=J.ChannelGroups:e.path.startsWith("/v1/push/")||e.path.startsWith("/v2/push/")?t=J.DevicePushNotifications:e.path.startsWith("/v1/files/")&&(t=J.Files),t};class ee{constructor(e,t,s){this.previousEntryTimestamp=0,this.pubNubId=e,this.minLogLevel=t,this.loggers=s}get logLevel(){return this.minLogLevel}trace(e,t){this.log(W.Trace,e,t)}debug(e,t){this.log(W.Debug,e,t)}info(e,t){this.log(W.Info,e,t)}warn(e,t){this.log(W.Warn,e,t)}error(e,t){this.log(W.Error,e,t)}log(e,t,s){if(ee[i](r)))}}var te={exports:{}}; +/*! lil-uuid - v0.1 - MIT License - https://github.com/lil-js/uuid */!function(e,t){!function(e){var t="0.1.0",s={3:/^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,4:/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,5:/^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,all:/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i};function n(){var e,t,s="";for(e=0;e<32;e++)t=16*Math.random()|0,8!==e&&12!==e&&16!==e&&20!==e||(s+="-"),s+=(12===e?4:16===e?3&t|8:t).toString(16);return s}function i(e,t){var n=s[t||"all"];return n&&n.test(e)||!1}n.isUUID=i,n.VERSION=t,e.uuid=n,e.isUUID=i}(t),null!==e&&(e.exports=t.uuid)}(te,te.exports);var se=t(te.exports),ne={createUUID:()=>se.uuid?se.uuid():se()};const ie=(e,t)=>{var s,n,i,r;!e.retryConfiguration&&e.enableEventEngine&&(e.retryConfiguration=Y.ExponentialRetryPolicy({minimumDelay:2,maximumDelay:150,maximumRetry:6,excluded:[J.MessageSend,J.Presence,J.Files,J.MessageStorage,J.ChannelGroups,J.DevicePushNotifications,J.AppContext,J.MessageReactions]}));const a=`pn-${ne.createUUID()}`;e.logVerbosity?e.logLevel=W.Debug:void 0===e.logLevel&&(e.logLevel=W.None);const o=new ee(ae(a),e.logLevel,[...null!==(s=e.loggers)&&void 0!==s?s:[],new B]);void 0!==e.logVerbosity&&o.warn("Configuration","'logVerbosity' is deprecated. Use 'logLevel' instead."),null===(n=e.retryConfiguration)||void 0===n||n.validate();const c=e.useRandomIVs;null!==(i=e.useRandomIVs)&&void 0!==i||(e.useRandomIVs=true),void 0!==c&&(o.warn("Configuration","'useRandomIVs' is deprecated. Pass it to 'cryptoModule' instead."),!1===c&&o.warn("Configuration","Setting 'useRandomIVs' to false is insecure and should only be used to support legacy clients.\n Do not disable random IVs in new applications.")),e.origin=re(null!==(r=e.ssl)&&void 0!==r&&r,e.origin);const u=e.cryptoModule;u&&delete e.cryptoModule;const l=Object.assign(Object.assign({},e),{_pnsdkSuffix:{},_loggerManager:o,_instanceId:a,_cryptoModule:void 0,_cipherKey:void 0,_setupCryptoModule:t,get instanceId(){if(e.useInstanceId)return this._instanceId},getInstanceId(){if(e.useInstanceId)return this._instanceId},getUserId(){return this.userId},setUserId(e){if(!e||"string"!=typeof e||0===e.trim().length)throw new Error("Missing or invalid userId parameter. Provide a valid string userId");this.userId=e},logger(){return this._loggerManager},getAuthKey(){return this.authKey},setAuthKey(e){this.authKey=e},getFilterExpression(){return this.filterExpression},setFilterExpression(e){this.filterExpression=e},getCipherKey(){return this._cipherKey},setCipherKey(t){this._cipherKey=t,t||!this._cryptoModule?t&&this._setupCryptoModule&&(this._cryptoModule=this._setupCryptoModule({cipherKey:t,useRandomIVs:e.useRandomIVs,customEncrypt:this.getCustomEncrypt(),customDecrypt:this.getCustomDecrypt(),logger:this.logger()})):this._cryptoModule=void 0},getCryptoModule(){return this._cryptoModule},getUseRandomIVs:()=>e.useRandomIVs,isSharedWorkerEnabled:()=>"Web"===e.sdkFamily&&e.subscriptionWorkerUrl,getKeepPresenceChannelsInPresenceRequests:()=>"Web"===e.sdkFamily&&e.subscriptionWorkerUrl,setPresenceTimeout(e){this.heartbeatInterval=e/2-1,this.presenceTimeout=e},getPresenceTimeout(){return this.presenceTimeout},getHeartbeatInterval(){return this.heartbeatInterval},setHeartbeatInterval(e){this.heartbeatInterval=e},getTransactionTimeout(){return this.transactionalRequestTimeout},getSubscribeTimeout(){return this.subscribeRequestTimeout},getFileTimeout(){return this.fileRequestTimeout},get PubNubFile(){return e.PubNubFile},get version(){return"13.1.3"},getVersion(){return this.version},_addPnsdkSuffix(e,t){this._pnsdkSuffix[e]=`${t}`},_getPnsdkSuffix(e){const t=Object.values(this._pnsdkSuffix).join(e);return t.length>0?e+t:""},getUUID(){return this.getUserId()},setUUID(e){this.setUserId(e)},getCustomEncrypt:()=>e.customEncrypt,getCustomDecrypt:()=>e.customDecrypt});return e.cipherKey?(o.warn("Configuration","'cipherKey' is deprecated. Use 'cryptoModule' instead."),l.setCipherKey(e.cipherKey)):u&&(l._cryptoModule=u),l},re=(e,t)=>{const s=e?"https://":"http://";return"string"==typeof t?`${s}${t}`:`${s}${t[Math.floor(Math.random()*t.length)]}`},ae=e=>{let t=2166136261;for(let s=0;s>>0;return t.toString(16).padStart(8,"0")};class oe{constructor(e){this.cbor=e}setToken(e){e&&e.length>0?this.token=e:this.token=void 0}getToken(){return this.token}parseToken(e){var t,s;const n=this.cbor.decodeToken(e);if(void 0!==n){const e=n.res.uuid?Object.keys(n.res.uuid):[],i=n.res.usr?Object.keys(n.res.usr):[],r=Object.keys(n.res.chan),a=Object.keys(n.res.grp),o=n.pat.uuid?Object.keys(n.pat.uuid):[],c=n.pat.usr?Object.keys(n.pat.usr):[],u=Object.keys(n.pat.chan),l=Object.keys(n.pat.grp),h={version:n.v,timestamp:n.t,ttl:n.ttl,authorized_uuid:n.uuid,signature:n.sig},d=e.length>0,p=i.length>0,g=r.length>0,b=a.length>0;if(d||p||g||b){if(h.resources={},d){const t=h.resources.uuids={};e.forEach((e=>t[e]=this.extractPermissions(n.res.uuid[e])))}if(p){const e=h.resources.users={};i.forEach((t=>e[t]=this.extractCrudPermissions(n.res.usr[t])))}if(g){const e=h.resources.channels={};r.forEach((t=>e[t]=this.extractPermissions(n.res.chan[t])))}if(b){const e=h.resources.groups={};a.forEach((t=>e[t]=this.extractPermissions(n.res.grp[t])))}}const y=this.extractDataSyncScopes(n.res);y&&((null!==(t=h.resources)&&void 0!==t?t:h.resources={}).dataSync=y);const m=o.length>0,f=c.length>0,v=u.length>0,O=l.length>0;if(m||f||v||O){if(h.patterns={},m){const e=h.patterns.uuids={};o.forEach((t=>e[t]=this.extractPermissions(n.pat.uuid[t])))}if(f){const e=h.patterns.users={};c.forEach((t=>e[t]=this.extractCrudPermissions(n.pat.usr[t])))}if(v){const e=h.patterns.channels={};u.forEach((t=>e[t]=this.extractPermissions(n.pat.chan[t])))}if(O){const e=h.patterns.groups={};l.forEach((t=>e[t]=this.extractPermissions(n.pat.grp[t])))}}const S=this.extractDataSyncScopes(n.pat);S&&((null!==(s=h.patterns)&&void 0!==s?s:h.patterns={}).dataSync=S);const j=this.extractCategories(n.cat);return j&&(h.categories=j),n.meta&&Object.keys(n.meta).length>0&&(h.meta=n.meta),h}}extractPermissions(e){const t={read:!1,write:!1,manage:!1,delete:!1,get:!1,update:!1,join:!1};return 128&~e||(t.join=!0),64&~e||(t.update=!0),32&~e||(t.get=!0),8&~e||(t.delete=!0),4&~e||(t.manage=!0),2&~e||(t.write=!0),1&~e||(t.read=!0),t}extractCategories(e){if(!e)return;const t={};return"number"==typeof e.chan&&(t.channels={get:!(32&~e.chan)}),"number"==typeof e.uuid&&(t.uuids={get:!(32&~e.uuid)}),Object.keys(t).length>0?t:void 0}extractDataSyncScopes(e){let t;return[["entities","datasync:entities"],["relationships","datasync:relationships"],["memberships","datasync:memberships"]].forEach((([s,n])=>{const i=e[n];if(!i)return;const r=Object.keys(i);if(0===r.length)return;const a=(null!=t?t:t={})[s]={};r.forEach((e=>a[e]=this.extractCrudPermissions(i[e])))})),t}extractCrudPermissions(e){return{create:!(16&~e),get:!(32&~e),update:!(64&~e),delete:!(8&~e)}}}var ce;!function(e){e.GET="GET",e.POST="POST",e.PATCH="PATCH",e.PUT="PUT",e.DELETE="DELETE",e.LOCAL="LOCAL"}(ce||(ce={}));class ue{constructor(e,t,s,n){this.publishKey=e,this.secretKey=t,this.hasher=s,this.logger=n}signature(e){const t=e.path.startsWith("/publish")?ce.GET:e.method;let s=`${t}\n${this.publishKey}\n${e.path}\n${this.queryParameters(e.queryParameters)}\n`;if(t===ce.POST||t===ce.PATCH||t===ce.PUT){const t=e.body;let n;t&&t instanceof ArrayBuffer?n=ue.textDecoder.decode(t):t&&"object"!=typeof t&&(n=t),n&&(s+=n)}return this.logger.trace("RequestSignature",(()=>({messageType:"text",message:`Request signature input:\n${s}`}))),`v2.${this.hasher(s,this.secretKey)}`.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}queryParameters(e){return Object.keys(e).sort().map((t=>{const s=e[t];return Array.isArray(s)?s.sort().map((e=>`${t}=${k(e)}`)).join("&"):`${t}=${k(s)}`})).join("&")}}ue.textDecoder=new TextDecoder("utf-8");class le{constructor(e){this.configuration=e;const{clientConfiguration:{keySet:t},shaHMAC:s}=e;t.secretKey&&s&&(this.signatureGenerator=new ue(t.publishKey,t.secretKey,s,this.logger))}get logger(){return this.configuration.clientConfiguration.logger()}makeSendable(e){const t=this.configuration.clientConfiguration.retryConfiguration,s=this.configuration.transport;if(void 0!==t){let n,i,r=!1,a=0;const o={abort:e=>{r=!0,n&&clearTimeout(n),i&&i.abort(e)}};return[new Promise(((o,c)=>{const u=()=>{if(r)return;const[l,h]=s.makeSendable(this.request(e));i=h;const d=(s,i)=>{let r=-1;t.shouldRetry(e,s,null==i?void 0:i.category,a+1,null==i?void 0:i.statusCode)&&(r=t.getDelay(a,s)),r>0?(a++,this.logger.warn("PubNubMiddleware",`HTTP request retry #${a} in ${r}ms.`),n=setTimeout((()=>u()),r)):s?o(s):i&&c(i)};l.then((e=>d(e))).catch((e=>d(void 0,e)))};u()})),i?o:void 0]}return s.makeSendable(this.request(e))}request(e){var t;const{clientConfiguration:s}=this.configuration;return(e=this.configuration.transport.request(e)).queryParameters||(e.queryParameters={}),s.useInstanceId&&(e.queryParameters.instanceid=s.getInstanceId()),e.queryParameters.uuid||(e.queryParameters.uuid=s.userId),s.useRequestId&&(e.queryParameters.requestid=e.identifier),e.queryParameters.pnsdk=this.generatePNSDK(),null!==(t=e.origin)&&void 0!==t||(e.origin=s.origin),this.authenticateRequest(e),this.signRequest(e),e}authenticateRequest(e){var t;if(e.path.startsWith("/v2/auth/")||e.path.startsWith("/v3/pam/")||e.path.startsWith("/time"))return;const{clientConfiguration:s,tokenManager:n}=this.configuration,i=null!==(t=n&&n.getToken())&&void 0!==t?t:s.authKey;i&&(e.queryParameters.auth=i)}signRequest(e){this.signatureGenerator&&!e.path.startsWith("/time")&&(e.queryParameters.timestamp=String(Math.floor((new Date).getTime()/1e3)),e.queryParameters.signature=this.signatureGenerator.signature(e))}generatePNSDK(){const{clientConfiguration:e}=this.configuration;if(e.sdkName)return e.sdkName;let t=`PubNub-JS-${e.sdkFamily}`;e.partnerId&&(t+=`-${e.partnerId}`),t+=`/${e.getVersion()}`;const s=e._getPnsdkSuffix(" ");return s.length>0&&(t+=s),t}}class he{constructor(e,t="fetch"){this.logger=e,this.transport=t,e.debug("WebTransport",`Create with configuration:\n - transport: ${t}`),"fetch"===t&&"undefined"==typeof fetch&&(e.warn("WebTransport",`'${t}' not supported in this browser. Fallback to the 'xhr' transport.`),this.transport="xhr"),"fetch"===this.transport&&(he.originalFetch=he.getOriginalFetch(),this.isFetchMonkeyPatched()&&e.warn("WebTransport","Native Web Fetch API 'fetch' function monkey patched."))}makeSendable(e){const t=new AbortController,s={abortController:t,abort:e=>{t.signal.aborted||(this.logger.trace("WebTransport",`On-demand request aborting: ${e}`),t.abort(e))}};return[this.webTransportRequestFromTransportRequest(e).then((t=>(this.logger.debug("WebTransport",(()=>({messageType:"network-request",message:e}))),this.sendRequest(t,s).then((e=>e.arrayBuffer().then((t=>[e,t])))).then((e=>{const s=e[1].byteLength>0?e[1]:void 0,{status:n,headers:i}=e[0],r={};i.forEach(((e,t)=>r[t]=e.toLowerCase()));const a={status:n,url:t.url,headers:r,body:s};if(this.logger.debug("WebTransport",(()=>({messageType:"network-response",message:a}))),n>=400)throw q.create(a);return a})).catch((t=>{const s=("string"==typeof t?t:t.message).toLowerCase();let n="string"==typeof t?new Error(t):t;throw s.includes("timeout")?this.logger.warn("WebTransport",(()=>({messageType:"network-request",message:e,details:"Timeout",canceled:!0}))):s.includes("cancel")||s.includes("abort")?(this.logger.debug("WebTransport",(()=>({messageType:"network-request",message:e,details:"Aborted",canceled:!0}))),n=new Error("Aborted"),n.name="AbortError"):s.includes("network")?this.logger.warn("WebTransport",(()=>({messageType:"network-request",message:e,details:"Network error",failed:!0}))):this.logger.warn("WebTransport",(()=>({messageType:"network-request",message:e,details:q.create(n).message,failed:!0}))),q.create(n)}))))),s]}request(e){return e}sendRequest(e,t){return a(this,void 0,void 0,(function*(){return"fetch"===this.transport?this.sendFetchRequest(e,t):this.sendXHRRequest(e,t)}))}sendFetchRequest(e,t){return a(this,void 0,void 0,(function*(){let s;const n=new Promise(((n,i)=>{s=setTimeout((()=>{clearTimeout(s),i(new Error("Request timeout")),t.abort("Cancel because of timeout")}),1e3*e.timeout)})),i=new Request(e.url,{method:e.method,headers:e.headers,redirect:"follow",body:e.body});return Promise.race([he.originalFetch(i,{signal:t.abortController.signal,credentials:"omit",cache:"no-cache"}).then((e=>(s&&clearTimeout(s),e))),n])}))}sendXHRRequest(e,t){return a(this,void 0,void 0,(function*(){return new Promise(((s,n)=>{var i;const r=new XMLHttpRequest;r.open(e.method,e.url,!0);let a=!1;r.responseType="arraybuffer",r.timeout=1e3*e.timeout,t.abortController.signal.onabort=()=>{r.readyState!=XMLHttpRequest.DONE&&r.readyState!=XMLHttpRequest.UNSENT&&(a=!0,r.abort())},Object.entries(null!==(i=e.headers)&&void 0!==i?i:{}).forEach((([e,t])=>r.setRequestHeader(e,t))),r.onabort=()=>{n(new Error("Aborted"))},r.ontimeout=()=>{n(new Error("Request timeout"))},r.onerror=()=>{if(!a){const t=this.transportResponseFromXHR(e.url,r);n(new Error(q.create(t).message))}},r.onload=()=>{const e=new Headers;r.getAllResponseHeaders().split("\r\n").forEach((t=>{const[s,n]=t.split(": ");s.length>1&&n.length>1&&e.append(s,n)})),s(new Response(r.response,{status:r.status,headers:e,statusText:r.statusText}))},r.send(e.body)}))}))}webTransportRequestFromTransportRequest(e){return a(this,void 0,void 0,(function*(){let t,s=e.path;if(e.formData&&e.formData.length>0){e.queryParameters={};const s=e.body,n=new FormData;for(const{key:t,value:s}of e.formData)n.append(t,s);try{const e=yield s.toArrayBuffer();n.append("file",new Blob([e],{type:"application/octet-stream"}),s.name)}catch(e){this.logger.warn("WebTransport",(()=>({messageType:"error",message:e})));try{const e=yield s.toFileUri();n.append("file",e,s.name)}catch(e){this.logger.error("WebTransport",(()=>({messageType:"error",message:e})))}}t=n}else if(e.body&&("string"==typeof e.body||e.body instanceof ArrayBuffer))if(e.compressible&&"undefined"!=typeof CompressionStream){const s="string"==typeof e.body?he.encoder.encode(e.body):e.body,n=s.byteLength,i=new ReadableStream({start(e){e.enqueue(s),e.close()}});t=yield new Response(i.pipeThrough(new CompressionStream("deflate"))).arrayBuffer(),this.logger.trace("WebTransport",(()=>{const e=t.byteLength,s=(e/n).toFixed(2);return{messageType:"text",message:`Body of ${n} bytes, compressed by ${s}x to ${e} bytes.`}}))}else t=e.body;return e.queryParameters&&0!==Object.keys(e.queryParameters).length&&(s=`${s}?${E(e.queryParameters)}`),{url:`${e.origin}${s}`,method:e.method,headers:e.headers,timeout:e.timeout,body:t}}))}isFetchMonkeyPatched(e){return!(null!=e?e:fetch).toString().includes("[native code]")&&"fetch"!==fetch.name}transportResponseFromXHR(e,t){const s=t.getAllResponseHeaders().split("\n"),n={};for(const e of s){const[t,s]=e.trim().split(":");t&&s&&(n[t.toLowerCase()]=s.trim())}return{status:t.status,url:e,headers:n,body:t.response}}static getOriginalFetch(){if("undefined"==typeof document||!document.body)return fetch.bind(globalThis);let e=document.querySelector('iframe[name="pubnub-context-unpatched-fetch"]');return e||(e=document.createElement("iframe"),e.style.display="none",e.name="pubnub-context-unpatched-fetch",e.src="about:blank",document.body.appendChild(e)),e.contentWindow?e.contentWindow.fetch.bind(e.contentWindow):fetch.bind(globalThis)}}he.encoder=new TextEncoder,he.decoder=new TextDecoder;class de{constructor(e){this.params=e,this.requestIdentifier=ne.createUUID(),this._cancellationController=null}get cancellationController(){return this._cancellationController}set cancellationController(e){this._cancellationController=e}abort(e){this&&this.cancellationController&&this.cancellationController.abort(e)}operation(){throw Error("Should be implemented by subclass.")}validate(){}prepare(){}parse(e){return a(this,void 0,void 0,(function*(){return this.deserializeResponse(e)}))}request(){var e,t,s,n,i,r;const a={method:null!==(t=null===(e=this.params)||void 0===e?void 0:e.method)&&void 0!==t?t:ce.GET,path:this.path,queryParameters:this.queryParameters,cancellable:null!==(n=null===(s=this.params)||void 0===s?void 0:s.cancellable)&&void 0!==n&&n,compressible:null!==(r=null===(i=this.params)||void 0===i?void 0:i.compressible)&&void 0!==r&&r,timeout:10,identifier:this.requestIdentifier},o=this.headers;if(o&&(a.headers=o),a.method===ce.POST||a.method===ce.PATCH||a.method===ce.PUT){const[e,t]=[this.body,this.formData];t&&(a.formData=t),e&&(a.body=e)}return a}get headers(){var e,t;return Object.assign({"Accept-Encoding":"gzip, deflate"},null!==(t=null===(e=this.params)||void 0===e?void 0:e.compressible)&&void 0!==t&&t?{"Content-Encoding":"deflate"}:{})}get path(){throw Error("`path` getter should be implemented by subclass.")}get queryParameters(){return{}}get formData(){}get body(){}deserializeResponse(e){const t=de.decoder.decode(e.body),s=e.headers["content-type"];let n;if(!s||-1===s.indexOf("javascript")&&-1===s.indexOf("json"))throw new c("Service response error, check status for details",l(t,e.status));try{n=JSON.parse(t)}catch(s){throw console.error("Error parsing JSON response:",s),new c("Service response error, check status for details",l(t,e.status))}if("status"in n&&"number"==typeof n.status&&n.status>=400)throw q.create(e);return n}}de.decoder=new TextDecoder;var pe;!function(e){e[e.Presence=-2]="Presence",e[e.Message=-1]="Message",e[e.Signal=1]="Signal",e[e.AppContext=2]="AppContext",e[e.MessageAction=3]="MessageAction",e[e.Files=4]="Files",e[e.DataSync=5]="DataSync"}(pe||(pe={}));const ge=new Set([pe.Presence,pe.Message,pe.Signal,pe.AppContext,pe.MessageAction,pe.Files,pe.DataSync]),be={user:"user",channel:"channel",membership:"membership"};class ye extends de{constructor(e){var t,s,n,i,r,a;super({cancellable:!0}),this.parameters=e,null!==(t=(i=this.parameters).withPresence)&&void 0!==t||(i.withPresence=false),null!==(s=(r=this.parameters).channelGroups)&&void 0!==s||(r.channelGroups=[]),null!==(n=(a=this.parameters).channels)&&void 0!==n||(a.channels=[])}operation(){return L.PNSubscribeOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroups:s}=this.parameters;return e?t||s?void 0:"`channels` and `channelGroups` both should not be empty":"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){let t,s;try{s=de.decoder.decode(e.body);t=JSON.parse(s)}catch(e){console.error("Error parsing JSON response:",e)}if(!t)throw new c("Service response error, check status for details",l(s,e.status));const n=t.m.filter((e=>{const t=void 0===e.b?e.c:e.b;return this.parameters.channels&&this.parameters.channels.includes(t)||this.parameters.channelGroups&&this.parameters.channelGroups.includes(t)})),i=(yield Promise.all(n.map((e=>a(this,void 0,void 0,(function*(){let{e:t}=e;if(e.c.endsWith("-pnpres")?t=pe.Presence:null!=t||(t=pe.Message),!ge.has(t))return;const s=M(e.d);if(t!=pe.Presence&&t!=pe.Signal&&"string"==typeof e.d)return t==pe.Message?{type:pe.Message,data:yield this.messageFromEnvelope(e),pn_mfp:s}:{type:pe.Files,data:yield this.fileFromEnvelope(e),pn_mfp:s};if(t==pe.Message)return{type:pe.Message,data:yield this.messageFromEnvelope(e),pn_mfp:s};if(t===pe.Presence)return{type:pe.Presence,data:this.presenceEventFromEnvelope(e),pn_mfp:s};if(t==pe.Signal)return{type:pe.Signal,data:this.signalFromEnvelope(e),pn_mfp:s};if(t===pe.AppContext)return{type:pe.AppContext,data:this.appContextFromEnvelope(e),pn_mfp:s};if(t===pe.MessageAction)return{type:pe.MessageAction,data:this.messageActionFromEnvelope(e),pn_mfp:s};if(t===pe.DataSync){const t=this.dataSyncFromEnvelope(e);return t?{type:pe.DataSync,data:t,pn_mfp:s}:{type:pe.Message,data:yield this.messageFromEnvelope(e),pn_mfp:s}}return{type:pe.Files,data:yield this.fileFromEnvelope(e),pn_mfp:s}})))))).filter((e=>void 0!==e));return{cursor:{timetoken:t.t.t,region:t.t.r},messages:i}}))}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{accept:"text/javascript"})}presenceEventFromEnvelope(e){var t;const{d:s}=e,[n,i]=this.subscriptionChannelFromEnvelope(e),r=n.replace("-pnpres",""),a=null!==i?r:null,o=null!==i?i:r;let c;if("string"==typeof s)try{const e=JSON.parse(s);null===e||"object"!=typeof e||Array.isArray(e)||(c=e)}catch(e){}else c=s;return c&&("data"in c?(c.state=c.data,delete c.data):"action"in c&&"interval"===c.action&&(c.hereNowRefresh=null!==(t=c.here_now_refresh)&&void 0!==t&&t,delete c.here_now_refresh)),Object.assign({channel:r,subscription:i,actualChannel:a,subscribedChannel:o,timetoken:e.p.t},null!=c?c:{})}messageFromEnvelope(e){return a(this,void 0,void 0,(function*(){const[t,s]=this.subscriptionChannelFromEnvelope(e),[n,i]=yield this.decryptedData(e.d),r={channel:t,subscription:s,actualChannel:null!==s?t:null,subscribedChannel:null!==s?s:t,timetoken:e.p.t,publisher:e.i,message:n};return e.u&&(r.userMetadata=e.u),e.cmt&&(r.customMessageType=e.cmt),i&&(r.error=i),r}))}signalFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),n={channel:t,subscription:s,timetoken:e.p.t,publisher:e.i,message:e.d};return e.u&&(n.userMetadata=e.u),e.cmt&&(n.customMessageType=e.cmt),n}messageActionFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),n=e.d;return{channel:t,subscription:s,timetoken:e.p.t,publisher:e.i,event:n.event,data:Object.assign(Object.assign({},n.data),{uuid:e.i})}}appContextFromEnvelope(e){const[t,s]=this.subscriptionChannelFromEnvelope(e),n=e.d;return{channel:t,subscription:s,timetoken:e.p.t,message:n}}dataSyncFromEnvelope(e){var t;const[s,n]=this.subscriptionChannelFromEnvelope(e),i=e.d,r=null==i?void 0:i.metadata;if(!r||"data-sync"!==r.source||!r.event||!r.type)return;const a=r.className?r.className.split(":"):[],o=a.filter((e=>e.length>0)),c=o.length>0?o[o.length-1]:void 0,u=r.classLevel,l=void 0===u?a[0]:"Global"===u?c:void 0,h=("entity"===r.type||"relationship"===r.type)&&l&&be[l.toLowerCase()]||r.type,d=void 0!==r.classVersion?Number.parseInt(`${r.classVersion}`,10):NaN,p=Number.isNaN(d)?void 0:d,g=null!==(t=i.data)&&void 0!==t?t:{},b={version:i.version,source:r.source,type:r.type,className:c,classLevel:u,classVersion:p};let y;if("delete"===r.event&&"membership"===h){const e={id:g.id,deletedAt:g.deletedAt};"string"==typeof g.channelId&&(e.channelId=g.channelId),"string"==typeof g.userId&&(e.userId=g.userId),y=Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:e})}else if("delete"===r.event&&"relationship"===h){const e={id:g.id,deletedAt:g.deletedAt};"string"==typeof g.entityAId&&(e.entityAId=g.entityAId),"string"==typeof g.entityBId&&(e.entityBId=g.entityBId),y=Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:e})}else y="delete"===r.event?Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:{id:g.id,deletedAt:g.deletedAt}}):Object.assign(Object.assign({},b),{event:r.event,objectType:h,data:Object.assign({},g)});return{channel:s,subscription:n,timetoken:e.p.t,message:y}}fileFromEnvelope(e){return a(this,void 0,void 0,(function*(){const[t,s]=this.subscriptionChannelFromEnvelope(e),[n,i]=yield this.decryptedData(e.d);let r=i;const a={channel:t,subscription:s,timetoken:e.p.t,publisher:e.i};return e.u&&(a.userMetadata=e.u),n?"string"==typeof n?null!=r||(r="Unexpected file information payload data type."):(a.message=n.message,n.file&&(a.file={id:n.file.id,name:n.file.name,url:this.parameters.getFileUrl({id:n.file.id,name:n.file.name,channel:t})})):null!=r||(r="File information payload is missing."),e.cmt&&(a.customMessageType=e.cmt),r&&(a.error=r),a}))}subscriptionChannelFromEnvelope(e){return[e.c,void 0===e.b?e.c:e.b]}decryptedData(e){return a(this,void 0,void 0,(function*(){if(!this.parameters.crypto||"string"!=typeof e)return[e,void 0];let t,s;try{const s=yield this.parameters.crypto.decrypt(e);t=s instanceof ArrayBuffer?JSON.parse(me.decoder.decode(s)):s}catch(e){t=null,s=`Error while decrypting message content: ${e.message}`}return[null!=t?t:e,s]}))}}class me extends ye{get path(){var e;const{keySet:{subscribeKey:t},channels:s}=this.parameters;return`/v2/subscribe/${t}/${P(null!==(e=null==s?void 0:s.sort())&&void 0!==e?e:[],",")}/0`}get queryParameters(){const{channelGroups:e,filterExpression:t,heartbeat:s,state:n,timetoken:i,region:r,onDemand:a}=this.parameters,o={};return a&&(o["on-demand"]=1),e&&e.length>0&&(o["channel-group"]=e.sort().join(",")),t&&t.length>0&&(o["filter-expr"]=t),s&&(o.heartbeat=s),n&&Object.keys(n).length>0&&(o.state=JSON.stringify(n)),void 0!==i&&"string"==typeof i?i.length>0&&"0"!==i&&(o.tt=i):void 0!==i&&i>0&&(o.tt=i),r&&(o.tr=r),o}}class fe{constructor(){this.hasListeners=!1,this.listeners=[{count:-1,listener:{}}]}set onStatus(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"status"})}set onMessage(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"message"})}set onPresence(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"presence"})}set onSignal(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"signal"})}set onObjects(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"objects"})}set onMessageAction(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"messageAction"})}set onFile(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"file"})}set onDataSync(e){this.updateTypeOrObjectListener({add:!!e,listener:e,type:"dataSync"})}handleEvent(e){if(this.hasListeners)if(e.type===pe.Message)this.announce("message",e.data);else if(e.type===pe.Signal)this.announce("signal",e.data);else if(e.type===pe.Presence)this.announce("presence",e.data);else if(e.type===pe.AppContext){const{data:t}=e,{message:s}=t;if(this.announce("objects",t),"uuid"===s.type){const{message:e,channel:n}=t,i=r(t,["message","channel"]),{event:a,type:o}=s,c=r(s,["event","type"]),u=Object.assign(Object.assign({},i),{spaceId:n,message:Object.assign(Object.assign({},c),{event:"set"===a?"updated":"removed",type:"user"})});this.announce("user",u)}else if("channel"===s.type){const{message:e,channel:n}=t,i=r(t,["message","channel"]),{event:a,type:o}=s,c=r(s,["event","type"]),u=Object.assign(Object.assign({},i),{spaceId:n,message:Object.assign(Object.assign({},c),{event:"set"===a?"updated":"removed",type:"space"})});this.announce("space",u)}else if("membership"===s.type){const{message:e,channel:n}=t,i=r(t,["message","channel"]),{event:a,data:o}=s,c=r(s,["event","data"]),{uuid:u,channel:l}=o,h=r(o,["uuid","channel"]),d=Object.assign(Object.assign({},i),{spaceId:n,message:Object.assign(Object.assign({},c),{event:"set"===a?"updated":"removed",data:Object.assign(Object.assign({},h),{user:u,space:l})})});this.announce("membership",d)}}else e.type===pe.MessageAction?this.announce("messageAction",e.data):e.type===pe.Files?this.announce("file",e.data):e.type===pe.DataSync&&this.announce("dataSync",e.data)}handleStatus(e){this.hasListeners&&this.announce("status",e)}addListener(e){this.updateTypeOrObjectListener({add:!0,listener:e})}removeListener(e){this.updateTypeOrObjectListener({add:!1,listener:e})}removeAllListeners(){this.listeners=[{count:-1,listener:{}}],this.hasListeners=!1}updateTypeOrObjectListener(e){if(e.type)"function"==typeof e.listener?this.listeners[0].listener[e.type]=e.listener:delete this.listeners[0].listener[e.type];else if(e.listener&&"function"!=typeof e.listener){let t,s=!1;for(t of this.listeners)if(t.listener===e.listener){e.add?(t.count++,s=!0):(t.count--,0===t.count&&this.listeners.splice(this.listeners.indexOf(t),1));break}e.add&&!s&&this.listeners.push({count:1,listener:e.listener})}this.hasListeners=this.listeners.length>1||Object.keys(this.listeners[0]).length>0}announce(e,t){this.listeners.forEach((({listener:s})=>{const n=s[e];n&&n(t)}))}}class ve{constructor(e){this.time=e}onReconnect(e){this.callback=e}startPolling(){this.timeTimer=setInterval((()=>this.callTime()),3e3)}stopPolling(){this.timeTimer&&clearInterval(this.timeTimer),this.timeTimer=null}callTime(){this.time((e=>{e.error||(this.stopPolling(),this.callback&&this.callback())}))}}class Oe{constructor(e){this.config=e,e.logger().debug("DedupingManager",(()=>({messageType:"object",message:{maximumCacheSize:e.maximumCacheSize},details:"Create with configuration:"}))),this.maximumCacheSize=e.maximumCacheSize,this.hashHistory=[]}getKey(e){var t;return`${e.timetoken}-${this.hashCode(JSON.stringify(null!==(t=e.message)&&void 0!==t?t:"")).toString()}`}isDuplicate(e){return this.hashHistory.includes(this.getKey(e))}addEntry(e){this.hashHistory.length>=this.maximumCacheSize&&this.hashHistory.shift(),this.hashHistory.push(this.getKey(e))}clearHistory(){this.hashHistory=[]}hashCode(e){let t=0;if(0===e.length)return t;for(let s=0;s{this.pendingChannelSubscriptions.add(e),this.channels[e]={},i&&(this.presenceChannels[e]={}),(r||this.configuration.getHeartbeatInterval())&&(this.heartbeatChannels[e]={})})),null==s||s.forEach((e=>{this.pendingChannelGroupSubscriptions.add(e),this.channelGroups[e]={},i&&(this.presenceChannelGroups[e]={}),(r||this.configuration.getHeartbeatInterval())&&(this.heartbeatChannelGroups[e]={})})),this.subscriptionStatusAnnounced=!1,this.reconnect()}unsubscribe(e,t=!1){let{channels:s,channelGroups:n}=e;const i=new Set,a=new Set;if(null==s||s.forEach((e=>{e in this.channels&&(delete this.channels[e],a.add(e),e in this.heartbeatChannels&&delete this.heartbeatChannels[e]),e in this.presenceState&&delete this.presenceState[e],e in this.presenceChannels&&(delete this.presenceChannels[e],a.add(e))})),null==n||n.forEach((e=>{e in this.channelGroups&&(delete this.channelGroups[e],i.add(e),e in this.heartbeatChannelGroups&&delete this.heartbeatChannelGroups[e]),e in this.presenceState&&delete this.presenceState[e],e in this.presenceChannelGroups&&(delete this.presenceChannelGroups[e],i.add(e))})),0===a.size&&0===i.size)return;const o=this.lastTimetoken,c=this.currentTimetoken;0===Object.keys(this.channels).length&&0===Object.keys(this.presenceChannels).length&&0===Object.keys(this.channelGroups).length&&0===Object.keys(this.presenceChannelGroups).length&&(this.lastTimetoken="0",this.currentTimetoken="0",this.referenceTimetoken=null,this.storedTimetoken=null,this.region=null,this.reconnectionManager.stopPolling()),this.reconnect(!0),!1!==this.configuration.suppressLeaveEvents||t||(n=Array.from(i),s=Array.from(a),this.leaveCall({channels:s,channelGroups:n},(e=>{const{error:t}=e,i=r(e,["error"]);let a;t&&(e.errorData&&"object"==typeof e.errorData&&"message"in e.errorData&&"string"==typeof e.errorData.message?a=e.errorData.message:"message"in e&&"string"==typeof e.message&&(a=e.message)),this.emitStatus(Object.assign(Object.assign({},i),{error:null!=a&&a,affectedChannels:s,affectedChannelGroups:n,currentTimetoken:c,lastTimetoken:o}))})))}unsubscribeAll(e=!1){this.disconnectedWhileHandledEvent=!0,this.unsubscribe({channels:this.subscribedChannels,channelGroups:this.subscribedChannelGroups},e)}startSubscribeLoop(e=!1){this.disconnectedWhileHandledEvent=!1,this.stopSubscribeLoop();const t=[...Object.keys(this.channelGroups)],s=[...Object.keys(this.channels)];Object.keys(this.presenceChannelGroups).forEach((e=>t.push(`${e}-pnpres`))),Object.keys(this.presenceChannels).forEach((e=>s.push(`${e}-pnpres`))),0===s.length&&0===t.length||(this.subscribeCall(Object.assign(Object.assign(Object.assign({channels:s,channelGroups:t,state:this.presenceState,heartbeat:this.configuration.getPresenceTimeout(),timetoken:this.currentTimetoken},null!==this.region?{region:this.region}:{}),this.configuration.filterExpression?{filterExpression:this.configuration.filterExpression}:{}),{onDemand:!this.subscriptionStatusAnnounced||e}),((e,t)=>{this.processSubscribeResponse(e,t)})),!e&&this.configuration.useSmartHeartbeat&&this.startHeartbeatTimer())}stopSubscribeLoop(){this._subscribeAbort&&(this._subscribeAbort(),this._subscribeAbort=null)}processSubscribeResponse(e,t){if(e.error){if("object"==typeof e.errorData&&"name"in e.errorData&&"AbortError"===e.errorData.name||e.category===o.PNCancelledCategory)return;return void(e.category===o.PNTimeoutCategory?this.startSubscribeLoop():e.category===o.PNNetworkIssuesCategory||e.category===o.PNMalformedResponseCategory?(this.disconnect(),e.error&&this.configuration.autoNetworkDetection&&this.isOnline&&(this.isOnline=!1,this.emitStatus({category:o.PNNetworkDownCategory})),this.reconnectionManager.onReconnect((()=>{this.configuration.autoNetworkDetection&&!this.isOnline&&(this.isOnline=!0,this.emitStatus({category:o.PNNetworkUpCategory})),this.reconnect(),this.subscriptionStatusAnnounced=!0;const t={category:o.PNReconnectedCategory,operation:e.operation,lastTimetoken:this.lastTimetoken,currentTimetoken:this.currentTimetoken};this.emitStatus(t)})),this.reconnectionManager.startPolling(),this.emitStatus(Object.assign(Object.assign({},e),{category:o.PNNetworkIssuesCategory}))):e.category===o.PNBadRequestCategory?(this.stopHeartbeatTimer(),this.emitStatus(e)):this.emitStatus(e))}if(this.referenceTimetoken=I(t.cursor.timetoken,this.storedTimetoken),this.storedTimetoken?(this.currentTimetoken=this.storedTimetoken,this.storedTimetoken=null):(this.lastTimetoken=this.currentTimetoken,this.currentTimetoken=t.cursor.timetoken),!this.subscriptionStatusAnnounced){const t={category:o.PNConnectedCategory,operation:e.operation,affectedChannels:Array.from(this.pendingChannelSubscriptions),subscribedChannels:this.subscribedChannels,affectedChannelGroups:Array.from(this.pendingChannelGroupSubscriptions),lastTimetoken:this.lastTimetoken,currentTimetoken:this.currentTimetoken};this.subscriptionStatusAnnounced=!0,this.emitStatus(t),this.pendingChannelGroupSubscriptions.clear(),this.pendingChannelSubscriptions.clear()}const{messages:s}=t,{requestMessageCountThreshold:n,dedupeOnSubscribe:i}=this.configuration;n&&s.length>=n&&this.emitStatus({category:o.PNRequestMessageCountExceededCategory,operation:e.operation});try{const e={timetoken:this.currentTimetoken,region:this.region?this.region:void 0};this.configuration.logger().debug("SubscriptionManager",(()=>({messageType:"object",message:s.map((e=>({type:e.type,data:Object.assign(Object.assign({},e.data),{pn_mfp:e.pn_mfp})}))),details:"Received events:"}))),s.forEach((t=>{if(i&&"message"in t.data&&"timetoken"in t.data){if(this.dedupingManager.isDuplicate(t.data))return void this.configuration.logger().warn("SubscriptionManager",(()=>({messageType:"object",message:t.data,details:"Duplicate message detected (skipped):"})));this.dedupingManager.addEntry(t.data)}this.emitEvent(e,t)}))}catch(e){const t={error:!0,category:o.PNUnknownCategory,errorData:e,statusCode:0};this.emitStatus(t)}this.region=t.cursor.region,this.disconnectedWhileHandledEvent?this.disconnectedWhileHandledEvent=!1:this.startSubscribeLoop()}setState(e){const{state:t,channels:s,channelGroups:n}=e;null==s||s.forEach((e=>e in this.channels&&(this.presenceState[e]=t))),null==n||n.forEach((e=>e in this.channelGroups&&(this.presenceState[e]=t)))}changePresence(e){const{connected:t,channels:s,channelGroups:n}=e;t?(null==s||s.forEach((e=>this.heartbeatChannels[e]={})),null==n||n.forEach((e=>this.heartbeatChannelGroups[e]={}))):(null==s||s.forEach((e=>{e in this.heartbeatChannels&&delete this.heartbeatChannels[e]})),null==n||n.forEach((e=>{e in this.heartbeatChannelGroups&&delete this.heartbeatChannelGroups[e]})),!1===this.configuration.suppressLeaveEvents&&this.leaveCall({channels:s,channelGroups:n},(e=>this.emitStatus(e)))),this.reconnect()}startHeartbeatTimer(){this.stopHeartbeatTimer();const e=this.configuration.getHeartbeatInterval();e&&0!==e&&(this.configuration.useSmartHeartbeat||this.sendHeartbeat(),this.heartbeatTimer=setInterval((()=>this.sendHeartbeat()),1e3*e))}stopHeartbeatTimer(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}sendHeartbeat(){const e=Object.keys(this.heartbeatChannelGroups),t=Object.keys(this.heartbeatChannels);0===t.length&&0===e.length||this.heartbeatCall({channels:t,channelGroups:e,heartbeat:this.configuration.getPresenceTimeout(),state:this.presenceState},(e=>{e.error&&this.configuration.announceFailedHeartbeats&&this.emitStatus(e),e.error&&this.configuration.autoNetworkDetection&&this.isOnline&&(this.isOnline=!1,this.disconnect(),this.emitStatus({category:o.PNNetworkDownCategory}),this.reconnect()),!e.error&&this.configuration.announceSuccessfulHeartbeats&&this.emitStatus(e)}))}}class je{constructor(e,t,s){this._payload=e,this.setDefaultPayloadStructure(),this.title=t,this.body=s}get payload(){return this._payload}set title(e){this._title=e}set subtitle(e){this._subtitle=e}set body(e){this._body=e}set badge(e){this._badge=e}set sound(e){this._sound=e}setDefaultPayloadStructure(){}toObject(){return{}}}class we extends je{constructor(){super(...arguments),this._apnsPushType="apns",this._isSilent=!1}get payload(){return this._payload}set configurations(e){e&&e.length&&(this._configurations=e)}get notification(){return this.payload.aps}get title(){return this._title}set title(e){e&&e.length&&(this.payload.aps.alert.title=e,this._title=e)}get subtitle(){return this._subtitle}set subtitle(e){e&&e.length&&(this.payload.aps.alert.subtitle=e,this._subtitle=e)}get body(){return this._body}set body(e){e&&e.length&&(this.payload.aps.alert.body=e,this._body=e)}get badge(){return this._badge}set badge(e){null!=e&&(this.payload.aps.badge=e,this._badge=e)}get sound(){return this._sound}set sound(e){e&&e.length&&(this.payload.aps.sound=e,this._sound=e)}set silent(e){this._isSilent=e}setDefaultPayloadStructure(){this.payload.aps={alert:{}}}toObject(){const e=Object.assign({},this.payload),{aps:t}=e;let{alert:s}=t;if(this._isSilent&&(t["content-available"]=1),"apns2"===this._apnsPushType){if(!this._configurations||!this._configurations.length)throw new ReferenceError("APNS2 configuration is missing");const t=[];this._configurations.forEach((e=>{t.push(this.objectFromAPNS2Configuration(e))})),t.length&&(e.pn_push=t)}return s&&Object.keys(s).length||delete t.alert,this._isSilent&&(delete t.alert,delete t.badge,delete t.sound,s={}),this._isSilent||s&&Object.keys(s).length?e:null}objectFromAPNS2Configuration(e){if(!e.targets||!e.targets.length)throw new ReferenceError("At least one APNS2 target should be provided");const{collapseId:t,expirationDate:s}=e,n={auth_method:"token",targets:e.targets.map((e=>this.objectFromAPNSTarget(e))),version:"v2"};return t&&t.length&&(n.collapse_id=t),s&&(n.expiration=s.toISOString()),n}objectFromAPNSTarget(e){if(!e.topic||!e.topic.length)throw new TypeError("Target 'topic' undefined.");const{topic:t,environment:s="development",excludedDevices:n=[]}=e,i={topic:t,environment:s};return n.length&&(i.excluded_devices=n),i}}class ke extends je{get payload(){return this._payload}get notification(){return this.payload.notification}get data(){return this.payload.data}get androidNotification(){var e;return null===(e=this.payload.android)||void 0===e?void 0:e.notification}get title(){return this._title}set title(e){e&&e.length&&(this.payload.notification.title=e,this.androidNotification.title=e,this._title=e)}get body(){return this._body}set body(e){e&&e.length&&(this.payload.notification.body=e,this.androidNotification.body=e,this._body=e)}get badge(){return this._badge}set badge(e){null!=e&&(this.androidNotification.notification_count=e,this._badge=e)}get sound(){return this._sound}set sound(e){e&&e.length&&(this.androidNotification.sound=e,this._sound=e)}get icon(){return this._icon}set icon(e){e&&e.length&&(this.androidNotification.icon=e,this._icon=e)}get tag(){return this._tag}set tag(e){e&&e.length&&(this.androidNotification.tag=e,this._tag=e)}set silent(e){this._isSilent=e}setDefaultPayloadStructure(){this.payload.notification={},this.payload.data={},this.payload.android={notification:{}}}toObject(){var e,t;const s={},n=Object.assign({},this.payload.notification),i=Object.assign({},this.payload.android),a=r(Object.assign({},null!==(e=i.notification)&&void 0!==e?e:{}),["title","body"]);if(this._isSilent){const e={};this._title&&(e.title=this._title),this._body&&(e.body=this._body);for(const[t,s]of Object.entries(a))null!=s&&(e[t]=String(s));this.payload.data&&Object.assign(e,this.payload.data),Object.keys(e).length&&(s.data=e),delete i.notification,Object.keys(i).length&&(s.android=i)}else if(Object.keys(n).length&&(s.notification=n),this.payload.data&&Object.keys(this.payload.data).length&&(s.data=Object.assign({},this.payload.data)),Object.keys(a).length){const e=r(i,["notification"]);s.android=Object.assign(Object.assign({},e),{notification:a})}else{const e=r(i,["notification"]);Object.keys(e).length&&(s.android=e)}const o=r(this.payload,["notification","android","data","pn_exceptions"]);return Object.assign(s,o),(null===(t=this.payload.pn_exceptions)||void 0===t?void 0:t.length)&&(s.pn_exceptions=this.payload.pn_exceptions),Object.keys(s).length?s:null}}class Pe{constructor(e,t){this._payload={apns:{},fcm:{}},this._title=e,this._body=t,this.apns=new we(this._payload.apns,e,t),this.fcm=new ke(this._payload.fcm,e,t)}set debugging(e){this._debugging=e}get title(){return this._title}get subtitle(){return this._subtitle}set subtitle(e){this._subtitle=e,this.apns.subtitle=e,this.fcm.subtitle=e}get body(){return this._body}get badge(){return this._badge}set badge(e){this._badge=e,this.apns.badge=e,this.fcm.badge=e}get sound(){return this._sound}set sound(e){this._sound=e,this.apns.sound=e,this.fcm.sound=e}buildPayload(e){const t={};if(e.includes("apns")||e.includes("apns2")){this.apns._apnsPushType=e.includes("apns")?"apns":"apns2";const s=this.apns.toObject();s&&Object.keys(s).length&&(t.pn_apns=s)}if(e.includes("fcm")){const e=this.fcm.toObject();e&&Object.keys(e).length&&(t.pn_fcm=e)}return Object.keys(t).length&&this._debugging&&(t.pn_debug=!0),t}}class Ce{constructor(e=!1){this.sync=e,this.listeners=new Set}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}notify(e){const t=()=>{this.listeners.forEach((t=>{t(e)}))};this.sync?t():setTimeout(t,0)}}class Ne{transition(e,t){var s;if(this.transitionMap.has(t.type))return null===(s=this.transitionMap.get(t.type))||void 0===s?void 0:s(e,t)}constructor(e){this.label=e,this.transitionMap=new Map,this.enterEffects=[],this.exitEffects=[]}on(e,t){return this.transitionMap.set(e,t),this}with(e,t){return[this,e,null!=t?t:[]]}onEnter(e){return this.enterEffects.push(e),this}onExit(e){return this.exitEffects.push(e),this}}class Ee extends Ce{constructor(e){super(!0),this.logger=e,this._pendingEvents=[],this._inTransition=!1}get currentState(){return this._currentState}get currentContext(){return this._currentContext}describe(e){return new Ne(e)}start(e,t){this._currentState=e,this._currentContext=t,this.notify({type:"engineStarted",state:e,context:t})}transition(e){if(!this._currentState)throw this.logger.error("Engine","Finite state machine is not started"),new Error("Start the engine first");if(this._inTransition)return this.logger.trace("Engine",(()=>({messageType:"object",message:e,details:"Event engine in transition. Enqueue received event:"}))),void this._pendingEvents.push(e);this._inTransition=!0,this.logger.trace("Engine",(()=>({messageType:"object",message:e,details:"Event engine received event:"}))),this.notify({type:"eventReceived",event:e});const t=this._currentState.transition(this._currentContext,e);if(t){const[s,n,i]=t;this.logger.trace("Engine",`Exiting state: ${this._currentState.label}`);for(const e of this._currentState.exitEffects)this.notify({type:"invocationDispatched",invocation:e(this._currentContext)});this.logger.trace("Engine",(()=>({messageType:"object",details:`Entering '${s.label}' state with context:`,message:n})));const r=this._currentState;this._currentState=s;const a=this._currentContext;this._currentContext=n,this.notify({type:"transitionDone",fromState:r,fromContext:a,toState:s,toContext:n,event:e});for(const e of i)this.notify({type:"invocationDispatched",invocation:e});for(const e of this._currentState.enterEffects)this.notify({type:"invocationDispatched",invocation:e(this._currentContext)})}else this.logger.warn("Engine",`No transition from '${this._currentState.label}' found for event: ${e.type}`);if(this._inTransition=!1,this._pendingEvents.length>0){const e=this._pendingEvents.shift();e&&(this.logger.trace("Engine",(()=>({messageType:"object",message:e,details:"De-queueing pending event:"}))),this.transition(e))}}}class Te{constructor(e,t){this.dependencies=e,this.logger=t,this.instances=new Map,this.handlers=new Map}on(e,t){this.handlers.set(e,t)}dispatch(e){if(this.logger.trace("Dispatcher",`Process invocation: ${e.type}`),"CANCEL"===e.type){if(this.instances.has(e.payload)){const t=this.instances.get(e.payload);null==t||t.cancel(),this.instances.delete(e.payload)}return}const t=this.handlers.get(e.type);if(!t)throw this.logger.error("Dispatcher",`Unhandled invocation '${e.type}'`),new Error(`Unhandled invocation '${e.type}'`);const s=t(e.payload,this.dependencies);this.logger.trace("Dispatcher",(()=>({messageType:"object",details:"Call invocation handler with parameters:",message:e.payload,ignoredKeys:["abortSignal"]}))),e.managed&&this.instances.set(e.type,s),s.start()}dispose(){for(const[e,t]of this.instances.entries())t.cancel(),this.instances.delete(e)}}function Ie(e,t){const s=function(...s){return{type:e,payload:null==t?void 0:t(...s)}};return s.type=e,s}function Re(e,t){const s=(...s)=>({type:e,payload:t(...s),managed:!1});return s.type=e,s}function Me(e,t){const s=(...s)=>({type:e,payload:t(...s),managed:!0});return s.type=e,s.cancel={type:"CANCEL",payload:e,managed:!1},s}class De extends Error{constructor(){super("The operation was aborted."),this.name="AbortError",Object.setPrototypeOf(this,new.target.prototype)}}class _e extends Ce{constructor(){super(...arguments),this._aborted=!1}get aborted(){return this._aborted}throwIfAborted(){if(this._aborted)throw new De}abort(){this._aborted=!0,this.notify(new De)}}class Ue{constructor(e,t){this.payload=e,this.dependencies=t}}class Ae extends Ue{constructor(e,t,s){super(e,t),this.asyncFunction=s,this.abortSignal=new _e}start(){this.asyncFunction(this.payload,this.abortSignal,this.dependencies).catch((e=>{}))}cancel(){this.abortSignal.abort()}}const $e=e=>(t,s)=>new Ae(t,s,e),Fe=Me("HEARTBEAT",((e,t)=>({channels:e,groups:t}))),xe=Re("LEAVE",((e,t)=>({channels:e,groups:t}))),qe=Re("EMIT_STATUS",(e=>e)),Ge=Me("WAIT",(()=>({}))),Le=Ie("RECONNECT",(()=>({}))),Ke=Ie("DISCONNECT",((e=!1)=>({isOffline:e}))),He=Ie("JOINED",((e,t)=>({channels:e,groups:t}))),Ve=Ie("LEFT",((e,t)=>({channels:e,groups:t}))),We=Ie("LEFT_ALL",((e=!1)=>({isOffline:e}))),Be=Ie("HEARTBEAT_SUCCESS",(e=>({statusCode:e}))),ze=Ie("HEARTBEAT_FAILURE",(e=>e)),Je=Ie("TIMES_UP",(()=>({})));class Ye extends Te{constructor(e,t){super(t,t.config.logger()),this.on(Fe.type,$e(((t,s,n)=>a(this,[t,s,n],void 0,(function*(t,s,{heartbeat:n,presenceState:i,config:r}){s.throwIfAborted();try{yield n(Object.assign(Object.assign({abortSignal:s,channels:t.channels,channelGroups:t.groups},r.maintainPresenceState&&{state:i}),{heartbeat:r.presenceTimeout}));e.transition(Be(200))}catch(t){if(t instanceof c){if(t.status&&t.status.category==o.PNCancelledCategory)return;e.transition(ze(t))}}}))))),this.on(xe.type,$e(((e,t,s)=>a(this,[e,t,s],void 0,(function*(e,t,{leave:s,config:n}){if(!n.suppressLeaveEvents)try{s({channels:e.channels,channelGroups:e.groups})}catch(e){}}))))),this.on(Ge.type,$e(((t,s,n)=>a(this,[t,s,n],void 0,(function*(t,s,{heartbeatDelay:n}){const{cancelDelay:i,promise:r}=n();s.throwIfAborted();const a=s.subscribe((()=>i()));return yield r,a(),s.throwIfAborted(),e.transition(Je())}))))),this.on(qe.type,$e(((e,t,s)=>a(this,[e,t,s],void 0,(function*(e,t,{emitStatus:s,config:n}){n.announceFailedHeartbeats&&!0===(null==e?void 0:e.error)?s(Object.assign(Object.assign({},e),{operation:L.PNHeartbeatOperation})):n.announceSuccessfulHeartbeats&&200===e.statusCode&&s(Object.assign(Object.assign({},e),{error:!1,operation:L.PNHeartbeatOperation,category:o.PNAcknowledgmentCategory}))})))))}}const Qe=new Ne("HEARTBEAT_INACTIVE"),Xe=new Ne("HEARTBEATING"),Ze=new Ne("HEARTBEAT_COOLDOWN"),et=new Ne("HEARTBEAT_STOPPED"),tt=new Ne("HEARTBEAT_FAILED");Qe.on(He.type,((e,t)=>Xe.with({channels:t.payload.channels,groups:t.payload.groups}))),Xe.onEnter((e=>Fe(e.channels,e.groups))),Xe.onExit((()=>Fe.cancel)),Xe.on(Be.type,((e,t)=>Ze.with({channels:e.channels,groups:e.groups},[qe(Object.assign({},t.payload))]))),Xe.on(He.type,((e,t)=>Xe.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),Xe.on(Ve.type,((e,t)=>Xe.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))},[xe(t.payload.channels,t.payload.groups)]))),Xe.on(ze.type,((e,t)=>tt.with(Object.assign({},e),[...t.payload.status?[qe(Object.assign({},t.payload.status))]:[]]))),Xe.on(Ke.type,((e,t)=>et.with({channels:e.channels,groups:e.groups},[...t.payload.isOffline?[]:[xe(e.channels,e.groups)]]))),Xe.on(We.type,((e,t)=>Qe.with(void 0,[...t.payload.isOffline?[]:[xe(e.channels,e.groups)]]))),Ze.onEnter((()=>Ge())),Ze.onExit((()=>Ge.cancel)),Ze.on(Je.type,((e,t)=>Xe.with({channels:e.channels,groups:e.groups}))),Ze.on(He.type,((e,t)=>Xe.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),Ze.on(Ve.type,((e,t)=>Xe.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))},[xe(t.payload.channels,t.payload.groups)]))),Ze.on(Ke.type,((e,t)=>et.with({channels:e.channels,groups:e.groups},[...t.payload.isOffline?[]:[xe(e.channels,e.groups)]]))),Ze.on(We.type,((e,t)=>Qe.with(void 0,[...t.payload.isOffline?[]:[xe(e.channels,e.groups)]]))),et.on(He.type,((e,t)=>et.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),et.on(Ve.type,((e,t)=>et.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))}))),et.on(Le.type,((e,t)=>Xe.with({channels:e.channels,groups:e.groups}))),et.on(We.type,((e,t)=>Qe.with(void 0))),tt.on(He.type,((e,t)=>Xe.with({channels:[...e.channels,...t.payload.channels.filter((t=>!e.channels.includes(t)))],groups:[...e.groups,...t.payload.groups.filter((t=>!e.groups.includes(t)))]}))),tt.on(Ve.type,((e,t)=>Xe.with({channels:e.channels.filter((e=>!t.payload.channels.includes(e))),groups:e.groups.filter((e=>!t.payload.groups.includes(e)))},[xe(t.payload.channels,t.payload.groups)]))),tt.on(Le.type,((e,t)=>Xe.with({channels:e.channels,groups:e.groups}))),tt.on(Ke.type,((e,t)=>et.with({channels:e.channels,groups:e.groups},[...t.payload.isOffline?[]:[xe(e.channels,e.groups)]]))),tt.on(We.type,((e,t)=>Qe.with(void 0,[...t.payload.isOffline?[]:[xe(e.channels,e.groups)]])));class st{get _engine(){return this.engine}constructor(e){this.dependencies=e,this.channels=[],this.groups=[],this.engine=new Ee(e.config.logger()),this.dispatcher=new Ye(this.engine,e),e.config.logger().debug("PresenceEventEngine","Create presence event engine."),this._unsubscribeEngine=this.engine.subscribe((e=>{"invocationDispatched"===e.type&&this.dispatcher.dispatch(e.invocation)})),this.engine.start(Qe,void 0)}join({channels:e,groups:t}){this.channels=[...this.channels,...(null!=e?e:[]).filter((e=>!this.channels.includes(e)))],this.groups=[...this.groups,...(null!=t?t:[]).filter((e=>!this.groups.includes(e)))],0===this.channels.length&&0===this.groups.length||this.engine.transition(He(this.channels.slice(0),this.groups.slice(0)))}leave({channels:e,groups:t}){e&&(this.channels=this.channels.filter((t=>!e.includes(t)))),t&&(this.groups=this.groups.filter((e=>!t.includes(e)))),this.dependencies.presenceState&&(null==e||e.forEach((e=>delete this.dependencies.presenceState[e])),null==t||t.forEach((e=>delete this.dependencies.presenceState[e]))),this.engine.transition(Ve(null!=e?e:[],null!=t?t:[]))}leaveAll(e=!1){this.dependencies.presenceState&&(this.channels.forEach((e=>delete this.dependencies.presenceState[e])),this.groups.forEach((e=>delete this.dependencies.presenceState[e]))),this.channels=[],this.groups=[],this.engine.transition(We(e))}reconnect(){this.engine.transition(Le())}disconnect(e=!1){this.engine.transition(Ke(e))}dispose(){this.disconnect(!0),this._unsubscribeEngine(),this.dispatcher.dispose()}}const nt=Ie("SUBSCRIPTION_CHANGED",((e,t,s=!1)=>({channels:e,groups:t,isOffline:s}))),it=Ie("SUBSCRIPTION_RESTORED",((e,t,s,n)=>({channels:e,groups:t,cursor:{timetoken:s,region:null!=n?n:0}}))),rt=Ie("HANDSHAKE_SUCCESS",(e=>e)),at=Ie("HANDSHAKE_FAILURE",(e=>e)),ot=Ie("RECEIVE_SUCCESS",((e,t)=>({cursor:e,events:t}))),ct=Ie("RECEIVE_FAILURE",(e=>e)),ut=Ie("DISCONNECT",((e=!1)=>({isOffline:e}))),lt=Ie("RECONNECT",((e,t)=>({cursor:{timetoken:null!=e?e:"",region:null!=t?t:0}}))),ht=Ie("UNSUBSCRIBE_ALL",(()=>({}))),dt=new Ne("UNSUBSCRIBED"),pt=new Ne("HANDSHAKING"),gt=new Ne("HANDSHAKE_STOPPED"),bt=new Ne("HANDSHAKE_FAILED"),yt=new Ne("RECEIVING"),mt=new Ne("RECEIVE_STOPPED"),ft=new Ne("RECEIVE_FAILED");dt.on(nt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,onDemand:!0}))),dt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region},onDemand:!0})));const vt=Me("HANDSHAKE",((e,t,s)=>({channels:e,groups:t,onDemand:s}))),Ot=Me("RECEIVE_MESSAGES",((e,t,s,n)=>({channels:e,groups:t,cursor:s,onDemand:n}))),St=Re("EMIT_MESSAGES",((e,t)=>({cursor:e,events:t}))),jt=Re("EMIT_STATUS",(e=>e));pt.onEnter((e=>{var t;return vt(e.channels,e.groups,null!==(t=e.onDemand)&&void 0!==t&&t)})),pt.onExit((()=>vt.cancel)),pt.on(nt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,onDemand:!0}))),pt.on(rt.type,((e,{payload:t})=>{var s,n,i,r,a;return yt.with({channels:e.channels,groups:e.groups,cursor:{timetoken:(null===(s=e.cursor)||void 0===s?void 0:s.timetoken)?null===(n=e.cursor)||void 0===n?void 0:n.timetoken:t.timetoken,region:t.region},referenceTimetoken:I(t.timetoken,null===(i=e.cursor)||void 0===i?void 0:i.timetoken)},[jt({category:o.PNConnectedCategory,affectedChannels:e.channels.slice(0),affectedChannelGroups:e.groups.slice(0),operation:L.PNSubscribeOperation,currentTimetoken:(null===(r=e.cursor)||void 0===r?void 0:r.timetoken)?null===(a=e.cursor)||void 0===a?void 0:a.timetoken:t.timetoken})])})),pt.on(at.type,((e,t)=>{var s;return bt.with(Object.assign(Object.assign({},e),{reason:t.payload}),[jt({category:o.PNConnectionErrorCategory,error:null===(s=t.payload.status)||void 0===s?void 0:s.category})])})),pt.on(ut.type,((e,t)=>{var s;if(t.payload.isOffline){const t=q.create(new Error("Network connection error")).toPubNubError(L.PNSubscribeOperation);return bt.with(Object.assign(Object.assign({},e),{reason:t}),[jt({category:o.PNConnectionErrorCategory,error:null===(s=t.status)||void 0===s?void 0:s.category})])}return gt.with(Object.assign({},e))})),pt.on(it.type,((e,{payload:t})=>{var s;return 0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||(null===(s=null==e?void 0:e.cursor)||void 0===s?void 0:s.region)||0},onDemand:!0})})),pt.on(ht.type,(e=>dt.with())),gt.on(nt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:e.cursor}))),gt.on(lt.type,((e,{payload:t})=>pt.with(Object.assign(Object.assign({},e),{cursor:t.cursor||e.cursor,onDemand:!0})))),gt.on(it.type,((e,{payload:t})=>{var s;return 0===t.channels.length&&0===t.groups.length?dt.with(void 0):gt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||(null===(s=e.cursor)||void 0===s?void 0:s.region)||0}})})),gt.on(ht.type,(e=>dt.with())),bt.on(nt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,onDemand:!0}))),bt.on(lt.type,((e,{payload:t})=>pt.with(Object.assign(Object.assign({},e),{cursor:t.cursor||e.cursor,onDemand:!0})))),bt.on(it.type,((e,{payload:t})=>{var s,n;return 0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region?t.cursor.region:null!==(n=null===(s=null==e?void 0:e.cursor)||void 0===s?void 0:s.region)&&void 0!==n?n:0},onDemand:!0})})),bt.on(ht.type,(e=>dt.with())),yt.onEnter((e=>{var t;return Ot(e.channels,e.groups,e.cursor,null!==(t=e.onDemand)&&void 0!==t&&t)})),yt.onExit((()=>Ot.cancel)),yt.on(ot.type,((e,{payload:t})=>yt.with({channels:e.channels,groups:e.groups,cursor:t.cursor,referenceTimetoken:I(t.cursor.timetoken)},[St(e.cursor,t.events)]))),yt.on(nt.type,((e,{payload:t})=>{var s;if(0===t.channels.length&&0===t.groups.length){let e;return t.isOffline&&(e=null===(s=q.create(new Error("Network connection error")).toPubNubError(L.PNSubscribeOperation).status)||void 0===s?void 0:s.category),dt.with(void 0,[jt(Object.assign({category:t.isOffline?o.PNDisconnectedUnexpectedlyCategory:o.PNDisconnectedCategory,operation:L.PNUnsubscribeOperation},e?{error:e}:{}))])}return yt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,referenceTimetoken:e.referenceTimetoken,onDemand:!0},[jt({category:o.PNSubscriptionChangedCategory,affectedChannels:t.channels.slice(0),affectedChannelGroups:t.groups.slice(0),currentTimetoken:e.cursor.timetoken})])})),yt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0,[jt({category:o.PNDisconnectedCategory})]):yt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||e.cursor.region},referenceTimetoken:I(e.cursor.timetoken,`${t.cursor.timetoken}`,e.referenceTimetoken),onDemand:!0},[jt({category:o.PNSubscriptionChangedCategory,affectedChannels:t.channels.slice(0),affectedChannelGroups:t.groups.slice(0),currentTimetoken:t.cursor.timetoken})]))),yt.on(ct.type,((e,{payload:t})=>{var s;return ft.with(Object.assign(Object.assign({},e),{reason:t}),[jt({category:o.PNDisconnectedUnexpectedlyCategory,error:null===(s=t.status)||void 0===s?void 0:s.category})])})),yt.on(ut.type,((e,t)=>{var s;if(t.payload.isOffline){const t=q.create(new Error("Network connection error")).toPubNubError(L.PNSubscribeOperation);return ft.with(Object.assign(Object.assign({},e),{reason:t}),[jt({category:o.PNDisconnectedUnexpectedlyCategory,operation:L.PNSubscribeOperation,error:null===(s=t.status)||void 0===s?void 0:s.category})])}return mt.with(Object.assign({},e),[jt({category:o.PNDisconnectedCategory,operation:L.PNSubscribeOperation})])})),yt.on(ht.type,(e=>dt.with(void 0,[jt({category:o.PNDisconnectedCategory,operation:L.PNUnsubscribeOperation})]))),mt.on(nt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):mt.with({channels:t.channels,groups:t.groups,cursor:e.cursor}))),mt.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):mt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||e.cursor.region}}))),mt.on(lt.type,((e,{payload:t})=>{var s;return pt.with({channels:e.channels,groups:e.groups,cursor:{timetoken:t.cursor.timetoken?null===(s=t.cursor)||void 0===s?void 0:s.timetoken:e.cursor.timetoken,region:t.cursor.region||e.cursor.region},onDemand:!0})})),mt.on(ht.type,(()=>dt.with(void 0))),ft.on(lt.type,((e,{payload:t})=>{var s;return pt.with({channels:e.channels,groups:e.groups,cursor:{timetoken:t.cursor.timetoken?null===(s=t.cursor)||void 0===s?void 0:s.timetoken:e.cursor.timetoken,region:t.cursor.region||e.cursor.region},onDemand:!0})})),ft.on(nt.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,cursor:e.cursor,onDemand:!0}))),ft.on(it.type,((e,{payload:t})=>0===t.channels.length&&0===t.groups.length?dt.with(void 0):pt.with({channels:t.channels,groups:t.groups,cursor:{timetoken:`${t.cursor.timetoken}`,region:t.cursor.region||e.cursor.region},onDemand:!0}))),ft.on(ht.type,(e=>dt.with(void 0)));class wt extends Te{constructor(e,t){super(t,t.config.logger()),this.on(vt.type,$e(((t,s,n)=>a(this,[t,s,n],void 0,(function*(t,s,{handshake:n,presenceState:i,config:r}){s.throwIfAborted();try{const a=yield n(Object.assign(Object.assign({abortSignal:s,channels:t.channels,channelGroups:t.groups,filterExpression:r.filterExpression},r.maintainPresenceState&&{state:i}),{onDemand:t.onDemand}));return e.transition(rt(a))}catch(t){if(t instanceof c){if(t.status&&t.status.category==o.PNCancelledCategory)return;return e.transition(at(t))}}}))))),this.on(Ot.type,$e(((t,s,n)=>a(this,[t,s,n],void 0,(function*(t,s,{receiveMessages:n,config:i}){s.throwIfAborted();try{const r=yield n({abortSignal:s,channels:t.channels,channelGroups:t.groups,timetoken:t.cursor.timetoken,region:t.cursor.region,filterExpression:i.filterExpression,onDemand:t.onDemand});e.transition(ot(r.cursor,r.messages))}catch(t){if(t instanceof c){if(t.status&&t.status.category==o.PNCancelledCategory)return;if(!s.aborted)return e.transition(ct(t))}}}))))),this.on(St.type,$e(((e,t,s)=>a(this,[e,t,s],void 0,(function*({cursor:e,events:t},s,{emitMessages:n}){t.length>0&&n(e,t)}))))),this.on(jt.type,$e(((e,t,s)=>a(this,[e,t,s],void 0,(function*(e,t,{emitStatus:s}){return s(e)})))))}}class kt{get _engine(){return this.engine}constructor(e){this.channels=[],this.groups=[],this.dependencies=e,this.engine=new Ee(e.config.logger()),this.dispatcher=new wt(this.engine,e),e.config.logger().debug("EventEngine","Create subscribe event engine."),this._unsubscribeEngine=this.engine.subscribe((e=>{"invocationDispatched"===e.type&&this.dispatcher.dispatch(e.invocation)})),this.engine.start(dt,void 0)}get subscriptionTimetoken(){const e=this.engine.currentState;if(!e)return;let t,s="0";if(e.label===yt.label){const e=this.engine.currentContext;s=e.cursor.timetoken,t=e.referenceTimetoken}return T(s,null!=t?t:"0")}subscribe({channels:e,channelGroups:t,timetoken:s,withPresence:n}){var i;const r=null==e?void 0:e.some((e=>!this.channels.includes(e))),a=null==t?void 0:t.some((e=>!this.groups.includes(e))),c=r||a;if(this.channels=[...this.channels,...null!=e?e:[]],this.groups=[...this.groups,...null!=t?t:[]],n&&(this.channels.map((e=>this.channels.push(`${e}-pnpres`))),this.groups.map((e=>this.groups.push(`${e}-pnpres`)))),s)this.engine.transition(it(Array.from(new Set([...this.channels,...null!=e?e:[]])),Array.from(new Set([...this.groups,...null!=t?t:[]])),s));else if(c)this.engine.transition(nt(Array.from(new Set([...this.channels,...null!=e?e:[]])),Array.from(new Set([...this.groups,...null!=t?t:[]]))));else{this.dependencies.config.logger().debug("EventEngine","Skipping state transition - all channels/groups already subscribed. Emitting SubscriptionChanged event.");const e=this.engine.currentState,t=this.engine.currentContext;let s="0";if((null==e?void 0:e.label)===yt.label&&t){s=null===(i=t.cursor)||void 0===i?void 0:i.timetoken}this.dependencies.emitStatus({category:o.PNSubscriptionChangedCategory,affectedChannels:Array.from(new Set(this.channels)),affectedChannelGroups:Array.from(new Set(this.groups)),currentTimetoken:s})}this.dependencies.join&&this.dependencies.join({channels:Array.from(new Set(this.channels.filter((e=>!e.endsWith("-pnpres"))))),groups:Array.from(new Set(this.groups.filter((e=>!e.endsWith("-pnpres")))))})}unsubscribe({channels:e=[],channelGroups:t=[]}){const s=C(this.channels,[...e,...e.map((e=>`${e}-pnpres`))]),n=C(this.groups,[...t,...t.map((e=>`${e}-pnpres`))]);if(new Set(this.channels).size!==new Set(s).size||new Set(this.groups).size!==new Set(n).size){const i=N(this.channels,e),r=N(this.groups,t);this.dependencies.presenceState&&(null==i||i.forEach((e=>delete this.dependencies.presenceState[e])),null==r||r.forEach((e=>delete this.dependencies.presenceState[e]))),this.channels=s,this.groups=n,this.engine.transition(nt(Array.from(new Set(this.channels.slice(0))),Array.from(new Set(this.groups.slice(0))))),this.dependencies.leave&&this.dependencies.leave({channels:i.slice(0),groups:r.slice(0)})}}unsubscribeAll(e=!1){const t=this.getSubscribedChannelGroups(),s=this.getSubscribedChannels();this.channels=[],this.groups=[],this.dependencies.presenceState&&Object.keys(this.dependencies.presenceState).forEach((e=>{delete this.dependencies.presenceState[e]})),this.engine.transition(nt(this.channels.slice(0),this.groups.slice(0),e)),this.dependencies.leaveAll&&this.dependencies.leaveAll({channels:s,groups:t,isOffline:e})}reconnect({timetoken:e,region:t}){const s=this.getSubscribedChannels(),n=this.getSubscribedChannels();this.engine.transition(lt(e,t)),this.dependencies.presenceReconnect&&this.dependencies.presenceReconnect({channels:n,groups:s})}disconnect(e=!1){const t=this.getSubscribedChannels(),s=this.getSubscribedChannels();this.engine.transition(ut(e)),this.dependencies.presenceDisconnect&&this.dependencies.presenceDisconnect({channels:s,groups:t,isOffline:e})}getSubscribedChannels(){return Array.from(new Set(this.channels.slice(0)))}getSubscribedChannelGroups(){return Array.from(new Set(this.groups.slice(0)))}dispose(){this.disconnect(!0),this._unsubscribeEngine(),this.dispatcher.dispose()}}class Pt extends de{constructor(e){var t;const s=null!==(t=e.sendByPost)&&void 0!==t&&t;super({method:s?ce.POST:ce.GET,compressible:s}),this.parameters=e,this.parameters.sendByPost=s}operation(){return L.PNPublishOperation}validate(){const{message:e,channel:t,keySet:{publishKey:s}}=this.parameters;return t?e?s?void 0:"Missing 'publishKey'":"Missing 'message'":"Missing 'channel'"}prepare(){const{crypto:e,message:t}=this.parameters;if(e)return D(e,t).then((e=>{this.encryptedPayload=e}))}parse(e){return a(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[2]}}))}get path(){const{message:e,channel:t,keySet:s}=this.parameters,n=this.prepareMessagePayload(e);return`/publish/${s.publishKey}/${s.subscribeKey}/0/${k(t)}/0${this.parameters.sendByPost?"":`/${k(n)}`}`}get queryParameters(){const{customMessageType:e,meta:t,replicate:s,storeInHistory:n,ttl:i}=this.parameters,r={};return e&&(r.custom_message_type=e),void 0!==n&&(r.store=n?"1":"0"),void 0!==i&&(r.ttl=i),void 0===s||s||(r.norep="true"),t&&"object"==typeof t&&(r.meta=JSON.stringify(t)),r}get headers(){var e;return this.parameters.sendByPost?Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"}):super.headers}get body(){return this.prepareMessagePayload(this.parameters.message)}prepareMessagePayload(e){if(!this.parameters.crypto)return JSON.stringify(e)||"";if(void 0===this.encryptedPayload)throw new Error("Encryption error: request payload not prepared");return this.encryptedPayload}}class Ct extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNSignalOperation}validate(){const{message:e,channel:t,keySet:{publishKey:s}}=this.parameters;return t?e?s?void 0:"Missing 'publishKey'":"Missing 'message'":"Missing 'channel'"}parse(e){return a(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[2]}}))}get path(){const{keySet:{publishKey:e,subscribeKey:t},channel:s,message:n}=this.parameters,i=JSON.stringify(n);return`/signal/${e}/${t}/0/${k(s)}/0/${k(i)}`}get queryParameters(){const{customMessageType:e}=this.parameters,t={};return e&&(t.custom_message_type=e),t}}class Nt extends ye{operation(){return L.PNReceiveMessagesOperation}validate(){const e=super.validate();return e||(this.parameters.timetoken?this.parameters.region?void 0:"region can not be empty":"timetoken can not be empty")}get path(){const{keySet:{subscribeKey:e},channels:t=[]}=this.parameters;return`/v2/subscribe/${e}/${P(t.sort(),",")}/0`}get queryParameters(){const{channelGroups:e,filterExpression:t,timetoken:s,region:n,heartbeat:i,onDemand:r}=this.parameters,a={ee:""};return r&&(a["on-demand"]=1),e&&e.length>0&&(a["channel-group"]=e.sort().join(",")),t&&t.length>0&&(a["filter-expr"]=t),"string"==typeof s?s&&"0"!==s&&s.length>0&&(a.tt=s):s&&s>0&&(a.tt=s),n&&(a.tr=n),i&&(a.heartbeat=i),a}}class Et extends ye{operation(){return L.PNHandshakeOperation}get path(){const{keySet:{subscribeKey:e},channels:t=[]}=this.parameters;return`/v2/subscribe/${e}/${P(t.sort(),",")}/0`}get queryParameters(){const{channelGroups:e,heartbeat:t,filterExpression:s,state:n,onDemand:i}=this.parameters,r={ee:""};return i&&(r["on-demand"]=1),e&&e.length>0&&(r["channel-group"]=e.sort().join(",")),s&&s.length>0&&(r["filter-expr"]=s),n&&Object.keys(n).length>0&&(r.state=JSON.stringify(n)),t&&(r.heartbeat=t),r}}var Tt;!function(e){e[e.Channel=0]="Channel",e[e.ChannelGroup=1]="ChannelGroup"}(Tt||(Tt={}));class It{constructor({channels:e,channelGroups:t}){this.isEmpty=!0,this._channelGroups=new Set((null!=t?t:[]).filter((e=>e.length>0))),this._channels=new Set((null!=e?e:[]).filter((e=>e.length>0))),this.isEmpty=0===this._channels.size&&0===this._channelGroups.size}get length(){return this.isEmpty?0:this._channels.size+this._channelGroups.size}get channels(){return this.isEmpty?[]:Array.from(this._channels)}get channelGroups(){return this.isEmpty?[]:Array.from(this._channelGroups)}contains(e){return!this.isEmpty&&(this._channels.has(e)||this._channelGroups.has(e))}with(e){return new It({channels:[...this._channels,...e._channels],channelGroups:[...this._channelGroups,...e._channelGroups]})}without(e){return new It({channels:[...this._channels].filter((t=>!e._channels.has(t))),channelGroups:[...this._channelGroups].filter((t=>!e._channelGroups.has(t)))})}add(e){return e._channelGroups.size>0&&(this._channelGroups=new Set([...this._channelGroups,...e._channelGroups])),e._channels.size>0&&(this._channels=new Set([...this._channels,...e._channels])),this.isEmpty=0===this._channels.size&&0===this._channelGroups.size,this}remove(e){return e._channelGroups.size>0&&(this._channelGroups=new Set([...this._channelGroups].filter((t=>!e._channelGroups.has(t))))),e._channels.size>0&&(this._channels=new Set([...this._channels].filter((t=>!e._channels.has(t))))),this}removeAll(){return this._channels.clear(),this._channelGroups.clear(),this.isEmpty=!0,this}toString(){return`SubscriptionInput { channels: [${this.channels.join(", ")}], channelGroups: [${this.channelGroups.join(", ")}], is empty: ${this.isEmpty?"true":"false"}} }`}}class Rt{constructor(e,t,s,n){this._isSubscribed=!1,this.clones={},this.parents=[],this._id=ne.createUUID(),this.referenceTimetoken=n,this.subscriptionInput=t,this.options=s,this.client=e}get id(){return this._id}get isLastClone(){return 1===Object.keys(this.clones).length}get isSubscribed(){return!!this._isSubscribed||this.parents.length>0&&this.parents.some((e=>e.isSubscribed))}set isSubscribed(e){this.isSubscribed!==e&&(this._isSubscribed=e)}addParentState(e){this.parents.includes(e)||this.parents.push(e)}removeParentState(e){const t=this.parents.indexOf(e);-1!==t&&this.parents.splice(t,1)}storeClone(e,t){this.clones[e]||(this.clones[e]=t)}}class Mt{constructor(e,t="Subscription"){this.subscriptionType=t,this.id=ne.createUUID(),this.eventDispatcher=new fe,this._state=e}get state(){return this._state}get channels(){return this.state.subscriptionInput.channels.slice(0)}get channelGroups(){return this.state.subscriptionInput.channelGroups.slice(0)}set onMessage(e){this.eventDispatcher.onMessage=e}set onPresence(e){this.eventDispatcher.onPresence=e}set onSignal(e){this.eventDispatcher.onSignal=e}set onObjects(e){this.eventDispatcher.onObjects=e}set onMessageAction(e){this.eventDispatcher.onMessageAction=e}set onFile(e){this.eventDispatcher.onFile=e}set onDataSync(e){this.eventDispatcher.onDataSync=e}addListener(e){this.eventDispatcher.addListener(e)}removeListener(e){this.eventDispatcher.removeListener(e)}removeAllListeners(){this.eventDispatcher.removeAllListeners()}handleEvent(e,t){var s;if((!this.state.cursor||e>this.state.cursor)&&(this.state.cursor=e),this.state.referenceTimetoken&&t.data.timetoken({messageType:"text",message:`Event timetoken (${t.data.timetoken}) is older than reference timetoken (${this.state.referenceTimetoken}) for ${this.id} subscription object. Ignoring event.`})));if((null===(s=this.state.options)||void 0===s?void 0:s.filter)&&!this.state.options.filter(t))return void this.state.client.logger.trace(this.subscriptionType,`Event filtered out by filter function for ${this.id} subscription object. Ignoring event.`);const n=Object.values(this.state.clones);n.length>0&&this.state.client.logger.trace(this.subscriptionType,`Notify ${this.id} subscription object clones (count: ${n.length}) about received event.`),n.forEach((e=>e.eventDispatcher.handleEvent(t)))}dispose(){const e=Object.keys(this.state.clones);e.length>1?(this.state.client.logger.debug(this.subscriptionType,`Remove subscription object clone on dispose: ${this.id}`),delete this.state.clones[this.id]):1===e.length&&this.state.clones[this.id]&&(this.state.client.logger.debug(this.subscriptionType,`Unsubscribe subscription object on dispose: ${this.id}`),this.unsubscribe())}invalidate(e=!1){this.state._isSubscribed=!1,e&&(delete this.state.clones[this.id],0===Object.keys(this.state.clones).length&&(this.state.client.logger.trace(this.subscriptionType,"Last clone removed. Reset shared subscription state."),this.state.subscriptionInput.removeAll(),this.state.parents=[]))}subscribe(e){this.state.isSubscribed?this.state.client.logger.trace(this.subscriptionType,"Already subscribed. Ignoring subscribe request."):(this.state.client.logger.debug(this.subscriptionType,(()=>e?{messageType:"object",message:e,details:"Subscribe with parameters:"}:{messageType:"text",message:"Subscribe"})),this.state.isSubscribed=!0,this.updateSubscription({subscribing:!0,timetoken:null==e?void 0:e.timetoken}))}unsubscribe(){if(!this.state._isSubscribed||this.state.isSubscribed){if(!this.state._isSubscribed&&this.state.parents.length>0&&this.state.isSubscribed)return void this.state.client.logger.warn(this.subscriptionType,(()=>({messageType:"object",details:"Subscription is subscribed as part of a subscription set. Remove from active sets to unsubscribe:",message:this.state.parents.filter((e=>e.isSubscribed))})));if(!this.state._isSubscribed)return void this.state.client.logger.trace(this.subscriptionType,"Not subscribed. Ignoring unsubscribe request.")}this.state.client.logger.debug(this.subscriptionType,"Unsubscribe"),this.state.isSubscribed=!1,delete this.state.cursor,this.updateSubscription({subscribing:!1})}updateSubscription(e){var t,s;(null==e?void 0:e.timetoken)&&((null===(t=this.state.cursor)||void 0===t?void 0:t.timetoken)&&"0"!==(null===(s=this.state.cursor)||void 0===s?void 0:s.timetoken)?"0"!==e.timetoken&&e.timetoken>this.state.cursor.timetoken&&(this.state.cursor.timetoken=e.timetoken):this.state.cursor={timetoken:e.timetoken});const n=e.subscriptions&&e.subscriptions.length>0?e.subscriptions:void 0;e.subscribing?this.register(Object.assign(Object.assign({},e.timetoken?{cursor:this.state.cursor}:{}),n?{subscriptions:n}:{})):this.unregister(n)}}class Dt extends Rt{constructor(e){const t=new It({});e.subscriptions.forEach((e=>t.add(e.state.subscriptionInput))),super(e.client,t,e.options,e.client.subscriptionTimetoken),this.subscriptions=e.subscriptions}addSubscription(e){this.subscriptions.includes(e)||(e.state.addParentState(this),this.subscriptions.push(e),this.subscriptionInput.add(e.state.subscriptionInput))}removeSubscription(e,t){const s=this.subscriptions.indexOf(e);-1!==s&&(this.subscriptions.splice(s,1),t||e.state.removeParentState(this),this.subscriptionInput.remove(e.state.subscriptionInput))}removeAllSubscriptions(){this.subscriptions.forEach((e=>e.state.removeParentState(this))),this.subscriptions.splice(0,this.subscriptions.length),this.subscriptionInput.removeAll()}}class _t extends Mt{constructor(e){let t;if("client"in e){let s=[];!e.subscriptions&&e.entities?e.entities.forEach((t=>s.push(t.subscription(e.options)))):e.subscriptions&&(s=e.subscriptions),t=new Dt({client:e.client,subscriptions:s,options:e.options}),s.forEach((e=>e.state.addParentState(t))),t.client.logger.debug("SubscriptionSet",(()=>({messageType:"object",details:"Create subscription set with parameters:",message:Object.assign({subscriptions:t.subscriptions},e.options?e.options:{})})))}else t=e.state,t.client.logger.debug("SubscriptionSet","Create subscription set clone");super(t,"SubscriptionSet"),this.state.storeClone(this.id,this),t.subscriptions.forEach((e=>e.addParentSet(this)))}get state(){return super.state}get subscriptions(){return this.state.subscriptions.slice(0)}handleEvent(e,t){var s;this.state.subscriptionInput.contains(null!==(s=t.data.subscription)&&void 0!==s?s:t.data.channel)&&(this.state._isSubscribed?(super.handleEvent(e,t),this.state.subscriptions.length>0&&this.state.client.logger.trace(this.subscriptionType,`Notify ${this.id} subscription set subscriptions (count: ${this.state.subscriptions.length}) about received event.`),this.state.subscriptions.forEach((s=>s.handleEvent(e,t)))):this.state.client.logger.trace(this.subscriptionType,`Subscription set ${this.id} is not subscribed. Ignoring event.`))}subscriptionInput(e=!1){let t=this.state.subscriptionInput;return this.state.subscriptions.forEach((s=>{e&&s.state.entity.subscriptionsCount>0&&(t=t.without(s.state.subscriptionInput))})),t}cloneEmpty(){return new _t({state:this.state})}dispose(){const e=this.state.isLastClone;this.state.subscriptions.forEach((t=>{t.removeParentSet(this),e&&t.state.removeParentState(this.state)})),super.dispose()}invalidate(e=!1){(e?this.state.subscriptions.slice(0):this.state.subscriptions).forEach((t=>{e&&(t.state.entity.decreaseSubscriptionCount(this.state.id),t.removeParentSet(this)),t.invalidate(e)})),e&&this.state.removeAllSubscriptions(),super.invalidate()}addSubscription(e){this.addSubscriptions([e])}addSubscriptions(e){const t=[],s=[];this.state.client.logger.debug(this.subscriptionType,(()=>{const t=[],s=[];return e.forEach((e=>{this.state.subscriptions.includes(e)?t.push(e):s.push(e)})),{messageType:"object",details:`Add subscriptions to ${this.id} (subscriptions count: ${this.state.subscriptions.length+s.length}):`,message:{addedSubscriptions:s,ignoredSubscriptions:t}}})),e.filter((e=>!this.state.subscriptions.includes(e))).forEach((e=>{e.state.isSubscribed?s.push(e):t.push(e),e.addParentSet(this),this.state.addSubscription(e)})),0===s.length&&0===t.length||!this.state.isSubscribed||(s.forEach((({state:e})=>e.entity.increaseSubscriptionCount(this.state.id))),t.length>0&&this.updateSubscription({subscribing:!0,subscriptions:t}))}removeSubscription(e){this.removeSubscriptions([e])}removeSubscriptions(e){const t=[];this.state.client.logger.debug(this.subscriptionType,(()=>{const t=[],s=[];return e.forEach((e=>{this.state.subscriptions.includes(e)?s.push(e):t.push(e)})),{messageType:"object",details:`Remove subscriptions from ${this.id} (subscriptions count: ${this.state.subscriptions.length}):`,message:{removedSubscriptions:s,ignoredSubscriptions:t}}})),e.filter((e=>this.state.subscriptions.includes(e))).forEach((e=>{e.state.isSubscribed&&t.push(e),e.removeParentSet(this),this.state.removeSubscription(e,e.parentSetsCount>1)})),0!==t.length&&this.state.isSubscribed&&this.updateSubscription({subscribing:!1,subscriptions:t})}addSubscriptionSet(e){this.addSubscriptions(e.subscriptions)}removeSubscriptionSet(e){this.removeSubscriptions(e.subscriptions)}register(e){var t;const s=null!==(t=e.subscriptions)&&void 0!==t?t:this.state.subscriptions;s.forEach((({state:e})=>e.entity.increaseSubscriptionCount(this.state.id))),this.state.client.logger.trace(this.subscriptionType,(()=>({messageType:"text",message:`Register subscription for real-time events: ${this}`}))),this.state.client.registerEventHandleCapable(this,e.cursor,s)}unregister(e){const t=null!=e?e:this.state.subscriptions;t.forEach((({state:e})=>e.entity.decreaseSubscriptionCount(this.state.id))),this.state.client.logger.trace(this.subscriptionType,(()=>e?{messageType:"object",message:{subscription:this,subscriptions:e},details:"Unregister subscriptions of subscription set from real-time events:"}:{messageType:"text",message:`Unregister subscription from real-time events: ${this}`})),this.state.client.unregisterEventHandleCapable(this,t)}toString(){const e=this.state;return`${this.subscriptionType} { id: ${this.id}, stateId: ${e.id}, clonesCount: ${Object.keys(this.state.clones).length}, isSubscribed: ${e.isSubscribed}, subscriptions: [${e.subscriptions.map((e=>e.toString())).join(", ")}] }`}}class Ut extends Rt{constructor(e){var t,s;const n=e.entity.subscriptionNames(null!==(s=null===(t=e.options)||void 0===t?void 0:t.receivePresenceEvents)&&void 0!==s&&s),i=new It({[e.entity.subscriptionType==Tt.Channel?"channels":"channelGroups"]:n});super(e.client,i,e.options,e.client.subscriptionTimetoken),this.entity=e.entity}}class At extends Mt{constructor(e){"client"in e?e.client.logger.debug("Subscription",(()=>({messageType:"object",details:"Create subscription with parameters:",message:Object.assign({entity:e.entity},e.options?e.options:{})}))):e.state.client.logger.debug("Subscription","Create subscription clone"),super("state"in e?e.state:new Ut(e)),this.parents=[],this.handledUpdates=[],this.state.storeClone(this.id,this)}get state(){return super.state}get parentSetsCount(){return this.parents.length}handleEvent(e,t){var s,n;if(this.state.isSubscribed&&this.state.subscriptionInput.contains(null!==(s=t.data.subscription)&&void 0!==s?s:t.data.channel)){if(this.parentSetsCount>0){const e=M(t.data);if(this.handledUpdates.includes(e))return void this.state.client.logger.trace(this.subscriptionType,`Event (${e}) already handled by ${this.id}. Ignoring.`);this.handledUpdates.push(e),this.handledUpdates.length>10&&this.handledUpdates.shift()}this.state.subscriptionInput.contains(null!==(n=t.data.subscription)&&void 0!==n?n:t.data.channel)&&super.handleEvent(e,t)}}subscriptionInput(e=!1){return e&&this.state.entity.subscriptionsCount>0?new It({}):this.state.subscriptionInput}cloneEmpty(){return new At({state:this.state})}dispose(){this.parentSetsCount>0?this.state.client.logger.debug(this.subscriptionType,(()=>({messageType:"text",message:`'${this.state.entity.subscriptionNames()}' subscription still in use. Ignore dispose request.`}))):(this.handledUpdates.splice(0,this.handledUpdates.length),super.dispose())}invalidate(e=!1){e&&this.state.entity.decreaseSubscriptionCount(this.state.id),this.handledUpdates.splice(0,this.handledUpdates.length),super.invalidate(e)}addParentSet(e){this.parents.includes(e)||(this.parents.push(e),this.state.client.logger.trace(this.subscriptionType,`Add parent subscription set for ${this.id}: ${e.id}. Parent subscription set count: ${this.parentSetsCount}`))}removeParentSet(e){const t=this.parents.indexOf(e);-1!==t&&(this.parents.splice(t,1),this.state.client.logger.trace(this.subscriptionType,`Remove parent subscription set from ${this.id}: ${e.id}. Parent subscription set count: ${this.parentSetsCount}`)),0===this.parentSetsCount&&this.handledUpdates.splice(0,this.handledUpdates.length)}addSubscription(e){this.state.client.logger.debug(this.subscriptionType,(()=>({messageType:"text",message:`Create set with subscription: ${e}`})));const t=new _t({client:this.state.client,subscriptions:[this,e],options:this.state.options});return this.state.isSubscribed||e.state.isSubscribed?(this.state.client.logger.trace(this.subscriptionType,"Subscribe resulting set because the receiver is already subscribed."),t.subscribe(),t):t}register(e){this.state.entity.increaseSubscriptionCount(this.state.id),this.state.client.logger.trace(this.subscriptionType,(()=>({messageType:"text",message:`Register subscription for real-time events: ${this}`}))),this.state.client.registerEventHandleCapable(this,e.cursor)}unregister(e){this.state.entity.decreaseSubscriptionCount(this.state.id),this.state.client.logger.trace(this.subscriptionType,(()=>({messageType:"text",message:`Unregister subscription from real-time events: ${this}`}))),this.handledUpdates.splice(0,this.handledUpdates.length),this.state.client.unregisterEventHandleCapable(this)}toString(){const e=this.state;return`${this.subscriptionType} { id: ${this.id}, stateId: ${e.id}, entity: ${e.entity.subscriptionNames(!1).pop()}, clonesCount: ${Object.keys(e.clones).length}, isSubscribed: ${e.isSubscribed}, parentSetsCount: ${this.parentSetsCount}, cursor: ${e.cursor?e.cursor.timetoken:"not set"}, referenceTimetoken: ${e.referenceTimetoken?e.referenceTimetoken:"not set"} }`}}class $t extends de{constructor(e){var t,s,n,i;super(),this.parameters=e,null!==(t=(n=this.parameters).channels)&&void 0!==t||(n.channels=[]),null!==(s=(i=this.parameters).channelGroups)&&void 0!==s||(i.channelGroups=[])}operation(){return L.PNGetStateOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroups:s}=this.parameters;if(!e)return"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e),{channels:s=[],channelGroups:n=[]}=this.parameters,i={channels:{}};return 1===s.length&&0===n.length?i.channels[s[0]]=t.payload:i.channels=t.payload,i}))}get path(){const{keySet:{subscribeKey:e},uuid:t,channels:s}=this.parameters;return`/v2/presence/sub-key/${e}/channel/${P(null!=s?s:[],",")}/uuid/${k(null!=t?t:"")}`}get queryParameters(){const{channelGroups:e}=this.parameters;return e&&0!==e.length?{"channel-group":e.join(",")}:{}}}class Ft extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNSetStateOperation}validate(){const{keySet:{subscribeKey:e},state:t,channels:s=[],channelGroups:n=[]}=this.parameters;return e?void 0===t?"Missing State":0===(null==s?void 0:s.length)&&0===(null==n?void 0:n.length)?"Please provide a list of channels and/or channel-groups":void 0:"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){return{state:this.deserializeResponse(e).payload}}))}get path(){const{keySet:{subscribeKey:e},uuid:t,channels:s}=this.parameters;return`/v2/presence/sub-key/${e}/channel/${P(null!=s?s:[],",")}/uuid/${k(t)}/data`}get queryParameters(){const{channelGroups:e,state:t}=this.parameters,s={state:JSON.stringify(t)};return e&&0!==e.length&&(s["channel-group"]=e.join(",")),s}}class xt extends de{constructor(e){super({cancellable:!0}),this.parameters=e}operation(){return L.PNHeartbeatOperation}validate(){const{keySet:{subscribeKey:e},channels:t=[],channelGroups:s=[]}=this.parameters;return e?0===t.length&&0===s.length?"Please provide a list of channels and/or channel-groups":void 0:"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channels:t}=this.parameters;return`/v2/presence/sub-key/${e}/channel/${P(null!=t?t:[],",")}/heartbeat`}get queryParameters(){const{channelGroups:e,state:t,heartbeat:s}=this.parameters,n={heartbeat:`${s}`};return e&&0!==e.length&&(n["channel-group"]=e.join(",")),void 0!==t&&(n.state=JSON.stringify(t)),n}}class qt extends de{constructor(e){super(),this.parameters=e,this.parameters.channelGroups&&(this.parameters.channelGroups=Array.from(new Set(this.parameters.channelGroups))),this.parameters.channels&&(this.parameters.channels=Array.from(new Set(this.parameters.channels)))}operation(){return L.PNUnsubscribeOperation}validate(){const{keySet:{subscribeKey:e},channels:t=[],channelGroups:s=[]}=this.parameters;return e?0===t.length&&0===s.length?"At least one `channel` or `channel group` should be provided.":void 0:"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){var e;const{keySet:{subscribeKey:t},channels:s}=this.parameters;return`/v2/presence/sub-key/${t}/channel/${P(null!==(e=null==s?void 0:s.sort())&&void 0!==e?e:[],",")}/leave`}get queryParameters(){const{channelGroups:e}=this.parameters;return e&&0!==e.length?{"channel-group":e.sort().join(",")}:{}}}class Gt extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNWhereNowOperation}validate(){if(!this.parameters.keySet.subscribeKey)return"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return t.payload?{channels:t.payload.channels}:{channels:[]}}))}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/presence/sub-key/${e}/uuid/${k(t)}`}}class Lt extends de{constructor(e){var t,s,n,i,r,a,o,c;super(),this.parameters=e,null!==(t=(r=this.parameters).queryParameters)&&void 0!==t||(r.queryParameters={}),null!==(s=(a=this.parameters).includeUUIDs)&&void 0!==s||(a.includeUUIDs=true),null!==(n=(o=this.parameters).includeState)&&void 0!==n||(o.includeState=false),null!==(i=(c=this.parameters).limit)&&void 0!==i||(c.limit=1e3)}operation(){const{channels:e=[],channelGroups:t=[]}=this.parameters;return 0===e.length&&0===t.length?L.PNGlobalHereNowOperation:L.PNHereNowOperation}validate(){if(!this.parameters.keySet.subscribeKey)return"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){var t,s;const n=this.deserializeResponse(e),i="occupancy"in n?1:n.payload.total_channels,r="occupancy"in n?n.occupancy:n.payload.total_occupancy,a={};let o={};const c=this.parameters.limit;let u=!1;if("occupancy"in n){const e=this.parameters.channels[0];o[e]={uuids:null!==(t=n.uuids)&&void 0!==t?t:[],occupancy:r}}else o=null!==(s=n.payload.channels)&&void 0!==s?s:{};return Object.keys(o).forEach((e=>{const t=o[e];a[e]={occupants:this.parameters.includeUUIDs?t.uuids.map((e=>"string"==typeof e?{uuid:e,state:null}:e)):[],name:e,occupancy:t.occupancy},u||t.occupancy!==c||(u=!0)})),{totalChannels:i,totalOccupancy:r,channels:a}}))}get path(){const{keySet:{subscribeKey:e},channels:t,channelGroups:s}=this.parameters;let n=`/v2/presence/sub-key/${e}`;return(t&&t.length>0||s&&s.length>0)&&(n+=`/channel/${P(null!=t?t:[],",")}`),n}get queryParameters(){const{channelGroups:e,includeUUIDs:t,includeState:s,limit:n,offset:i,queryParameters:r}=this.parameters;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},this.operation()===L.PNHereNowOperation?{limit:n}:{}),this.operation()===L.PNHereNowOperation&&null!=i&&i?{offset:i}:{}),t?{}:{disable_uuids:"1"}),null!=s&&s?{state:"1"}:{}),e&&e.length>0?{"channel-group":e.join(",")}:{}),r)}}class Kt extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNDeleteMessagesOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channel?void 0:"Missing channel":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v3/history/sub-key/${e}/channel/${k(t)}`}get queryParameters(){const{start:e,end:t}=this.parameters;return Object.assign(Object.assign({},e?{start:e}:{}),t?{end:t}:{})}}class Ht extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNMessageCounts}validate(){const{keySet:{subscribeKey:e},channels:t,timetoken:s,channelTimetokens:n}=this.parameters;return e?t?s&&n?"`timetoken` and `channelTimetokens` are incompatible together":s||n?n&&n.length>1&&n.length!==t.length?"Length of `channelTimetokens` and `channels` do not match":void 0:"`timetoken` or `channelTimetokens` need to be set":"Missing channels":"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){return{channels:this.deserializeResponse(e).channels}}))}get path(){return`/v3/history/sub-key/${this.parameters.keySet.subscribeKey}/message-counts/${P(this.parameters.channels)}`}get queryParameters(){let{channelTimetokens:e}=this.parameters;return this.parameters.timetoken&&(e=[this.parameters.timetoken]),Object.assign(Object.assign({},1===e.length?{timetoken:e[0]}:{}),e.length>1?{channelsTimetoken:e.join(",")}:{})}}class Vt extends de{constructor(e){var t,s,n;super(),this.parameters=e,e.count?e.count=Math.min(e.count,100):e.count=100,null!==(t=e.stringifiedTimeToken)&&void 0!==t||(e.stringifiedTimeToken=false),null!==(s=e.includeMeta)&&void 0!==s||(e.includeMeta=false),null!==(n=e.logVerbosity)&&void 0!==n||(e.logVerbosity=false)}operation(){return L.PNHistoryOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channel?void 0:"Missing channel":"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e),s=t[0],n=t[1],i=t[2];return Array.isArray(s)?{messages:yield Promise.all(s.map((e=>a(this,void 0,void 0,(function*(){const t=yield this.processPayload(e.message),s={entry:t.payload,timetoken:e.timetoken};return t.error&&(s.error=t.error),e.meta&&(s.meta=e.meta),s}))))),startTimeToken:n,endTimeToken:i}:{messages:[],startTimeToken:n,endTimeToken:i}}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/history/sub-key/${e}/channel/${k(t)}`}get queryParameters(){const{start:e,end:t,reverse:s,count:n,stringifiedTimeToken:i,includeMeta:r}=this.parameters;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:n,include_token:"true"},e?{start:e}:{}),t?{end:t}:{}),i?{string_message_token:"true"}:{}),null!=s?{reverse:s.toString()}:{}),r?{include_meta:"true"}:{})}processPayload(e){return a(this,void 0,void 0,(function*(){const{crypto:t,logVerbosity:s}=this.parameters;if(!t||"string"!=typeof e)return{payload:e};let n,i;try{const s=yield t.decrypt(e);n=s instanceof ArrayBuffer?JSON.parse(Vt.decoder.decode(s)):s}catch(t){s&&console.log("decryption error",t.message),n=e,i=`Error while decrypting message content: ${t.message}`}return{payload:n,error:i}}))}}var Wt;!function(e){e[e.Message=-1]="Message",e[e.Files=4]="Files"}(Wt||(Wt={}));class Bt extends de{constructor(e){var t,s,n,i,r;super(),this.parameters=e;const a=null!==(t=e.includeMessageActions)&&void 0!==t&&t,o=e.channels.length>1||a?25:100;e.count?e.count=Math.min(e.count,o):e.count=o,e.includeUuid?e.includeUUID=e.includeUuid:null!==(s=e.includeUUID)&&void 0!==s||(e.includeUUID=true),null!==(n=e.stringifiedTimeToken)&&void 0!==n||(e.stringifiedTimeToken=false),null!==(i=e.includeMessageType)&&void 0!==i||(e.includeMessageType=true),null!==(r=e.logVerbosity)&&void 0!==r||(e.logVerbosity=false)}operation(){return L.PNFetchMessagesOperation}validate(){const{keySet:{subscribeKey:e},channels:t,includeMessageActions:s}=this.parameters;return e?t?void 0!==s&&s&&t.length>1?"History can return actions data for a single channel only. Either pass a single channel or disable the includeMessageActions flag.":void 0:"Missing channels":"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){var t;const s=this.deserializeResponse(e),n=null!==(t=s.channels)&&void 0!==t?t:{},i={},r=Object.keys(n),o=yield Promise.all(r.map((e=>Promise.all(n[e].map((t=>a(this,void 0,void 0,(function*(){null===t.message_type&&(t.message_type=Wt.Message);const s=yield this.processPayload(e,t),n=Object.assign(Object.assign({channel:e,timetoken:t.timetoken,message:s.payload,messageType:t.message_type},t.custom_message_type?{customMessageType:t.custom_message_type}:{}),{uuid:t.uuid});if(t.actions){const e=n;e.actions=t.actions,e.data=t.actions}return t.meta&&(n.meta=t.meta),s.error&&(n.error=s.error),n}))))))));return r.forEach(((e,t)=>i[e]=o[t])),s.more?{channels:i,more:s.more}:{channels:i}}))}get path(){const{keySet:{subscribeKey:e},channels:t,includeMessageActions:s}=this.parameters;return`/v3/${s?"history-with-actions":"history"}/sub-key/${e}/channel/${P(t)}`}get queryParameters(){const{start:e,end:t,count:s,includeCustomMessageType:n,includeMessageType:i,includeMeta:r,includeUUID:a,stringifiedTimeToken:o}=this.parameters;return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({max:s},e?{start:e}:{}),t?{end:t}:{}),o?{string_message_token:"true"}:{}),void 0!==r&&r?{include_meta:"true"}:{}),a?{include_uuid:"true"}:{}),null!=n?{include_custom_message_type:n?"true":"false"}:{}),i?{include_message_type:"true"}:{})}processPayload(e,t){return a(this,void 0,void 0,(function*(){const{crypto:s,logVerbosity:n}=this.parameters;if(!s||"string"!=typeof t.message)return{payload:t.message};let i,r;try{const e=yield s.decrypt(t.message);i=e instanceof ArrayBuffer?JSON.parse(Bt.decoder.decode(e)):e}catch(e){n&&console.log("decryption error",e.message),i=t.message,r=`Error while decrypting message content: ${e.message}`}if(!r&&i&&t.message_type==Wt.Files&&"object"==typeof i&&this.isFileMessage(i)){const t=i;return{payload:{message:t.message,file:Object.assign(Object.assign({},t.file),{url:this.parameters.getFileUrl({channel:e,id:t.file.id,name:t.file.name})})},error:r}}return{payload:i,error:r}}))}isFileMessage(e){return void 0!==e.file}}class zt extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNGetMessageActionsOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channel?void 0:"Missing message channel":"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);let s=null,n=null;return t.data.length>0&&(s=t.data[0].actionTimetoken,n=t.data[t.data.length-1].actionTimetoken),{data:t.data,more:t.more,start:s,end:n}}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v1/message-actions/${e}/channel/${k(t)}`}get queryParameters(){const{limit:e,start:t,end:s}=this.parameters;return Object.assign(Object.assign(Object.assign({},t?{start:t}:{}),s?{end:s}:{}),e?{limit:e}:{})}}class Jt extends de{constructor(e){super({method:ce.POST}),this.parameters=e}operation(){return L.PNAddMessageActionOperation}validate(){const{keySet:{subscribeKey:e},action:t,channel:s,messageTimetoken:n}=this.parameters;return e?s?n?t?t.value?t.type?t.type.length>15?"Action.type value exceed maximum length of 15":void 0:"Missing Action.type":"Missing Action.value":"Missing Action":"Missing message timetoken":"Missing message channel":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((({data:e})=>({data:e})))}))}get path(){const{keySet:{subscribeKey:e},channel:t,messageTimetoken:s}=this.parameters;return`/v1/message-actions/${e}/channel/${k(t)}/message/${s}`}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){return JSON.stringify(this.parameters.action)}}class Yt extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNRemoveMessageActionOperation}validate(){const{keySet:{subscribeKey:e},channel:t,messageTimetoken:s,actionTimetoken:n}=this.parameters;return e?t?s?n?void 0:"Missing action timetoken":"Missing message timetoken":"Missing message action channel":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((({data:e})=>({data:e})))}))}get path(){const{keySet:{subscribeKey:e},channel:t,actionTimetoken:s,messageTimetoken:n}=this.parameters;return`/v1/message-actions/${e}/channel/${k(t)}/message/${n}/action/${s}`}}class Qt extends de{constructor(e){var t,s;super(),this.parameters=e,null!==(t=(s=this.parameters).storeInHistory)&&void 0!==t||(s.storeInHistory=true)}operation(){return L.PNPublishFileMessageOperation}validate(){const{channel:e,fileId:t,fileName:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}prepare(){const{crypto:e}=this.parameters;if(e)return D(e,this.fileMessage).then((e=>{this.encryptedPayload=e}))}parse(e){return a(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[2]}}))}get path(){const{channel:e,keySet:{publishKey:t,subscribeKey:s}}=this.parameters;return`/v1/files/publish-file/${t}/${s}/0/${k(e)}/0/${k(this.prepareMessagePayload(this.fileMessage))}`}get fileMessage(){const{message:e,fileId:t,fileName:s}=this.parameters;return Object.assign({file:{name:s,id:t}},e?{message:e}:{})}get queryParameters(){const{customMessageType:e,storeInHistory:t,ttl:s,meta:n}=this.parameters;return Object.assign(Object.assign(Object.assign({store:t?"1":"0"},e?{custom_message_type:e}:{}),s?{ttl:s}:{}),n&&"object"==typeof n?{meta:JSON.stringify(n)}:{})}prepareMessagePayload(e){if(!this.parameters.crypto)return JSON.stringify(e)||"";if(void 0===this.encryptedPayload)throw new Error("Encryption error: request payload not prepared");return this.encryptedPayload}}class Xt extends de{constructor(e){super({method:ce.LOCAL}),this.parameters=e}operation(){return L.PNGetFileUrlOperation}validate(){const{channel:e,id:t,name:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}parse(e){return a(this,void 0,void 0,(function*(){return e.url}))}get path(){const{channel:e,id:t,name:s,keySet:{subscribeKey:n}}=this.parameters;return`/v1/files/${n}/channels/${k(e)}/files/${k(t)}/${k(s)}`}}class Zt extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNDeleteFileOperation}validate(){const{channel:e,id:t,name:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}get path(){const{keySet:{subscribeKey:e},id:t,channel:s,name:n}=this.parameters;return`/v1/files/${e}/channels/${k(s)}/files/${k(t)}/${k(n)}`}}class es extends de{constructor(e){var t,s;super(),this.parameters=e,null!==(t=(s=this.parameters).limit)&&void 0!==t||(s.limit=100)}operation(){return L.PNListFilesOperation}validate(){if(!this.parameters.channel)return"channel can't be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v1/files/${e}/channels/${k(t)}/files`}get queryParameters(){const{limit:e,next:t}=this.parameters;return Object.assign({limit:e},t?{next:t}:{})}}class ts extends de{constructor(e){super({method:ce.POST}),this.parameters=e}operation(){return L.PNGenerateUploadUrlOperation}validate(){return this.parameters.channel?this.parameters.name?void 0:"'name' can't be empty":"channel can't be empty"}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return{id:t.data.id,name:t.data.name,url:t.file_upload_request.url,formFields:t.file_upload_request.form_fields}}))}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v1/files/${e}/channels/${k(t)}/generate-upload-url`}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){return JSON.stringify({name:this.parameters.name})}}class ss extends de{constructor(e){super({method:ce.POST}),this.parameters=e;const t=e.file.mimeType;t&&(e.formFields=e.formFields.map((e=>"Content-Type"===e.name?{name:e.name,value:t}:e)))}operation(){return L.PNPublishFileOperation}validate(){const{fileId:e,fileName:t,file:s,uploadUrl:n}=this.parameters;return e?t?s?n?void 0:"Validation failed: file upload 'url' can't be empty":"Validation failed: 'file' can't be empty":"Validation failed: file 'name' can't be empty":"Validation failed: file 'id' can't be empty"}parse(e){return a(this,void 0,void 0,(function*(){return{status:e.status,message:e.body?ss.decoder.decode(e.body):"OK"}}))}request(){return Object.assign(Object.assign({},super.request()),{origin:new URL(this.parameters.uploadUrl).origin,timeout:300})}get path(){const{pathname:e,search:t}=new URL(this.parameters.uploadUrl);return`${e}${t}`}get body(){return this.parameters.file}get formData(){return this.parameters.formFields}}class ns{constructor(e){var t;if(this.parameters=e,this.file=null===(t=this.parameters.PubNubFile)||void 0===t?void 0:t.create(e.file),!this.file)throw new Error("File upload error: unable to create File object.")}process(){return a(this,void 0,void 0,(function*(){let e,t;return this.generateFileUploadUrl().then((s=>(e=s.name,t=s.id,this.uploadFile(s)))).then((e=>{if(204!==e.status)throw new c("Upload to bucket was unsuccessful",{error:!0,statusCode:e.status,category:o.PNUnknownCategory,operation:L.PNPublishFileOperation,errorData:{message:e.message}})})).then((()=>this.publishFileMessage(t,e))).catch((e=>{if(e instanceof c)throw e;const t=e instanceof q?e:q.create(e);throw new c("File upload error.",t.toStatus(L.PNPublishFileOperation))}))}))}generateFileUploadUrl(){return a(this,void 0,void 0,(function*(){const e=new ts(Object.assign(Object.assign({},this.parameters),{name:this.file.name,keySet:this.parameters.keySet}));return this.parameters.sendRequest(e)}))}uploadFile(e){return a(this,void 0,void 0,(function*(){const{cipherKey:t,PubNubFile:s,crypto:n,cryptography:i}=this.parameters,{id:r,name:a,url:o,formFields:c}=e;return this.parameters.PubNubFile.supportsEncryptFile&&(!t&&n?this.file=yield n.encryptFile(this.file,s):t&&i&&(this.file=yield i.encryptFile(t,this.file,s))),this.parameters.sendRequest(new ss({fileId:r,fileName:a,file:this.file,uploadUrl:o,formFields:c}))}))}publishFileMessage(e,t){return a(this,void 0,void 0,(function*(){var s,n,i,r;let a,u={timetoken:"0"},l=this.parameters.fileUploadPublishRetryLimit,h=!1;do{try{u=yield this.parameters.publishFile(Object.assign(Object.assign({},this.parameters),{fileId:e,fileName:t})),h=!0}catch(e){e instanceof c&&(a=e),l-=1}}while(!h&&l>0);if(h)return{status:200,timetoken:u.timetoken,id:e,name:t};throw new c("Publish failed. You may want to execute that operation manually using pubnub.publishFile",{error:!0,category:null!==(n=null===(s=a.status)||void 0===s?void 0:s.category)&&void 0!==n?n:o.PNUnknownCategory,statusCode:null!==(r=null===(i=a.status)||void 0===i?void 0:i.statusCode)&&void 0!==r?r:0,channel:this.parameters.channel,id:e,name:t})}))}}class is{constructor(e,t){this.subscriptionStateIds=[],this.client=t,this._nameOrId=e}get entityType(){return"Channel"}get subscriptionType(){return Tt.Channel}subscriptionNames(e){return[this._nameOrId,...e&&!this._nameOrId.endsWith("-pnpres")?[`${this._nameOrId}-pnpres`]:[]]}subscription(e){return new At({client:this.client,entity:this,options:e})}get subscriptionsCount(){return this.subscriptionStateIds.length}increaseSubscriptionCount(e){this.subscriptionStateIds.includes(e)||this.subscriptionStateIds.push(e)}decreaseSubscriptionCount(e){{const t=this.subscriptionStateIds.indexOf(e);t>=0&&this.subscriptionStateIds.splice(t,1)}}toString(){return`${this.entityType} { nameOrId: ${this._nameOrId}, subscriptionsCount: ${this.subscriptionsCount} }`}}const rs=["default","__default__"],as=e=>{if(!e)return;const t=e.trim();return 0===t.length||rs.includes(t.toLowerCase())?void 0:t};class os extends is{constructor(e,t,s){super(e,t),this._projection=as(s)}get id(){return this._nameOrId}get projection(){return this._projection}get subscriptionChannel(){return this._projection?`__${this._projection}__${this._nameOrId}`:this._nameOrId}subscriptionNames(e){return[this.subscriptionChannel]}subscription(e){{const t=this.entityForProjection(as(null==e?void 0:e.projection));return t!==this?t.subscription(e):super.subscription(e)}}entityForProjection(e){var t,s,n,i;if(e===this._projection)return this;const r=null!==(t=this.variants)&&void 0!==t?t:this.variants={};null!==(n=r[i=null!==(s=this._projection)&&void 0!==s?s:""])&&void 0!==n||(r[i]=this);const a=null!=e?e:"";let o=r[a];return o||(o=r[a]=this.withProjection(e),o.variants=r),o}toString(){var e;return`${this.entityType} { id: ${this._nameOrId}, projection: ${null!==(e=this._projection)&&void 0!==e?e:"none"}, channel: ${this.subscriptionChannel}, subscriptionsCount: ${this.subscriptionsCount} }`}}class cs extends os{get entityType(){return"DataSyncRelationship"}withProjection(e){return new cs(this._nameOrId,this.client,e)}}class us extends os{get entityType(){return"DataSyncMembership"}withProjection(e){return new us(this._nameOrId,this.client,e)}}class ls extends os{get entityType(){return"DataSyncChannel"}withProjection(e){return new ls(this._nameOrId,this.client,e)}}class hs extends os{get entityType(){return"DataSyncEntity"}withProjection(e){return new hs(this._nameOrId,this.client,e)}}class ds extends is{get entityType(){return"ChannelMetadata"}get id(){return this._nameOrId}subscriptionNames(e){return[this.id]}}class ps extends os{get entityType(){return"DataSyncUser"}withProjection(e){return new ps(this._nameOrId,this.client,e)}}class gs extends is{get entityType(){return"ChannelGroups"}get name(){return this._nameOrId}get subscriptionType(){return Tt.ChannelGroup}}class bs extends is{get entityType(){return"UserMetadata"}get id(){return this._nameOrId}subscriptionNames(e){return[this.id]}}class ys extends is{get entityType(){return"Channel"}get name(){return this._nameOrId}}class ms extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNRemoveChannelsFromGroupOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroup:s}=this.parameters;return e?s?t?void 0:"Missing channels":"Missing Channel Group":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${k(t)}`}get queryParameters(){return{remove:this.parameters.channels.join(",")}}}class fs extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNAddChannelsToGroupOperation}validate(){const{keySet:{subscribeKey:e},channels:t,channelGroup:s}=this.parameters;return e?s?t?void 0:"Missing channels":"Missing Channel Group":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${k(t)}`}get queryParameters(){return{add:this.parameters.channels.join(",")}}}class vs extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNChannelsForGroupOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channelGroup?void 0:"Missing Channel Group":"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){return{channels:this.deserializeResponse(e).payload.channels}}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${k(t)}`}}class Os extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNRemoveGroupOperation}validate(){return this.parameters.keySet.subscribeKey?this.parameters.channelGroup?void 0:"Missing Channel Group":"Missing Subscribe Key"}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}get path(){const{keySet:{subscribeKey:e},channelGroup:t}=this.parameters;return`/v1/channel-registration/sub-key/${e}/channel-group/${k(t)}/remove`}}class Ss extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNChannelGroupsOperation}validate(){if(!this.parameters.keySet.subscribeKey)return"Missing Subscribe Key"}parse(e){return a(this,void 0,void 0,(function*(){return{groups:this.deserializeResponse(e).payload.groups}}))}get path(){return`/v1/channel-registration/sub-key/${this.parameters.keySet.subscribeKey}/channel-group`}}class js{constructor(e,t,s){this.sendRequest=s,this.logger=e,this.keySet=t}listChannels(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"List channel group channels with parameters:"})));const s=new vs(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`List channel group channels success. Received ${e.channels.length} channels.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}listGroups(e){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub","List all channel groups.");const t=new Ss({keySet:this.keySet}),s=e=>{e&&this.logger.debug("PubNub",`List all channel groups success. Received ${e.groups.length} groups.`)};return e?this.sendRequest(t,((t,n)=>{s(n),e(t,n)})):this.sendRequest(t).then((e=>(s(e),e)))}))}addChannels(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add channels to the channel group with parameters:"})));const s=new fs(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Add channels to the channel group success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}removeChannels(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove channels from the channel group with parameters:"})));const s=new ms(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Remove channels from the channel group success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}deleteGroup(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove a channel group with parameters:"})));const s=new Os(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub",`Remove a channel group success. Removed '${e.channelGroup}' channel group.'`)};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}}class ws extends de{constructor(e){var t,s;super(),this.parameters=e,"apns2"===this.parameters.pushGateway&&(null!==(t=(s=this.parameters).environment)&&void 0!==t||(s.environment="development")),this.parameters.count&&this.parameters.count>1e3&&(this.parameters.count=1e3)}operation(){throw Error("Should be implemented in subclass.")}validate(){const{keySet:{subscribeKey:e},action:t,device:s,pushGateway:n}=this.parameters;return e?s?"add"!==t&&"remove"!==t||"channels"in this.parameters&&0!==this.parameters.channels.length?n?"apns2"!==this.parameters.pushGateway||this.parameters.topic?void 0:"Missing APNS2 topic":"Missing GW Type (pushGateway: fcm or apns2)":"Missing Channels":"Missing Device ID (device)":"Missing Subscribe Key"}get path(){const{keySet:{subscribeKey:e},action:t,device:s,pushGateway:n}=this.parameters;let i="apns2"===n?`/v2/push/sub-key/${e}/devices-apns2/${s}`:`/v1/push/sub-key/${e}/devices/${s}`;return"remove-device"===t&&(i=`${i}/remove`),i}get queryParameters(){const{start:e,count:t}=this.parameters;let s=Object.assign(Object.assign({type:this.parameters.pushGateway},e?{start:e}:{}),t&&t>0?{count:t}:{});if("channels"in this.parameters&&(s[this.parameters.action]=this.parameters.channels.join(",")),"apns2"===this.parameters.pushGateway){const{environment:e,topic:t}=this.parameters;s=Object.assign(Object.assign({},s),{environment:e,topic:t})}return s}}class ks extends ws{constructor(e){super(Object.assign(Object.assign({},e),{action:"remove"}))}operation(){return L.PNRemovePushNotificationEnabledChannelsOperation}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}}class Ps extends ws{constructor(e){super(Object.assign(Object.assign({},e),{action:"list"}))}operation(){return L.PNPushNotificationEnabledChannelsOperation}parse(e){return a(this,void 0,void 0,(function*(){return{channels:this.deserializeResponse(e)}}))}}class Cs extends ws{constructor(e){super(Object.assign(Object.assign({},e),{action:"add"}))}operation(){return L.PNAddPushNotificationEnabledChannelsOperation}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}}class Ns extends ws{constructor(e){super(Object.assign(Object.assign({},e),{action:"remove-device"}))}operation(){return L.PNRemoveAllPushNotificationsOperation}parse(e){const t=Object.create(null,{parse:{get:()=>super.parse}});return a(this,void 0,void 0,(function*(){return t.parse.call(this,e).then((e=>({})))}))}}class Es{constructor(e,t,s){this.sendRequest=s,this.logger=e,this.keySet=t}listChannels(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"List push-enabled channels with parameters:"})));const s=new Ps(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`List push-enabled channels success. Received ${e.channels.length} channels.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}addChannels(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add push-enabled channels with parameters:"})));const s=new Cs(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Add push-enabled channels success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}removeChannels(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove push-enabled channels with parameters:"})));const s=new ks(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Remove push-enabled channels success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}deleteDevice(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove push notifications for device with parameters:"})));const s=new Ns(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=()=>{this.logger.debug("PubNub","Remove push notifications for device success.")};return t?this.sendRequest(s,(e=>{e.error||n(),t(e)})):this.sendRequest(s).then((e=>(n(),e)))}))}}class Ts extends de{constructor(e){var t,s,n,i,r,a;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(r=e.include).customFields)&&void 0!==s||(r.customFields=false),null!==(n=(a=e.include).totalCount)&&void 0!==n||(a.totalCount=false),null!==(i=e.limit)&&void 0!==i||(e.limit=100)}operation(){return L.PNGetAllChannelMetadataOperation}get path(){return`/v2/objects/${this.parameters.keySet.subscribeKey}/channels`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";return r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e)),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({include:["status","type",...e.customFields?["custom"]:[]].join(","),count:`${e.totalCount}`},s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class Is extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNRemoveChannelMetadataOperation}validate(){if(!this.parameters.channel)return"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${k(t)}`}}class Rs extends de{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).channelFields)&&void 0!==a||(b.channelFields=false),null!==(o=(y=e.include).customChannelFields)&&void 0!==o||(y.customChannelFields=false),null!==(c=(m=e.include).channelStatusField)&&void 0!==c||(m.channelStatusField=false),null!==(u=(f=e.include).channelTypeField)&&void 0!==u||(f.channelTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return L.PNGetMembershipsOperation}validate(){if(!this.parameters.uuid)return"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${k(t)}/channels`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=[];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.channelFields&&a.push("channel"),e.channelStatusField&&a.push("channel.status"),e.channelTypeField&&a.push("channel.type"),e.customChannelFields&&a.push("channel.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class Ms extends de{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super({method:ce.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).channelFields)&&void 0!==a||(b.channelFields=false),null!==(o=(y=e.include).customChannelFields)&&void 0!==o||(y.customChannelFields=false),null!==(c=(m=e.include).channelStatusField)&&void 0!==c||(m.channelStatusField=false),null!==(u=(f=e.include).channelTypeField)&&void 0!==u||(f.channelTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return L.PNSetMembershipsOperation}validate(){const{uuid:e,channels:t}=this.parameters;return e?t&&0!==t.length?void 0:"Channels cannot be empty":"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${k(t)}/channels`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=["channel.status","channel.type","status"];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.channelFields&&a.push("channel"),e.channelStatusField&&a.push("channel.status"),e.channelTypeField&&a.push("channel.type"),e.customChannelFields&&a.push("channel.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){const{channels:e,type:t}=this.parameters;return JSON.stringify({[`${t}`]:e.map((e=>"string"==typeof e?{channel:{id:e}}:{channel:{id:e.id},status:e.status,type:e.type,custom:e.custom}))})}}class Ds extends de{constructor(e){var t,s,n,i;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(i=e.include).customFields)&&void 0!==s||(i.customFields=false),null!==(n=e.limit)&&void 0!==n||(e.limit=100)}operation(){return L.PNGetAllUUIDMetadataOperation}get path(){return`/v2/objects/${this.parameters.keySet.subscribeKey}/uuids`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";return r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e)),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({include:["status","type",...e.customFields?["custom"]:[]].join(",")},void 0!==e.totalCount?{count:`${e.totalCount}`}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class _s extends de{constructor(e){var t,s,n;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true)}operation(){return L.PNGetChannelMetadataOperation}validate(){if(!this.parameters.channel)return"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${k(t)}`}get queryParameters(){return{include:["status","type",...this.parameters.include.customFields?["custom"]:[]].join(",")}}}class Us extends de{constructor(e){var t,s,n;super({method:ce.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true)}operation(){return L.PNSetChannelMetadataOperation}validate(){return this.parameters.channel?this.parameters.data?void 0:"Data cannot be empty":"Channel cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json"})}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${k(t)}`}get queryParameters(){return{include:["status","type",...this.parameters.include.customFields?["custom"]:[]].join(",")}}get body(){return JSON.stringify(this.parameters.data)}}class As extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e,this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return L.PNRemoveUUIDMetadataOperation}validate(){if(!this.parameters.uuid)return"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${k(t)}`}}class $s extends de{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).UUIDFields)&&void 0!==a||(b.UUIDFields=false),null!==(o=(y=e.include).customUUIDFields)&&void 0!==o||(y.customUUIDFields=false),null!==(c=(m=e.include).UUIDStatusField)&&void 0!==c||(m.UUIDStatusField=false),null!==(u=(f=e.include).UUIDTypeField)&&void 0!==u||(f.UUIDTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100)}operation(){return L.PNSetMembersOperation}validate(){if(!this.parameters.channel)return"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${k(t)}/uuids`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=[];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.UUIDFields&&a.push("uuid"),e.UUIDStatusField&&a.push("uuid.status"),e.UUIDTypeField&&a.push("uuid.type"),e.customUUIDFields&&a.push("uuid.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}}class Fs extends de{constructor(e){var t,s,n,i,r,a,o,c,u,l,h,d,p,g,b,y,m,f;super({method:ce.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(h=e.include).customFields)&&void 0!==s||(h.customFields=false),null!==(n=(d=e.include).totalCount)&&void 0!==n||(d.totalCount=false),null!==(i=(p=e.include).statusField)&&void 0!==i||(p.statusField=false),null!==(r=(g=e.include).typeField)&&void 0!==r||(g.typeField=false),null!==(a=(b=e.include).UUIDFields)&&void 0!==a||(b.UUIDFields=false),null!==(o=(y=e.include).customUUIDFields)&&void 0!==o||(y.customUUIDFields=false),null!==(c=(m=e.include).UUIDStatusField)&&void 0!==c||(m.UUIDStatusField=false),null!==(u=(f=e.include).UUIDTypeField)&&void 0!==u||(f.UUIDTypeField=false),null!==(l=e.limit)&&void 0!==l||(e.limit=100)}operation(){return L.PNSetMembersOperation}validate(){const{channel:e,uuids:t}=this.parameters;return e?t&&0!==t.length?void 0:"UUIDs cannot be empty":"Channel cannot be empty"}get path(){const{keySet:{subscribeKey:e},channel:t}=this.parameters;return`/v2/objects/${e}/channels/${k(t)}/uuids`}get queryParameters(){const{include:e,page:t,filter:s,sort:n,limit:i}=this.parameters;let r="";r="string"==typeof n?n:Object.entries(null!=n?n:{}).map((([e,t])=>null!==t?`${e}:${t}`:e));const a=["uuid.status","uuid.type","type"];return e.statusField&&a.push("status"),e.typeField&&a.push("type"),e.customFields&&a.push("custom"),e.UUIDFields&&a.push("uuid"),e.UUIDStatusField&&a.push("uuid.status"),e.UUIDTypeField&&a.push("uuid.type"),e.customUUIDFields&&a.push("uuid.custom"),Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({count:`${e.totalCount}`},a.length>0?{include:a.join(",")}:{}),s?{filter:s}:{}),(null==t?void 0:t.next)?{start:t.next}:{}),(null==t?void 0:t.prev)?{end:t.prev}:{}),i?{limit:i}:{}),r.length?{sort:r}:{})}get headers(){var e;return Object.assign(Object.assign({},null!==(e=super.headers)&&void 0!==e?e:{}),{"Content-Type":"application/json"})}get body(){const{uuids:e,type:t}=this.parameters;return JSON.stringify({[`${t}`]:e.map((e=>"string"==typeof e?{uuid:{id:e}}:{uuid:{id:e.id},status:e.status,type:e.type,custom:e.custom}))})}}class xs extends de{constructor(e){var t,s,n;super(),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return L.PNGetUUIDMetadataOperation}validate(){if(!this.parameters.uuid)return"'uuid' cannot be empty"}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${k(t)}`}get queryParameters(){const{include:e}=this.parameters;return{include:["status","type",...e.customFields?["custom"]:[]].join(",")}}}class qs extends de{constructor(e){var t,s,n;super({method:ce.PATCH}),this.parameters=e,null!==(t=e.include)&&void 0!==t||(e.include={}),null!==(s=(n=e.include).customFields)&&void 0!==s||(n.customFields=true),this.parameters.userId&&(this.parameters.uuid=this.parameters.userId)}operation(){return L.PNSetUUIDMetadataOperation}validate(){return this.parameters.uuid?this.parameters.data?void 0:"Data cannot be empty":"'uuid' cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json"})}get path(){const{keySet:{subscribeKey:e},uuid:t}=this.parameters;return`/v2/objects/${e}/uuids/${k(t)}`}get queryParameters(){return{include:["status","type",...this.parameters.include.customFields?["custom"]:[]].join(",")}}get body(){return JSON.stringify(this.parameters.data)}}class Gs{constructor(e,t){this.keySet=e.keySet,this.configuration=e,this.sendRequest=t}get logger(){return this.configuration.logger()}getAllUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Get all UUID metadata objects with parameters:"}))),this._getAllUUIDMetadata(e,t)}))}_getAllUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0);const n=new Ds(Object.assign(Object.assign({},s),{keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Get all UUID metadata success. Received ${e.totalCount} UUID metadata objects.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}getUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.configuration.userId},details:`Get ${e&&"function"!=typeof e?"":" current"} UUID metadata object with parameters:`}))),this._getUUIDMetadata(e,t)}))}_getUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){var s;const n=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),n.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),n.uuid=n.userId),null!==(s=n.uuid)&&void 0!==s||(n.uuid=this.configuration.userId);const i=new xs(Object.assign(Object.assign({},n),{keySet:this.keySet})),r=e=>{e&&this.logger.debug("PubNub",`Get UUID metadata object success. Received '${n.uuid}' UUID metadata object.`)};return t?this.sendRequest(i,((e,s)=>{r(s),t(e,s)})):this.sendRequest(i).then((e=>(r(e),e)))}))}setUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set UUID metadata object with parameters:"}))),this._setUUIDMetadata(e,t)}))}_setUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){var s;e.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),e.uuid=e.userId),null!==(s=e.uuid)&&void 0!==s||(e.uuid=this.configuration.userId);const n=new qs(Object.assign(Object.assign({},e),{keySet:this.keySet})),i=t=>{t&&this.logger.debug("PubNub",`Set UUID metadata object success. Updated '${e.uuid}' UUID metadata object.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}removeUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.configuration.userId},details:`Remove${e&&"function"!=typeof e?"":" current"} UUID metadata object with parameters:`}))),this._removeUUIDMetadata(e,t)}))}_removeUUIDMetadata(e,t){return a(this,void 0,void 0,(function*(){var s;const n=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),n.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),n.uuid=n.userId),null!==(s=n.uuid)&&void 0!==s||(n.uuid=this.configuration.userId);const i=new As(Object.assign(Object.assign({},n),{keySet:this.keySet})),r=e=>{e&&this.logger.debug("PubNub",`Remove UUID metadata object success. Removed '${n.uuid}' UUID metadata object.`)};return t?this.sendRequest(i,((e,s)=>{r(s),t(e,s)})):this.sendRequest(i).then((e=>(r(e),e)))}))}getAllChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Get all Channel metadata objects with parameters:"}))),this._getAllChannelMetadata(e,t)}))}_getAllChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0);const n=new Ts(Object.assign(Object.assign({},s),{keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Get all Channel metadata objects success. Received ${e.totalCount} Channel metadata objects.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}getChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Channel metadata object with parameters:"}))),this._getChannelMetadata(e,t)}))}_getChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){const s=new _s(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Get Channel metadata object success. Received '${e.channel}' Channel metadata object.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}setChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Channel metadata object with parameters:"}))),this._setChannelMetadata(e,t)}))}_setChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){const s=new Us(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Set Channel metadata object success. Updated '${e.channel}' Channel metadata object.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}removeChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Channel metadata object with parameters:"}))),this._removeChannelMetadata(e,t)}))}_removeChannelMetadata(e,t){return a(this,void 0,void 0,(function*(){const s=new Is(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Remove Channel metadata object success. Removed '${e.channel}' Channel metadata object.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}getChannelMembers(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get channel members with parameters:"})));const s=new $s(Object.assign(Object.assign({},e),{keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Get channel members success. Received ${e.totalCount} channel members.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}setChannelMembers(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set channel members with parameters:"})));const s=new Fs(Object.assign(Object.assign({},e),{type:"set",keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Set channel members success. There are ${e.totalCount} channel members now.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}removeChannelMembers(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove channel members with parameters:"})));const s=new Fs(Object.assign(Object.assign({},e),{type:"delete",keySet:this.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Remove channel members success. There are ${e.totalCount} channel members now.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}))}getMemberships(e,t){return a(this,void 0,void 0,(function*(){var s;const n=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),n.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),n.uuid=n.userId),null!==(s=n.uuid)&&void 0!==s||(n.uuid=this.configuration.userId),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},n),details:"Get memberships with parameters:"})));const i=new Rs(Object.assign(Object.assign({},n),{keySet:this.keySet})),r=e=>{e&&this.logger.debug("PubNub",`Get memberships success. Received ${e.totalCount} memberships.`)};return t?this.sendRequest(i,((e,s)=>{r(s),t(e,s)})):this.sendRequest(i).then((e=>(r(e),e)))}))}setMemberships(e,t){return a(this,void 0,void 0,(function*(){var s;e.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),e.uuid=e.userId),null!==(s=e.uuid)&&void 0!==s||(e.uuid=this.configuration.userId),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set memberships with parameters:"})));const n=new Ms(Object.assign(Object.assign({},e),{type:"set",keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Set memberships success. There are ${e.totalCount} memberships now.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}removeMemberships(e,t){return a(this,void 0,void 0,(function*(){var s;e.userId&&(this.logger.warn("PubNub","'userId' parameter is deprecated. Use 'uuid' instead."),e.uuid=e.userId),null!==(s=e.uuid)&&void 0!==s||(e.uuid=this.configuration.userId),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove memberships with parameters:"})));const n=new Ms(Object.assign(Object.assign({},e),{type:"delete",keySet:this.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Remove memberships success. There are ${e.totalCount} memberships now.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}))}fetchMemberships(e,t){return a(this,void 0,void 0,(function*(){var s,n;if(this.logger.warn("PubNub","'fetchMemberships' is deprecated. Use 'pubnub.objects.getChannelMembers' or 'pubnub.objects.getMemberships' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch memberships with parameters:"}))),"spaceId"in e){const n=e,i={channel:null!==(s=n.spaceId)&&void 0!==s?s:n.channel,filter:n.filter,limit:n.limit,page:n.page,include:Object.assign({},n.include),sort:n.sort?Object.fromEntries(Object.entries(n.sort).map((([e,t])=>[e.replace("user","uuid"),t]))):void 0},r=e=>({status:e.status,data:e.data.map((e=>({user:e.uuid,custom:e.custom,updated:e.updated,eTag:e.eTag}))),totalCount:e.totalCount,next:e.next,prev:e.prev});return t?this.getChannelMembers(i,((e,s)=>{t(e,s?r(s):s)})):this.getChannelMembers(i).then(r)}const i=e,r={uuid:null!==(n=i.userId)&&void 0!==n?n:i.uuid,filter:i.filter,limit:i.limit,page:i.page,include:Object.assign({},i.include),sort:i.sort?Object.fromEntries(Object.entries(i.sort).map((([e,t])=>[e.replace("space","channel"),t]))):void 0},a=e=>({status:e.status,data:e.data.map((e=>({space:e.channel,custom:e.custom,updated:e.updated,eTag:e.eTag}))),totalCount:e.totalCount,next:e.next,prev:e.prev});return t?this.getMemberships(r,((e,s)=>{t(e,s?a(s):s)})):this.getMemberships(r).then(a)}))}addMemberships(e,t){return a(this,void 0,void 0,(function*(){var s,n,i,r,a,o;if(this.logger.warn("PubNub","'addMemberships' is deprecated. Use 'pubnub.objects.setChannelMembers' or 'pubnub.objects.setMemberships' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add memberships with parameters:"}))),"spaceId"in e){const r=e,a={channel:null!==(s=r.spaceId)&&void 0!==s?s:r.channel,uuids:null!==(i=null===(n=r.users)||void 0===n?void 0:n.map((e=>"string"==typeof e?e:{id:e.userId,custom:e.custom})))&&void 0!==i?i:r.uuids,limit:0};return t?this.setChannelMembers(a,t):this.setChannelMembers(a)}const c=e,u={uuid:null!==(r=c.userId)&&void 0!==r?r:c.uuid,channels:null!==(o=null===(a=c.spaces)||void 0===a?void 0:a.map((e=>"string"==typeof e?e:{id:e.spaceId,custom:e.custom})))&&void 0!==o?o:c.channels,limit:0};return t?this.setMemberships(u,t):this.setMemberships(u)}))}}class Ls extends de{constructor(e){super({method:ce.POST}),this.parameters=e}operation(){return L.PNCreateDataSyncUserOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"User data cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.user+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/users`}get body(){const{id:e,class:t,classLevel:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),void 0!==t?{entityClass:t}:{}),{entityClassVersion:n.classVersion}),void 0!==s?{entityClassLevel:s}:{}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}function Ks(e){return"string"==typeof e?e:Object.entries(null!=e?e:{}).map((([e,t])=>null!==t?`${e}:${t}`:e))}function Hs(e){const{add:t,replace:s,remove:n,move:i,copy:r,test:a}=e,o=[];if(t)for(const[e,s]of Object.entries(t))o.push({op:"add",path:e,value:s});if(s)for(const[e,t]of Object.entries(s))o.push({op:"replace",path:e,value:t});if(n)for(const e of n)o.push({op:"remove",path:e});if(i)for(const{from:e,path:t}of i)o.push({op:"move",from:e,path:t});if(r)for(const{from:e,path:t}of r)o.push({op:"copy",from:e,path:t});if(a)for(const[e,t]of Object.entries(a))o.push({op:"test",path:e,value:t});return o}class Vs extends de{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return L.PNGetDataSyncUsersOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/users`}get queryParameters(){const{class:e,classVersion:t,classLevel:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Ks(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},e?{entity_class:e}:{}),void 0!==t?{entity_class_version:`${t}`}:{}),s?{entity_class_level:s}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class Ws extends de{constructor(e){super({method:ce.PUT}),this.parameters=e}operation(){return L.PNSetDataSyncUserOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"User data cannot be empty":"User id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.user+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${k(t)}`}get body(){const{data:e}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityClassVersion:e.classVersion},void 0!==e.status?{status:e.status}:{}),void 0!==e.payload?{payload:e.payload}:{})})}}class Bs extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNRemoveDataSyncUserOperation}validate(){if(!this.parameters.id)return"User id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return a(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${k(t)}`}}class zs extends de{constructor(e){super({method:ce.PATCH}),this.parameters=e}operation(){return L.PNUpdateDataSyncUserOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"User id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${k(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Hs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class Js extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNGetDataSyncUserOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"User id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/users/${k(t)}`}}class Ys extends de{constructor(e){super({method:ce.POST}),this.parameters=e}operation(){return L.PNCreateDataSyncChannelOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Channel data cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.channel+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/channels`}get body(){const{id:e,class:t,classLevel:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),void 0!==t?{entityClass:t}:{}),{entityClassVersion:n.classVersion}),void 0!==s?{entityClassLevel:s}:{}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}class Qs extends de{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return L.PNGetDataSyncChannelsOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/channels`}get queryParameters(){const{class:e,classVersion:t,classLevel:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Ks(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},e?{entity_class:e}:{}),void 0!==t?{entity_class_version:`${t}`}:{}),s?{entity_class_level:s}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class Xs extends de{constructor(e){super({method:ce.PUT}),this.parameters=e}operation(){return L.PNSetDataSyncChannelOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Channel data cannot be empty":"Channel id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.channel+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${k(t)}`}get body(){const{data:e}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityClassVersion:e.classVersion},void 0!==e.status?{status:e.status}:{}),void 0!==e.payload?{payload:e.payload}:{})})}}class Zs extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNRemoveDataSyncChannelOperation}validate(){if(!this.parameters.id)return"Channel id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return a(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${k(t)}`}}class en extends de{constructor(e){super({method:ce.PATCH}),this.parameters=e}operation(){return L.PNUpdateDataSyncChannelOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Channel id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${k(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Hs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class tn extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNGetDataSyncChannelOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Channel id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/channels/${k(t)}`}}class sn extends de{constructor(e){super({method:ce.POST}),this.parameters=e}operation(){return L.PNCreateDataSyncMembershipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.userId?this.parameters.channelId?this.parameters.data?this.parameters.data.classVersion?void 0:"Membership class version cannot be empty":"Membership data cannot be empty":"Channel id cannot be empty":"User id cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.membership+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships`}get body(){const{id:e,userId:t,channelId:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),{userId:t,channelId:s,relationshipClassVersion:n.classVersion}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}class nn extends de{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return L.PNGetDataSyncMembershipsOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/memberships`}get queryParameters(){const{userId:e,channelId:t,classVersion:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Ks(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},e?{user_id:e}:{}),t?{channel_id:t}:{}),void 0!==s?{relationship_class_version:`${s}`}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class rn extends de{constructor(e){super({method:ce.PUT}),this.parameters=e}operation(){return L.PNSetDataSyncMembershipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.userId?this.parameters.channelId?this.parameters.data?this.parameters.data.classVersion?void 0:"Membership class version cannot be empty":"Membership data cannot be empty":"Channel id cannot be empty":"User id cannot be empty":"Membership id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.membership+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${k(t)}`}get body(){const{userId:e,channelId:t,data:s}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({userId:e,channelId:t,relationshipClassVersion:s.classVersion},void 0!==s.status?{status:s.status}:{}),void 0!==s.payload?{payload:s.payload}:{})})}}class an extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNRemoveDataSyncMembershipOperation}validate(){if(!this.parameters.id)return"Membership id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return a(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${k(t)}`}}class on extends de{constructor(e){super({method:ce.PATCH}),this.parameters=e}operation(){return L.PNUpdateDataSyncMembershipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Membership id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${k(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Hs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class cn extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNGetDataSyncMembershipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Membership id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/memberships/${k(t)}`}}class un extends de{constructor(e){super({method:ce.POST}),this.parameters=e}operation(){return L.PNCreateDataSyncRelationshipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.entityAId?this.parameters.entityBId?this.parameters.class?this.parameters.data?this.parameters.data.classVersion?void 0:"Relationship class version cannot be empty":"Relationship data cannot be empty":"Relationship class cannot be empty":"Entity B id cannot be empty":"Entity A id cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.relationship+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships`}get body(){const{id:e,class:t,entityAId:s,entityBId:n,data:i}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),{entityAId:s,entityBId:n,relationshipClass:t,relationshipClassVersion:i.classVersion}),void 0!==i.status?{status:i.status}:{}),void 0!==i.payload?{payload:i.payload}:{})})}}class ln extends de{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return L.PNGetDataSyncRelationshipsOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.class)return"Relationship class cannot be empty"}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/relationships`}get queryParameters(){const{class:e,classVersion:t,entityAId:s,entityBId:n,cursor:i,limit:r,filter:a,filterFast:o,sort:c}=this.parameters,u=Ks(c);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({relationship_class:e},void 0!==t?{relationship_class_version:`${t}`}:{}),s?{entity_a_id:s}:{}),n?{entity_b_id:n}:{}),i?{cursor:i}:{}),r?{limit:`${r}`}:{}),a?{filter:a}:{}),o?{filter_fast:o}:{}),u.length?{sort:u}:{})}}class hn extends de{constructor(e){super({method:ce.PUT}),this.parameters=e}operation(){return L.PNSetDataSyncRelationshipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.entityAId?this.parameters.entityBId?this.parameters.data?this.parameters.data.classVersion?void 0:"Relationship class version cannot be empty":"Relationship data cannot be empty":"Entity B id cannot be empty":"Entity A id cannot be empty":"Relationship id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.relationship+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${k(t)}`}get body(){const{entityAId:e,entityBId:t,data:s}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityAId:e,entityBId:t,relationshipClassVersion:s.classVersion},void 0!==s.status?{status:s.status}:{}),void 0!==s.payload?{payload:s.payload}:{})})}}class dn extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNRemoveDataSyncRelationshipOperation}validate(){if(!this.parameters.id)return"Relationship id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return a(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${k(t)}`}}class pn extends de{constructor(e){super({method:ce.PATCH}),this.parameters=e}operation(){return L.PNUpdateDataSyncRelationshipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Relationship id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${k(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Hs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class gn extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNGetDataSyncRelationshipOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Relationship id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/relationships/${k(t)}`}}class bn extends de{constructor(e){super({method:ce.POST}),this.parameters=e}operation(){return L.PNCreateDataSyncEntityOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.class?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Entity data cannot be empty":"Entity class cannot be empty"}get headers(){var e;const t=null!==(e=super.headers)&&void 0!==e?e:{};return Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.entity+json;version=1"})}get path(){const{keySet:{subscribeKey:e}}=this.parameters;return`/v1/datasync/subkeys/${e}/entities`}get body(){const{id:e,class:t,classLevel:s,data:n}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},void 0!==e?{id:e}:{}),{entityClass:t,entityClassVersion:n.classVersion}),void 0!==s?{entityClassLevel:s}:{}),void 0!==n.status?{status:n.status}:{}),void 0!==n.payload?{payload:n.payload}:{})})}}class yn extends de{constructor(e){var t;super(),this.parameters=e,null!==(t=e.limit)&&void 0!==t||(e.limit=20)}operation(){return L.PNGetDataSyncEntitiesOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.class)return"Entity class cannot be empty"}get path(){return`/v1/datasync/subkeys/${this.parameters.keySet.subscribeKey}/entities`}get queryParameters(){const{class:e,classVersion:t,classLevel:s,cursor:n,limit:i,filter:r,filterFast:a,sort:o}=this.parameters,c=Ks(o);return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({entity_class:e},void 0!==t?{entity_class_version:`${t}`}:{}),s?{entity_class_level:s}:{}),n?{cursor:n}:{}),i?{limit:`${i}`}:{}),r?{filter:r}:{}),a?{filter_fast:a}:{}),c.length?{sort:c}:{})}}class mn extends de{constructor(e){super({method:ce.PUT}),this.parameters=e}operation(){return L.PNSetDataSyncEntityOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){return this.parameters.id?this.parameters.data?void 0===this.parameters.data.classVersion||null===this.parameters.data.classVersion?"Entity class version cannot be empty":void 0:"Entity data cannot be empty":"Entity id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/vnd.pubnub.objects.entity+json;version=1"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${k(t)}`}get body(){const{data:e}=this.parameters;return JSON.stringify({data:Object.assign(Object.assign({entityClassVersion:e.classVersion},void 0!==e.status?{status:e.status}:{}),void 0!==e.payload?{payload:e.payload}:{})})}}class fn extends de{constructor(e){super({method:ce.DELETE}),this.parameters=e}operation(){return L.PNRemoveDataSyncEntityOperation}validate(){if(!this.parameters.id)return"Entity id cannot be empty"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.keys(t).length>0?t:void 0}parse(e){return a(this,void 0,void 0,(function*(){return{status:e.status}}))}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${k(t)}`}}class vn extends de{constructor(e){super({method:ce.PATCH}),this.parameters=e}operation(){return L.PNUpdateDataSyncEntityOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Entity id cannot be empty";const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=e&&Object.keys(e).length>0,o=t&&Object.keys(t).length>0,c=s&&s.length>0,u=n&&n.length>0,l=i&&i.length>0,h=r&&Object.keys(r).length>0;return a||o||c||u||l||h?void 0:"At least one of add, replace, remove, move, copy, or test must be provided"}get headers(){var e;let t=null!==(e=super.headers)&&void 0!==e?e:{};return this.parameters.ifMatchesEtag&&(t=Object.assign(Object.assign({},t),{"If-Match":this.parameters.ifMatchesEtag})),Object.assign(Object.assign({},t),{"Content-Type":"application/json-patch+json"})}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${k(t)}`}get body(){const{add:e,replace:t,remove:s,move:n,copy:i,test:r}=this.parameters,a=Hs({add:e,replace:t,remove:s,move:n,copy:i,test:r});return JSON.stringify(a)}}class On extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNGetDataSyncEntityOperation}parse(e){return a(this,void 0,void 0,(function*(){const t=this.deserializeResponse(e);return Object.assign(Object.assign({},t),{status:e.status})}))}validate(){if(!this.parameters.id)return"Entity id cannot be empty"}get path(){const{keySet:{subscribeKey:e},id:t}=this.parameters;return`/v1/datasync/subkeys/${e}/entities/${k(t)}`}}class Sn{constructor(e,t){this.keySet=e.keySet,this.configuration=e,this.sendRequest=t}get logger(){return this.configuration.logger()}createEntity(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Entity with parameters:"})));const s=new bn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getEntity(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Entity with parameters:"})));const s=new On(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getEntities(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Entities with parameters:"})));const s=new yn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}setEntity(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Entity with parameters:"})));const s=new mn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateEntity(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Entity with parameters:"})));const s=new vn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeEntity(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Entity with parameters:"})));const s=new fn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createRelationship(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Relationship with parameters:"})));const s=new un(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getRelationship(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Relationship with parameters:"})));const s=new gn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getRelationships(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Relationships with parameters:"})));const s=new ln(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}setRelationship(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Relationship with parameters:"})));const s=new hn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateRelationship(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Relationship with parameters:"})));const s=new pn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeRelationship(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Relationship with parameters:"})));const s=new dn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createUser(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create User with parameters:"})));const s=new Ls(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getUser(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get User with parameters:"})));const s=new Js(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getUsers(e,t){return a(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},s),details:"Get Users with parameters:"})));const n=new Vs(Object.assign(Object.assign({},s),{keySet:this.keySet}));return t?this.sendRequest(n,t):this.sendRequest(n)}))}setUser(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set User with parameters:"})));const s=new Ws(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateUser(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update User with parameters:"})));const s=new zs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeUser(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove User with parameters:"})));const s=new Bs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createChannel(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Channel with parameters:"})));const s=new Ys(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getChannel(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Channel with parameters:"})));const s=new tn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getChannels(e,t){return a(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},s),details:"Get Channels with parameters:"})));const n=new Qs(Object.assign(Object.assign({},s),{keySet:this.keySet}));return t?this.sendRequest(n,t):this.sendRequest(n)}))}setChannel(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Channel with parameters:"})));const s=new Xs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateChannel(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Channel with parameters:"})));const s=new en(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeChannel(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Channel with parameters:"})));const s=new Zs(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}createMembership(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Membership with parameters:"})));const s=new sn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getMembership(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get Membership with parameters:"})));const s=new cn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}getMemberships(e,t){return a(this,void 0,void 0,(function*(){const s=e&&"function"!=typeof e?e:{};null!=t||(t="function"==typeof e?e:void 0),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},s),details:"Get Memberships with parameters:"})));const n=new nn(Object.assign(Object.assign({},s),{keySet:this.keySet}));return t?this.sendRequest(n,t):this.sendRequest(n)}))}setMembership(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set Membership with parameters:"})));const s=new rn(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}updateMembership(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Membership with parameters:"})));const s=new on(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}removeMembership(e,t){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Membership with parameters:"})));const s=new an(Object.assign(Object.assign({},e),{keySet:this.keySet}));return t?this.sendRequest(s,t):this.sendRequest(s)}))}}class jn extends de{constructor(){super()}operation(){return L.PNTimeOperation}parse(e){return a(this,void 0,void 0,(function*(){return{timetoken:this.deserializeResponse(e)[0]}}))}get path(){return"/time/0"}}class wn extends de{constructor(e){super(),this.parameters=e}operation(){return L.PNDownloadFileOperation}validate(){const{channel:e,id:t,name:s}=this.parameters;return e?t?s?void 0:"file name can't be empty":"file id can't be empty":"channel can't be empty"}parse(e){return a(this,void 0,void 0,(function*(){const{cipherKey:t,crypto:s,cryptography:n,name:i,PubNubFile:r}=this.parameters,a=e.headers["content-type"];let o,c=e.body;return r.supportsEncryptFile&&(t||s)&&(t&&n?c=yield n.decrypt(t,c):!t&&s&&(o=yield s.decryptFile(r.create({data:c,name:i,mimeType:a}),r))),o||r.create({data:c,name:i,mimeType:a})}))}get path(){const{keySet:{subscribeKey:e},channel:t,id:s,name:n}=this.parameters;return`/v1/files/${e}/channels/${k(t)}/files/${k(s)}/${k(n)}`}}class kn{static notificationPayload(e,t){return new Pe(e,t)}static generateUUID(){return ne.createUUID()}constructor(e){if(this.requestsPreparation=Promise.resolve(),this.eventHandleCapable={},this.entities={},this._configuration=e.configuration,this.cryptography=e.cryptography,this.tokenManager=e.tokenManager,this.transport=e.transport,this.crypto=e.crypto,this.logger.debug("PubNub",(()=>({messageType:"object",message:e.configuration,details:"Create with configuration:",ignoredKeys:(e,t)=>"function"==typeof t[e]||e.startsWith("_")||"keySet"===e||w(e)}))),this._objects=new Gs(this._configuration,this.sendRequest.bind(this)),this._dataSync=new Sn(this._configuration,this.sendRequest.bind(this)),this._channelGroups=new js(this._configuration.logger(),this._configuration.keySet,this.sendRequest.bind(this)),this._push=new Es(this._configuration.logger(),this._configuration.keySet,this.sendRequest.bind(this)),this.eventDispatcher=new fe,this._configuration.enableEventEngine){this.logger.debug("PubNub","Using new subscription loop management.");let e=this._configuration.getHeartbeatInterval();this.presenceState={},e&&(this.presenceEventEngine=new st({heartbeat:(e,t)=>(this.logger.trace("PresenceEventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Heartbeat with parameters:"}))),this.heartbeat(e,t)),leave:e=>{this.logger.trace("PresenceEventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),this.makeUnsubscribe(e,(()=>{}))},heartbeatDelay:()=>{let t=()=>{};const s=new Promise(((s,n)=>{if(e=this._configuration.getHeartbeatInterval(),e){const i=setTimeout((()=>{s()}),1e3*e);t=()=>{clearTimeout(i),n(new c("Heartbeat interval has been cancelled."))}}else n(new c("Heartbeat interval has been reset."))}));return{cancelDelay:t,promise:s}},emitStatus:e=>this.emitStatus(e),config:this._configuration,presenceState:this.presenceState})),this.eventEngine=new kt({handshake:e=>(this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Handshake with parameters:",ignoredKeys:["abortSignal","crypto","timeout","keySet","getFileUrl"]}))),this.subscribeHandshake(e)),receiveMessages:e=>(this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Receive messages with parameters:",ignoredKeys:["abortSignal","crypto","timeout","keySet","getFileUrl"]}))),this.subscribeReceiveMessages(e)),join:e=>{var t,s;this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Join with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("EventEngine","Ignoring 'join' announcement request."):this.join(e)},leave:e=>{var t,s;this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("EventEngine","Ignoring 'leave' announcement request."):this.leave(e)},leaveAll:e=>{this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave all with parameters:"}))),this.leaveAll(e)},presenceReconnect:e=>{this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Reconnect with parameters:"}))),this.presenceReconnect(e)},presenceDisconnect:e=>{this.logger.trace("EventEngine",(()=>({messageType:"object",message:Object.assign({},e),details:"Disconnect with parameters:"}))),this.presenceDisconnect(e)},presenceState:this.presenceState,config:this._configuration,emitMessages:(e,t)=>{try{this.logger.debug("EventEngine",(()=>({messageType:"object",message:t.map((e=>{const t=e.type===pe.Message||e.type===pe.Signal?M(e.data.message):void 0;return t?{type:e.type,data:Object.assign(Object.assign({},e.data),{pn_mfp:t})}:e})),details:"Received events:"}))),t.forEach((t=>this.emitEvent(e,t)))}catch(e){const t={error:!0,category:o.PNUnknownCategory,errorData:e,statusCode:0};this.emitStatus(t)}},emitStatus:e=>this.emitStatus(e)})}else this.logger.debug("PubNub","Using legacy subscription loop management."),this.subscriptionManager=new Se(this._configuration,((e,t)=>{try{this.emitEvent(e,t)}catch(e){const t={error:!0,category:o.PNUnknownCategory,errorData:e,statusCode:0};this.emitStatus(t)}}),this.emitStatus.bind(this),((e,t)=>{this.logger.trace("SubscriptionManager",(()=>({messageType:"object",message:Object.assign({},e),details:"Subscribe with parameters:",ignoredKeys:["crypto","timeout","keySet","getFileUrl"]}))),this.makeSubscribe(e,t)}),((e,t)=>(this.logger.trace("SubscriptionManager",(()=>({messageType:"object",message:Object.assign({},e),details:"Heartbeat with parameters:",ignoredKeys:["crypto","timeout","keySet","getFileUrl"]}))),this.heartbeat(e,t))),((e,t)=>{this.logger.trace("SubscriptionManager",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),this.makeUnsubscribe(e,t)}),this.time.bind(this))}get configuration(){return this._configuration}get _config(){return this.configuration}get authKey(){var e;return null!==(e=this._configuration.authKey)&&void 0!==e?e:void 0}getAuthKey(){return this.authKey}setAuthKey(e){this.logger.debug("PubNub","Auth key updated."),this._configuration.setAuthKey(e),this.onAuthenticationChange&&this.onAuthenticationChange(e)}get userId(){return this._configuration.userId}set userId(e){if(!e||"string"!=typeof e||0===e.trim().length){const e=new Error("Missing or invalid userId parameter. Provide a valid string userId");throw this.logger.error("PubNub",(()=>({messageType:"error",message:e}))),e}this.logger.debug("PubNub",`Set user ID: ${e}`),this._configuration.userId=e,this.onUserIdChange&&this.onUserIdChange(this._configuration.userId)}getUserId(){return this._configuration.userId}setUserId(e){this.userId=e}get filterExpression(){var e;return null!==(e=this._configuration.getFilterExpression())&&void 0!==e?e:void 0}getFilterExpression(){return this.filterExpression}set filterExpression(e){this.logger.debug("PubNub",`Set filter expression: ${e}`),this._configuration.setFilterExpression(e)}setFilterExpression(e){this.logger.debug("PubNub",`Set filter expression: ${e}`),this.filterExpression=e}get cipherKey(){return this._configuration.getCipherKey()}set cipherKey(e){this._configuration.setCipherKey(e)}setCipherKey(e){this.logger.debug("PubNub","Cipher key updated."),this.cipherKey=e}set heartbeatInterval(e){var t;this.logger.debug("PubNub",`Set heartbeat interval: ${e}`),this._configuration.setHeartbeatInterval(e),this.onHeartbeatIntervalChange&&this.onHeartbeatIntervalChange(null!==(t=this._configuration.getHeartbeatInterval())&&void 0!==t?t:0)}setHeartbeatInterval(e){this.heartbeatInterval=e}get logger(){return this._configuration.logger()}getVersion(){return this._configuration.getVersion()}_addPnsdkSuffix(e,t){this.logger.debug("PubNub",`Add '${e}' 'pnsdk' suffix: ${t}`),this._configuration._addPnsdkSuffix(e,t)}getUUID(){return this.userId}setUUID(e){this.logger.warn("PubNub","'setUserId` is deprecated, please use 'setUserId' or 'userId' setter instead."),this.logger.debug("PubNub",`Set UUID: ${e}`),this.userId=e}get customEncrypt(){return this._configuration.getCustomEncrypt()}get customDecrypt(){return this._configuration.getCustomDecrypt()}channel(e){let t=this.entities[`${e}_ch`];return t||(t=this.entities[`${e}_ch`]=new ys(e,this)),t}channelGroup(e){let t=this.entities[`${e}_chg`];return t||(t=this.entities[`${e}_chg`]=new gs(e,this)),t}channelMetadata(e){let t=this.entities[`${e}_chm`];return t||(t=this.entities[`${e}_chm`]=new ds(e,this)),t}userMetadata(e){let t=this.entities[`${e}_um`];return t||(t=this.entities[`${e}_um`]=new bs(e,this)),t}dataSyncUser(e){let t=this.entities[`${e}_dsu`];return t||(t=this.entities[`${e}_dsu`]=new ps(e,this)),t}dataSyncChannel(e){let t=this.entities[`${e}_dsc`];return t||(t=this.entities[`${e}_dsc`]=new ls(e,this)),t}dataSyncMembership(e){let t=this.entities[`${e}_dsm`];return t||(t=this.entities[`${e}_dsm`]=new us(e,this)),t}dataSyncEntity(e){let t=this.entities[`${e}_dse`];return t||(t=this.entities[`${e}_dse`]=new hs(e,this)),t}dataSyncRelationship(e){let t=this.entities[`${e}_dsr`];return t||(t=this.entities[`${e}_dsr`]=new cs(e,this)),t}subscriptionSet(e){var t,s;{const n=[];return null===(t=e.channels)||void 0===t||t.forEach((e=>n.push(this.channel(e)))),null===(s=e.channelGroups)||void 0===s||s.forEach((e=>n.push(this.channelGroup(e)))),new _t({client:this,entities:n,options:e.subscriptionOptions})}}sendRequest(e,t){return a(this,void 0,void 0,(function*(){const s=e.validate();if(s){const e=(n=s,u(Object.assign({message:n},{}),o.PNValidationErrorCategory));if(this.logger.error("PubNub",(()=>({messageType:"error",message:e}))),t)return t(e,null);throw new c("Validation failed, check status for details",e)}var n;const i=e.prepare();if(i){const s=this.requestsPreparation.then((()=>i));this.requestsPreparation=s.catch((()=>{}));try{yield s}catch(s){return this.handleRequestError(s,e.operation(),t)}}const r=e.request(),a=e.operation();r.formData&&r.formData.length>0||a===L.PNDownloadFileOperation?r.timeout=this._configuration.getFileTimeout():a===L.PNSubscribeOperation||a===L.PNReceiveMessagesOperation?r.timeout=this._configuration.getSubscribeTimeout():r.timeout=this._configuration.getTransactionTimeout();const l={error:!1,operation:a,category:o.PNAcknowledgmentCategory,statusCode:0},[h,d]=this.transport.makeSendable(r);return e.cancellationController=d||null,h.then((t=>{if(l.statusCode=t.status,200!==t.status&&204!==t.status){const e=kn.decoder.decode(t.body),s=t.headers["content-type"];if(s||-1!==s.indexOf("javascript")||-1!==s.indexOf("json")){const t=JSON.parse(e);"object"==typeof t&&"error"in t&&t.error&&"object"==typeof t.error&&(l.errorData=t.error)}else l.responseText=e}return e.parse(t)})).then((e=>t?t(l,e):e)).catch((e=>this.handleRequestError(e,a,t)))}))}handleRequestError(e,t,s){const n=e instanceof q?e:q.create(e);if(s)return n.category!==o.PNCancelledCategory&&this.logger.error("PubNub",(()=>({messageType:"error",message:n.toPubNubError(t,"REST API request processing error, check status for details")}))),s(n.toStatus(t),null);const i=n.toPubNubError(t,"REST API request processing error, check status for details");throw n.category!==o.PNCancelledCategory&&this.logger.error("PubNub",(()=>({messageType:"error",message:i}))),i}destroy(e=!1){this.logger.info("PubNub","Destroying PubNub client."),this._globalSubscriptionSet&&(this._globalSubscriptionSet.invalidate(!0),this._globalSubscriptionSet=void 0),Object.values(this.eventHandleCapable).forEach((e=>e.invalidate(!0))),this.eventHandleCapable={},this.subscriptionManager?(this.subscriptionManager.unsubscribeAll(e),this.subscriptionManager.disconnect()):this.eventEngine&&this.eventEngine.unsubscribeAll(e),this.presenceEventEngine&&this.presenceEventEngine.leaveAll(e)}stop(){this.logger.warn("PubNub","'stop' is deprecated, please use 'destroy' instead."),this.destroy()}publish(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Publish with parameters:"})));const s=!1===e.replicate&&!1===e.storeInHistory,n=new Pt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule()})),i=e=>{e&&this.logger.debug("PubNub",`${s?"Fire":"Publish"} success with timetoken: ${e.timetoken}`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}}))}signal(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Signal with parameters:"})));const s=new Ct(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Publish success with timetoken: ${e.timetoken}`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}fire(e,t){return a(this,void 0,void 0,(function*(){return this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fire with parameters:"}))),null!=t||(t=()=>{}),this.publish(Object.assign(Object.assign({},e),{replicate:!1,storeInHistory:!1}),t)}))}get globalSubscriptionSet(){return this._globalSubscriptionSet||(this._globalSubscriptionSet=this.subscriptionSet({})),this._globalSubscriptionSet}get subscriptionTimetoken(){return this.subscriptionManager?this.subscriptionManager.subscriptionTimetoken:this.eventEngine?this.eventEngine.subscriptionTimetoken:void 0}getSubscribedChannels(){return this.subscriptionManager?this.subscriptionManager.subscribedChannels:this.eventEngine?this.eventEngine.getSubscribedChannels():[]}getSubscribedChannelGroups(){return this.subscriptionManager?this.subscriptionManager.subscribedChannelGroups:this.eventEngine?this.eventEngine.getSubscribedChannelGroups():[]}registerEventHandleCapable(e,t,s){{let n;this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign(Object.assign({subscription:e},t?{cursor:t}:[]),s?{subscriptions:s}:{}),details:"Register event handle capable:"}))),this.eventHandleCapable[e.state.id]||(this.eventHandleCapable[e.state.id]=e),s&&0!==s.length?(n=new It({}),s.forEach((e=>n.add(e.subscriptionInput(!1))))):n=e.subscriptionInput(!1);const i={};i.channels=n.channels,i.channelGroups=n.channelGroups,t&&(i.timetoken=t.timetoken),this.subscriptionManager?this.subscriptionManager.subscribe(i):this.eventEngine&&this.eventEngine.subscribe(i)}}unregisterEventHandleCapable(e,t){{if(!this.eventHandleCapable[e.state.id])return;const s=[];this.logger.trace("PubNub",(()=>({messageType:"object",message:{subscription:e,subscriptions:t},details:"Unregister event handle capable:"})));let n,i=!t||0===t.length;if(!i&&e instanceof _t&&e.subscriptions.length===(null==t?void 0:t.length)&&(i=e.subscriptions.every((e=>t.includes(e)))),i&&delete this.eventHandleCapable[e.state.id],t&&0!==t.length?(n=new It({}),t.forEach((e=>{const t=e.subscriptionInput(!0);t.isEmpty?s.push(e):n.add(t)}))):(n=e.subscriptionInput(!0),n.isEmpty&&s.push(e)),s.length>0&&this.logger.trace("PubNub",(()=>{const e=[];return s[0]instanceof _t?s[0].subscriptions.forEach((t=>e.push(t.state.entity))):s.forEach((t=>e.push(t.state.entity))),{messageType:"object",message:{entities:e},details:"Can't unregister event handle capable because entities still in use:"}})),n.isEmpty)return;{const e=[],t=[];if(Object.values(this.eventHandleCapable).forEach((s=>{const i=s.subscriptionInput(!1),r=i.channelGroups,a=i.channels;e.push(...n.channelGroups.filter((e=>r.includes(e)))),t.push(...n.channels.filter((e=>a.includes(e))))})),(t.length>0||e.length>0)&&(this.logger.trace("PubNub",(()=>{const s=[],i=n=>{const i=n.subscriptionNames(!0),r=n.subscriptionType===Tt.Channel?t:e;i.some((e=>r.includes(e)))&&s.push(n)};Object.values(this.eventHandleCapable).forEach((e=>{e instanceof _t?e.subscriptions.forEach((e=>{i(e.state.entity)})):e instanceof At&&i(e.state.entity)}));let r="Some entities still in use:";return t.length+e.length===n.length&&(r="Can't unregister event handle capable because entities still in use:"),{messageType:"object",message:{entities:s},details:r}})),n.remove(new It({channels:t,channelGroups:e})),n.isEmpty))return}const r={};r.channels=n.channels,r.channelGroups=n.channelGroups,this.subscriptionManager?this.subscriptionManager.unsubscribe(r):this.eventEngine&&this.eventEngine.unsubscribe(r)}}subscribe(e){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Subscribe with parameters:"})));const t=this.subscriptionSet(Object.assign(Object.assign({},e),{subscriptionOptions:{receivePresenceEvents:e.withPresence}}));this.globalSubscriptionSet.addSubscriptionSet(t),t.dispose();const s="number"==typeof e.timetoken?`${e.timetoken}`:e.timetoken;this.globalSubscriptionSet.subscribe({timetoken:s})}}makeSubscribe(e,t){{this._configuration.isSharedWorkerEnabled()||(e.onDemand=!1);const s=new me(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)}));if(this.sendRequest(s,((e,n)=>{var i;this.subscriptionManager&&(null===(i=this.subscriptionManager.abort)||void 0===i?void 0:i.identifier)===s.requestIdentifier&&(this.subscriptionManager.abort=null),t(e,n)})),this.subscriptionManager){const e=()=>s.abort("Cancel long-poll subscribe request");e.identifier=s.requestIdentifier,this.subscriptionManager.abort=e}}}unsubscribe(e){{if(this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Unsubscribe with parameters:"}))),!this._globalSubscriptionSet)return void this.logger.debug("PubNub","There are no active subscriptions. Ignore.");const t=this.globalSubscriptionSet.subscriptions.filter((t=>{var s,n;const i=t.subscriptionInput(!1);if(i.isEmpty)return!1;for(const t of null!==(s=e.channels)&&void 0!==s?s:[])if(i.contains(t))return!0;for(const t of null!==(n=e.channelGroups)&&void 0!==n?n:[])if(i.contains(t))return!0}));t.length>0&&this.globalSubscriptionSet.removeSubscriptions(t)}}makeUnsubscribe(e,t){{let{channels:s,channelGroups:n}=e;if(this._configuration.getKeepPresenceChannelsInPresenceRequests()||(n&&(n=n.filter((e=>!e.endsWith("-pnpres")))),s&&(s=s.filter((e=>!e.endsWith("-pnpres"))))),0===(null!=n?n:[]).length&&0===(null!=s?s:[]).length)return t({error:!1,operation:L.PNUnsubscribeOperation,category:o.PNAcknowledgmentCategory,statusCode:200});this.sendRequest(new qt({channels:s,channelGroups:n,keySet:this._configuration.keySet}),t)}}unsubscribeAll(){this.logger.debug("PubNub","Unsubscribe all channels and groups"),this._globalSubscriptionSet&&this._globalSubscriptionSet.invalidate(!1),Object.values(this.eventHandleCapable).forEach((e=>e.invalidate(!1))),this.eventHandleCapable={},this.subscriptionManager?this.subscriptionManager.unsubscribeAll():this.eventEngine&&this.eventEngine.unsubscribeAll()}disconnect(e=!1){this.logger.debug("PubNub",`Disconnect (while offline? ${e?"yes":"no"})`),this.subscriptionManager?this.subscriptionManager.disconnect():this.eventEngine&&this.eventEngine.disconnect(e)}reconnect(e){this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Reconnect with parameters:"}))),this.subscriptionManager?this.subscriptionManager.reconnect():this.eventEngine&&this.eventEngine.reconnect(null!=e?e:{})}subscribeHandshake(e){return a(this,void 0,void 0,(function*(){{this._configuration.isSharedWorkerEnabled()||(e.onDemand=!1),e.heartbeat||(e.heartbeat=this._configuration.getPresenceTimeout());const t=new Et(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)})),s=e.abortSignal.subscribe((e=>{t.abort("Cancel subscribe handshake request")}));return this.sendRequest(t).then((e=>(s(),e.cursor)))}}))}subscribeReceiveMessages(e){return a(this,void 0,void 0,(function*(){{this._configuration.isSharedWorkerEnabled()||(e.onDemand=!1),e.heartbeat||(e.heartbeat=this._configuration.getPresenceTimeout());const t=new Nt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)})),s=e.abortSignal.subscribe((e=>{t.abort("Cancel long-poll subscribe request")}));return this.sendRequest(t).then((e=>(s(),e)))}}))}getMessageActions(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get message actions with parameters:"})));const s=new zt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Get message actions success. Received ${e.data.length} message actions.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}addMessageAction(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Add message action with parameters:"})));const s=new Jt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Message action add success. Message action added with timetoken: ${e.data.actionTimetoken}`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}removeMessageAction(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove message action with parameters:"})));const s=new Yt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Message action remove success. Removed message action with ${e.actionTimetoken} timetoken.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}fetchMessages(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch messages with parameters:"})));const s=new Bt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule(),getFileUrl:this.getFileUrl.bind(this)})),n=e=>{if(!e)return;const t=Object.values(e.channels).reduce(((e,t)=>e+t.length),0);this.logger.debug("PubNub",`Fetch messages success. Received ${t} messages.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}deleteMessages(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Delete messages with parameters:"})));const s=new Kt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub","Delete messages success.")};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}messageCounts(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get messages count with parameters:"})));const s=new Ht(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=t=>{if(!t)return;const s=Object.values(t.channels).reduce(((e,t)=>e+t),0);this.logger.debug("PubNub",`Get messages count success. There are ${s} messages since provided reference timetoken${e.channelTimetokens?e.channelTimetokens.join(","):""}.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}history(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch history with parameters:"})));const s=new Vt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule()})),n=e=>{e&&this.logger.debug("PubNub",`Fetch history success. Received ${e.messages.length} messages.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}hereNow(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Here now with parameters:"})));const s=new Lt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`Here now success. There are ${e.totalOccupancy} participants in ${e.totalChannels} channels.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}whereNow(e,t){return a(this,void 0,void 0,(function*(){var s;{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Where now with parameters:"})));const n=new Gt({uuid:null!==(s=e.uuid)&&void 0!==s?s:this._configuration.userId,keySet:this._configuration.keySet}),i=e=>{e&&this.logger.debug("PubNub",`Where now success. Currently present in ${e.channels.length} channels.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}}))}getState(e,t){return a(this,void 0,void 0,(function*(){var s;{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Get presence state with parameters:"})));const n=new $t(Object.assign(Object.assign({},e),{uuid:null!==(s=e.uuid)&&void 0!==s?s:this._configuration.userId,keySet:this._configuration.keySet})),i=e=>{e&&this.logger.debug("PubNub",`Get presence state success. Received presence state for ${Object.keys(e.channels).length} channels.`)};return t?this.sendRequest(n,((e,s)=>{i(s),t(e,s)})):this.sendRequest(n).then((e=>(i(e),e)))}}))}setState(e,t){return a(this,void 0,void 0,(function*(){var s,n;{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Set presence state with parameters:"})));const{keySet:i,userId:r}=this._configuration,a=this._configuration.getPresenceTimeout();let o;if(this._configuration.enableEventEngine&&this.presenceState){const t=this.presenceState;null===(s=e.channels)||void 0===s||s.forEach((s=>t[s]=e.state)),"channelGroups"in e&&(null===(n=e.channelGroups)||void 0===n||n.forEach((s=>t[s]=e.state))),this.onPresenceStateChange&&this.onPresenceStateChange(this.presenceState)}o="withHeartbeat"in e&&e.withHeartbeat?new xt(Object.assign(Object.assign({},e),{keySet:i,heartbeat:a})):new Ft(Object.assign(Object.assign({},e),{keySet:i,uuid:r}));const c=e=>{e&&this.logger.debug("PubNub","Set presence state success."+(o instanceof xt?" Presence state has been set using heartbeat endpoint.":""))};return this.subscriptionManager&&(this.subscriptionManager.setState(e),this.onPresenceStateChange&&this.onPresenceStateChange(this.subscriptionManager.presenceState)),t?this.sendRequest(o,((e,s)=>{c(s),t(e,s)})):this.sendRequest(o).then((e=>(c(e),e)))}}))}presence(e){var t;this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Change presence with parameters:"}))),null===(t=this.subscriptionManager)||void 0===t||t.changePresence(e)}heartbeat(e,t){return a(this,void 0,void 0,(function*(){var s;{this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Heartbeat with parameters:"})));let{channels:n,channelGroups:i}=e;if(i&&(i=i.filter((e=>!e.endsWith("-pnpres")))),n&&(n=n.filter((e=>!e.endsWith("-pnpres")))),0===(null!=i?i:[]).length&&0===(null!=n?n:[]).length){const e={error:!1,operation:L.PNHeartbeatOperation,category:o.PNAcknowledgmentCategory,statusCode:200};return this.logger.trace("PubNub","There are no active subscriptions. Ignore."),t?t(e,{}):Promise.resolve(e)}const r=new xt(Object.assign(Object.assign({},e),{channels:[...new Set(n)],channelGroups:[...new Set(i)],keySet:this._configuration.keySet})),a=e=>{e&&this.logger.trace("PubNub","Heartbeat success.")},c=null===(s=e.abortSignal)||void 0===s?void 0:s.subscribe((e=>{r.abort("Cancel long-poll subscribe request")}));return t?this.sendRequest(r,((e,s)=>{a(s),c&&c(),t(e,s)})):this.sendRequest(r).then((e=>(a(e),c&&c(),e)))}}))}join(e){var t,s;this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Join with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("PubNub","Ignoring 'join' announcement request."):this.presenceEventEngine?this.presenceEventEngine.join(e):this.heartbeat(Object.assign(Object.assign({channels:e.channels,channelGroups:e.groups},this._configuration.maintainPresenceState&&this.presenceState&&Object.keys(this.presenceState).length>0&&{state:this.presenceState}),{heartbeat:this._configuration.getPresenceTimeout()}),(()=>{}))}presenceReconnect(e){this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Presence reconnect with parameters:"}))),this.presenceEventEngine?this.presenceEventEngine.reconnect():this.heartbeat(Object.assign(Object.assign({channels:e.channels,channelGroups:e.groups},this._configuration.maintainPresenceState&&{state:this.presenceState}),{heartbeat:this._configuration.getPresenceTimeout()}),(()=>{}))}leave(e){var t,s,n;this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave with parameters:"}))),e&&0===(null!==(t=e.channels)&&void 0!==t?t:[]).length&&0===(null!==(s=e.groups)&&void 0!==s?s:[]).length?this.logger.trace("PubNub","Ignoring 'leave' announcement request."):this.presenceEventEngine?null===(n=this.presenceEventEngine)||void 0===n||n.leave(e):this.makeUnsubscribe({channels:e.channels,channelGroups:e.groups},(()=>{}))}leaveAll(e={}){this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Leave all with parameters:"}))),this.presenceEventEngine?this.presenceEventEngine.leaveAll(!!e.isOffline):e.isOffline||this.makeUnsubscribe({channels:e.channels,channelGroups:e.groups},(()=>{}))}presenceDisconnect(e){this.logger.trace("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Presence disconnect parameters:"}))),this.presenceEventEngine?this.presenceEventEngine.disconnect(!!e.isOffline):e.isOffline||this.makeUnsubscribe({channels:e.channels,channelGroups:e.groups},(()=>{}))}grantToken(e,t){return a(this,void 0,void 0,(function*(){throw new Error("Grant Token error: PAM module disabled")}))}revokeToken(e,t){return a(this,void 0,void 0,(function*(){throw new Error("Revoke Token error: PAM module disabled")}))}get token(){return this.tokenManager&&this.tokenManager.getToken()}getToken(){return this.token}set token(e){this.tokenManager&&this.tokenManager.setToken(e),this.onAuthenticationChange&&this.onAuthenticationChange(e)}setToken(e){this.logger.debug("PubNub","Access token updated."),this.token=e}parseToken(e){return this.logger.debug("PubNub","Parse access token."),this.tokenManager&&this.tokenManager.parseToken(e)}grant(e,t){return a(this,void 0,void 0,(function*(){throw new Error("Grant error: PAM module disabled")}))}audit(e,t){return a(this,void 0,void 0,(function*(){throw new Error("Grant Permissions error: PAM module disabled")}))}get objects(){return this._objects}get dataSync(){return this._dataSync}fetchUsers(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchUsers' is deprecated. Use 'pubnub.objects.getAllUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Fetch all User objects with parameters:"}))),this.objects._getAllUUIDMetadata(e,t)}))}fetchUser(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchUser' is deprecated. Use 'pubnub.objects.getUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.userId},details:`Fetch${e&&"function"!=typeof e?"":" current"} User object with parameters:`}))),this.objects._getUUIDMetadata(e,t)}))}createUser(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'createUser' is deprecated. Use 'pubnub.objects.setUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create User object with parameters:"}))),this.objects._setUUIDMetadata(e,t)}))}updateUser(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'updateUser' is deprecated. Use 'pubnub.objects.setUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update User object with parameters:"}))),this.objects._setUUIDMetadata(e,t)}))}removeUser(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'removeUser' is deprecated. Use 'pubnub.objects.removeUUIDMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{uuid:this.userId},details:`Remove${e&&"function"!=typeof e?"":" current"} User object with parameters:`}))),this.objects._removeUUIDMetadata(e,t)}))}fetchSpaces(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchSpaces' is deprecated. Use 'pubnub.objects.getAllChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:e&&"function"!=typeof e?e:{},details:"Fetch all Space objects with parameters:"}))),this.objects._getAllChannelMetadata(e,t)}))}fetchSpace(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'fetchSpace' is deprecated. Use 'pubnub.objects.getChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Fetch Space object with parameters:"}))),this.objects._getChannelMetadata(e,t)}))}createSpace(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'createSpace' is deprecated. Use 'pubnub.objects.setChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Create Space object with parameters:"}))),this.objects._setChannelMetadata(e,t)}))}updateSpace(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'updateSpace' is deprecated. Use 'pubnub.objects.setChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update Space object with parameters:"}))),this.objects._setChannelMetadata(e,t)}))}removeSpace(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'removeSpace' is deprecated. Use 'pubnub.objects.removeChannelMetadata' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove Space object with parameters:"}))),this.objects._removeChannelMetadata(e,t)}))}fetchMemberships(e,t){return a(this,void 0,void 0,(function*(){return this.objects.fetchMemberships(e,t)}))}addMemberships(e,t){return a(this,void 0,void 0,(function*(){return this.objects.addMemberships(e,t)}))}updateMemberships(e,t){return a(this,void 0,void 0,(function*(){return this.logger.warn("PubNub","'addMemberships' is deprecated. Use 'pubnub.objects.setChannelMembers' or 'pubnub.objects.setMemberships' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Update memberships with parameters:"}))),this.objects.addMemberships(e,t)}))}removeMemberships(e,t){return a(this,void 0,void 0,(function*(){var s,n,i;{if(this.logger.warn("PubNub","'removeMemberships' is deprecated. Use 'pubnub.objects.removeMemberships' or 'pubnub.objects.removeChannelMembers' instead."),this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Remove memberships with parameters:"}))),"spaceId"in e){const i=e,r={channel:null!==(s=i.spaceId)&&void 0!==s?s:i.channel,uuids:null!==(n=i.userIds)&&void 0!==n?n:i.uuids,limit:0};return t?this.objects.removeChannelMembers(r,t):this.objects.removeChannelMembers(r)}const r=e,a={uuid:r.userId,channels:null!==(i=r.spaceIds)&&void 0!==i?i:r.channels,limit:0};return t?this.objects.removeMemberships(a,t):this.objects.removeMemberships(a)}}))}get channelGroups(){return this._channelGroups}get push(){return this._push}sendFile(e,t){return a(this,void 0,void 0,(function*(){{if(!this._configuration.PubNubFile)throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform.");this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Send file with parameters:"})));const s=new ns(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,PubNubFile:this._configuration.PubNubFile,fileUploadPublishRetryLimit:this._configuration.fileUploadPublishRetryLimit,file:e.file,sendRequest:this.sendRequest.bind(this),publishFile:this.publishFile.bind(this),crypto:this._configuration.getCryptoModule(),cryptography:this.cryptography?this.cryptography:void 0})),n={error:!1,operation:L.PNPublishFileOperation,category:o.PNAcknowledgmentCategory,statusCode:0},i=e=>{e&&this.logger.debug("PubNub",`Send file success. File shared with ${e.id} ID.`)};return s.process().then((e=>(n.statusCode=e.status,i(e),t?t(n,e):e))).catch((e=>{let s;throw e instanceof c?s=e.status:e instanceof q&&(s=e.toStatus(n.operation)),this.logger.error("PubNub",(()=>({messageType:"error",message:new c("File sending error. Check status for details",s)}))),t&&s&&t(s,null),new c("REST API request processing error. Check status for details",s)}))}}))}publishFile(e,t){return a(this,void 0,void 0,(function*(){{if(!this._configuration.PubNubFile)throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform.");this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Publish file message with parameters:"})));const s=new Qt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,crypto:this._configuration.getCryptoModule()})),n=e=>{e&&this.logger.debug("PubNub",`Publish file message success. File message published with timetoken: ${e.timetoken}`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}listFiles(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"List files with parameters:"})));const s=new es(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=e=>{e&&this.logger.debug("PubNub",`List files success. There are ${e.count} uploaded files.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}getFileUrl(e){var t;{const s=this.transport.request(new Xt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})).request()),n=null!==(t=s.queryParameters)&&void 0!==t?t:{},i=Object.keys(n).map((e=>{const t=n[e];return Array.isArray(t)?t.map((t=>`${e}=${k(t)}`)).join("&"):`${e}=${k(t)}`})).join("&");return`${s.origin}${s.path}?${i}`}}downloadFile(e,t){return a(this,void 0,void 0,(function*(){{if(!this._configuration.PubNubFile)throw new Error("Validation failed: 'PubNubFile' not configured or file upload not supported by the platform.");this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Download file with parameters:"})));const s=new wn(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet,PubNubFile:this._configuration.PubNubFile,cryptography:this.cryptography?this.cryptography:void 0,crypto:this._configuration.getCryptoModule()})),n=e=>{e&&this.logger.debug("PubNub","Download file success.")};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):yield this.sendRequest(s).then((e=>(n(e),e)))}}))}deleteFile(e,t){return a(this,void 0,void 0,(function*(){{this.logger.debug("PubNub",(()=>({messageType:"object",message:Object.assign({},e),details:"Delete file with parameters:"})));const s=new Zt(Object.assign(Object.assign({},e),{keySet:this._configuration.keySet})),n=t=>{t&&this.logger.debug("PubNub",`Delete file success. Deleted file with ${e.id} ID.`)};return t?this.sendRequest(s,((e,s)=>{n(s),t(e,s)})):this.sendRequest(s).then((e=>(n(e),e)))}}))}time(e){return a(this,void 0,void 0,(function*(){this.logger.debug("PubNub","Get service time.");const t=new jn,s=e=>{e&&this.logger.debug("PubNub",`Get service time success. Current timetoken: ${e.timetoken}`)};return e?this.sendRequest(t,((t,n)=>{s(n),e(t,n)})):this.sendRequest(t).then((e=>(s(e),e)))}))}emitStatus(e){var t;null===(t=this.eventDispatcher)||void 0===t||t.handleStatus(e)}emitEvent(e,t){var s;this._globalSubscriptionSet&&this._globalSubscriptionSet.handleEvent(e,t),null===(s=this.eventDispatcher)||void 0===s||s.handleEvent(t),Object.values(this.eventHandleCapable).forEach((s=>{s!==this._globalSubscriptionSet&&s.handleEvent(e,t)}))}set onStatus(e){this.eventDispatcher&&(this.eventDispatcher.onStatus=e)}set onMessage(e){this.eventDispatcher&&(this.eventDispatcher.onMessage=e)}set onPresence(e){this.eventDispatcher&&(this.eventDispatcher.onPresence=e)}set onSignal(e){this.eventDispatcher&&(this.eventDispatcher.onSignal=e)}set onObjects(e){this.eventDispatcher&&(this.eventDispatcher.onObjects=e)}set onMessageAction(e){this.eventDispatcher&&(this.eventDispatcher.onMessageAction=e)}set onFile(e){this.eventDispatcher&&(this.eventDispatcher.onFile=e)}set onDataSync(e){this.eventDispatcher&&(this.eventDispatcher.onDataSync=e)}addListener(e){this.eventDispatcher&&this.eventDispatcher.addListener(e)}removeListener(e){this.eventDispatcher&&this.eventDispatcher.removeListener(e)}removeAllListeners(){this.eventDispatcher&&this.eventDispatcher.removeAllListeners()}encrypt(e,t){return a(this,void 0,void 0,(function*(){this.logger.warn("PubNub","'encrypt' is deprecated. Use cryptoModule instead.");const s=this._configuration.getCryptoModule();if(!t&&s&&"string"==typeof e){const t=yield s.encrypt(e);return"string"==typeof t?t:g(t)}if(!this.crypto)throw new Error("Encryption error: cypher key not set");return this.crypto.encrypt(e,t)}))}decrypt(e,t){return a(this,void 0,void 0,(function*(){this.logger.warn("PubNub","'decrypt' is deprecated. Use cryptoModule instead.");const s=this._configuration.getCryptoModule();if(!t&&s){const t=yield s.decrypt(e);return t instanceof ArrayBuffer?JSON.parse((new TextDecoder).decode(t)):t}if(!this.crypto)throw new Error("Decryption error: cypher key not set");return this.crypto.decrypt(e,t)}))}encryptFile(e,t){return a(this,void 0,void 0,(function*(){var s;if("string"!=typeof e&&(t=e),!t)throw new Error("File encryption error. Source file is missing.");if(!this._configuration.PubNubFile)throw new Error("File encryption error. File constructor not configured.");if("string"!=typeof e&&!this._configuration.getCryptoModule())throw new Error("File encryption error. Crypto module not configured.");if("string"==typeof e){if(!this.cryptography)throw new Error("File encryption error. File encryption not available");return this.cryptography.encryptFile(e,t,this._configuration.PubNubFile)}return null===(s=this._configuration.getCryptoModule())||void 0===s?void 0:s.encryptFile(t,this._configuration.PubNubFile)}))}decryptFile(e,t){return a(this,void 0,void 0,(function*(){var s;if("string"!=typeof e&&(t=e),!t)throw new Error("File encryption error. Source file is missing.");if(!this._configuration.PubNubFile)throw new Error("File decryption error. File constructor not configured.");if("string"==typeof e&&!this._configuration.getCryptoModule())throw new Error("File decryption error. Crypto module not configured.");if("string"==typeof e){if(!this.cryptography)throw new Error("File decryption error. File decryption not available");return this.cryptography.decryptFile(e,t,this._configuration.PubNubFile)}return null===(s=this._configuration.getCryptoModule())||void 0===s?void 0:s.decryptFile(t,this._configuration.PubNubFile)}))}}kn.decoder=new TextDecoder,kn.OPERATIONS=L,kn.CATEGORIES=o,kn.Endpoint=J,kn.ExponentialRetryPolicy=Y.ExponentialRetryPolicy,kn.LinearRetryPolicy=Y.LinearRetryPolicy,kn.NoneRetryPolicy=Y.None,kn.LogLevel=W;class Pn{constructor(e,t){this.decode=e,this.base64ToBinary=t}decodeToken(e){let t="";e.length%4==3?t="=":e.length%4==2&&(t="==");const s=e.replace(/-/gi,"+").replace(/_/gi,"/")+t,n=this.decode(this.base64ToBinary(s));return"object"==typeof n?n:void 0}}class Cn extends kn{constructor(e){var t;const s=void 0!==e.subscriptionWorkerUrl,n=V(e),r=Object.assign(Object.assign({},n),{sdkFamily:"Web"});r.PubNubFile=d;const a=ie(r,(e=>{if(e.cipherKey){return new $({default:new A(Object.assign(Object.assign({},e),e.logger?{}:{logger:a.logger()})),cryptors:[new O({cipherKey:e.cipherKey}),new S({cipherKey:e.cipherKey})]})}}));let o,c;e.subscriptionWorkerLogVerbosity?e.subscriptionWorkerLogLevel=W.Debug:void 0===e.subscriptionWorkerLogLevel&&(e.subscriptionWorkerLogLevel=W.None),void 0!==e.subscriptionWorkerLogVerbosity&&a.logger().warn("Configuration","'subscriptionWorkerLogVerbosity' is deprecated. Use 'subscriptionWorkerLogLevel' instead."),a.getCryptoModule()&&(a.getCryptoModule().logger=a.logger()),o=new oe(new Pn((e=>H(i.decode(e))),p)),(a.getCipherKey()||a.secretKey)&&(c=new _({secretKey:a.secretKey,cipherKey:a.getCipherKey(),useRandomIVs:a.getUseRandomIVs(),customEncrypt:a.getCustomEncrypt(),customDecrypt:a.getCustomDecrypt(),logger:a.logger()},new v));let u,l=()=>{},h=()=>{},g=()=>{},b=()=>{};u=new U;let y=new he(a.logger(),r.transport);if(n.subscriptionWorkerUrl)try{const e=new K({clientIdentifier:a._instanceId,subscriptionKey:a.subscribeKey,userId:a.getUserId(),workerUrl:n.subscriptionWorkerUrl,sdkVersion:a.getVersion(),heartbeatInterval:a.getHeartbeatInterval(),announceSuccessfulHeartbeats:a.announceSuccessfulHeartbeats,announceFailedHeartbeats:a.announceFailedHeartbeats,workerOfflineClientsCheckInterval:r.subscriptionWorkerOfflineClientsCheckInterval,workerUnsubscribeOfflineClients:r.subscriptionWorkerUnsubscribeOfflineClients,workerLogLevel:r.subscriptionWorkerLogLevel,tokenManager:o,transport:y,logger:a.logger()});h=t=>e.onPresenceStateChange(t),l=t=>e.onHeartbeatIntervalChange(t),g=t=>e.onTokenChange(t),b=t=>e.onUserIdChange(t),y=e,n.subscriptionWorkerUnsubscribeOfflineClients&&"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("pagehide",(t=>{t.persisted||e.terminate()}),{once:!0})}catch(e){a.logger().error("PubNub",(()=>({messageType:"error",message:e})))}else s&&a.logger().warn("PubNub","SharedWorker not supported in this browser. Fallback to the original transport.");const m=new le({clientConfiguration:a,tokenManager:o,transport:y});if(super({configuration:a,transport:m,cryptography:u,tokenManager:o,crypto:c}),this.File=d,this.onHeartbeatIntervalChange=l,this.onAuthenticationChange=g,this.onPresenceStateChange=h,this.onUserIdChange=b,y instanceof K){y.emitStatus=this.emitStatus.bind(this);const e=this.disconnect.bind(this);this.disconnect=t=>{y.disconnect(),e()}}(null===(t=e.listenToBrowserNetworkEvents)||void 0===t||t)&&"undefined"!=typeof window&&window.addEventListener&&(window.addEventListener("offline",(()=>{this.networkDownDetected()})),window.addEventListener("online",(()=>{this.networkUpDetected()})))}networkDownDetected(){this.logger.debug("PubNub","Network down detected"),this.emitStatus({category:Cn.CATEGORIES.PNNetworkDownCategory}),this._configuration.restore?this.disconnect(!0):this.destroy(!0)}networkUpDetected(){this.logger.debug("PubNub","Network up detected"),this.emitStatus({category:Cn.CATEGORIES.PNNetworkUpCategory}),this.reconnect()}}return Cn.CryptoModule=$,Cn})); diff --git a/docs-snippets/basic-usage/miscellaneous.ts b/docs-snippets/basic-usage/miscellaneous.ts index 56ab7d566..3b32e7429 100644 --- a/docs-snippets/basic-usage/miscellaneous.ts +++ b/docs-snippets/basic-usage/miscellaneous.ts @@ -17,7 +17,7 @@ const msgContent = 'This is the data I wish to encrypt.'; console.log(`Original Message: ${msgContent}`); // Encrypt the message -const encryptedMessage = cryptoModule.encrypt(JSON.stringify(msgContent)); +const encryptedMessage = await cryptoModule.encrypt(JSON.stringify(msgContent)); console.log('message encrypted successfully'); // snippet.end @@ -34,7 +34,7 @@ const encryptedFile = await pubnub.encryptFile(file); const encrypted = '..'; // snippet.decryptBasicUsage -const decrypted = pubnub.decrypt(encrypted); // Pass the encrypted data as the first parameter in decrypt Method +const decrypted = await pubnub.decrypt(encrypted); // Pass the encrypted data as the first parameter in decrypt Method // snippet.end // snippet.decryptFileBasicUsage diff --git a/lib/core/components/cryptography/bytes.js b/lib/core/components/cryptography/bytes.js new file mode 100644 index 000000000..124645130 --- /dev/null +++ b/lib/core/components/cryptography/bytes.js @@ -0,0 +1,128 @@ +"use strict"; +/** + * Byte helpers shared by cryptors and crypto primitives. + * + * @internal + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decodeUtf8Strict = exports.isWellFormedUtf8 = exports.toArrayBuffer = exports.bytesToHex = void 0; +/** + * Lazily created UTF-8 decoder. + * + * **Note:** Not created at module load and doesn't use `fatal` option because some polyfills (for example + * `fast-text-encoding` used on React Native) throw when it is requested. Input is validated by + * {@link isWellFormedUtf8} instead, so the decoder only ever receives well-formed data. + */ +let decoder; +/** + * Whether {@link decoder} strips a leading BOM despite `ignoreBOM` option (detected once, on first use). + */ +let decoderStripsBOM = false; +/** + * Convert bytes to lowercase HEX string. + * + * @param bytes - Source bytes. + * + * @returns HEX encoded bytes. + * + * @internal + */ +const bytesToHex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); +exports.bytesToHex = bytesToHex; +/** + * Copy bytes into standalone {@link ArrayBuffer}. + * + * @param bytes - Source bytes (may be a view into a larger buffer). + * + * @returns {@link ArrayBuffer} which contains only source bytes. + * + * @internal + */ +const toArrayBuffer = (bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); +exports.toArrayBuffer = toArrayBuffer; +/** + * Check whether bytes are well-formed UTF-8. + * + * Implements Unicode "Well-Formed UTF-8 Byte Sequences" (Table 3-7): rejects stray continuation bytes, overlong + * forms (`C0`, `C1`, `E0 80-9F`, `F0 80-8F`), surrogates (`ED A0-BF`), code points above U+10FFFF (`F4 90-BF`, + * `F5-FF`) and truncated sequences. Same rules as CryptoJS `Utf8.stringify` (`decodeURIComponent`). + * + * @param bytes - Bytes which should be checked. + * + * @returns `true` if bytes are well-formed UTF-8. + * + * @internal + */ +const isWellFormedUtf8 = (bytes) => { + const length = bytes.length; + let index = 0; + while (index < length) { + const lead = bytes[index]; + if (lead < 0x80) { + index += 1; + continue; + } + let continuationBytes; + // Allowed range of the first continuation byte (narrower for some lead bytes). + let lower = 0x80; + let upper = 0xbf; + if (lead >= 0xc2 && lead <= 0xdf) + continuationBytes = 1; + else if (lead >= 0xe0 && lead <= 0xef) { + continuationBytes = 2; + if (lead === 0xe0) + lower = 0xa0; + else if (lead === 0xed) + upper = 0x9f; + } + else if (lead >= 0xf0 && lead <= 0xf4) { + continuationBytes = 3; + if (lead === 0xf0) + lower = 0x90; + else if (lead === 0xf4) + upper = 0x8f; + } + else + return false; + if (index + continuationBytes >= length) + return false; + const first = bytes[index + 1]; + if (first < lower || first > upper) + return false; + for (let offset = 2; offset <= continuationBytes; offset++) { + const next = bytes[index + offset]; + if (next < 0x80 || next > 0xbf) + return false; + } + index += continuationBytes + 1; + } + return true; +}; +exports.isWellFormedUtf8 = isWellFormedUtf8; +/** + * Strictly decode UTF-8 bytes. + * + * Behaves like CryptoJS `Utf8.stringify` used by previous SDK versions: malformed sequences are rejected instead of + * being replaced, and a leading BOM is preserved (regular decoders silently strip it). Result doesn't depend on the + * platform `TextDecoder` implementation or polyfill. + * + * @param bytes - UTF-8 encoded bytes. + * + * @returns Decoded string. + * + * @throws Error if bytes aren't a valid UTF-8 sequence. + * + * @internal + */ +const decodeUtf8Strict = (bytes) => { + if (!(0, exports.isWellFormedUtf8)(bytes)) + throw new Error('Malformed UTF-8 data'); + if (!decoder) { + decoder = new TextDecoder('utf-8', { ignoreBOM: true }); + decoderStripsBOM = decoder.decode(new Uint8Array([0xef, 0xbb, 0xbf])).length === 0; + } + const decoded = decoder.decode(bytes); + const hasBOM = bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf; + return hasBOM && decoderStripsBOM ? `\uFEFF${decoded}` : decoded; +}; +exports.decodeUtf8Strict = decodeUtf8Strict; diff --git a/lib/core/components/cryptography/hmac-sha256.js b/lib/core/components/cryptography/hmac-sha256.js deleted file mode 100644 index b22f461a1..000000000 --- a/lib/core/components/cryptography/hmac-sha256.js +++ /dev/null @@ -1,692 +0,0 @@ -"use strict"; -/** - * CryptoJS implementation. - * - * @internal - */ -/*eslint-disable */ -/* - CryptoJS v3.1.2 - code.google.com/p/crypto-js - (c) 2009-2013 by Jeff Mott. All rights reserved. - code.google.com/p/crypto-js/wiki/License - */ -var CryptoJS = CryptoJS || - (function (h, s) { - var f = {}, g = (f.lib = {}), q = function () { }, m = (g.Base = { - extend: function (a) { - q.prototype = this; - var c = new q(); - a && c.mixIn(a); - c.hasOwnProperty('init') || - (c.init = function () { - c.$super.init.apply(this, arguments); - }); - c.init.prototype = c; - c.$super = this; - return c; - }, - create: function () { - var a = this.extend(); - a.init.apply(a, arguments); - return a; - }, - init: function () { }, - mixIn: function (a) { - for (var c in a) - a.hasOwnProperty(c) && (this[c] = a[c]); - a.hasOwnProperty('toString') && (this.toString = a.toString); - }, - clone: function () { - return this.init.prototype.extend(this); - }, - }), r = (g.WordArray = m.extend({ - init: function (a, c) { - a = this.words = a || []; - this.sigBytes = c != s ? c : 4 * a.length; - }, - toString: function (a) { - return (a || k).stringify(this); - }, - concat: function (a) { - var c = this.words, d = a.words, b = this.sigBytes; - a = a.sigBytes; - this.clamp(); - if (b % 4) - for (var e = 0; e < a; e++) - c[(b + e) >>> 2] |= ((d[e >>> 2] >>> (24 - 8 * (e % 4))) & 255) << (24 - 8 * ((b + e) % 4)); - else if (65535 < d.length) - for (e = 0; e < a; e += 4) - c[(b + e) >>> 2] = d[e >>> 2]; - else - c.push.apply(c, d); - this.sigBytes += a; - return this; - }, - clamp: function () { - var a = this.words, c = this.sigBytes; - a[c >>> 2] &= 4294967295 << (32 - 8 * (c % 4)); - a.length = h.ceil(c / 4); - }, - clone: function () { - var a = m.clone.call(this); - a.words = this.words.slice(0); - return a; - }, - random: function (a) { - for (var c = [], d = 0; d < a; d += 4) - c.push((4294967296 * h.random()) | 0); - return new r.init(c, a); - }, - })), l = (f.enc = {}), k = (l.Hex = { - stringify: function (a) { - var c = a.words; - a = a.sigBytes; - for (var d = [], b = 0; b < a; b++) { - var e = (c[b >>> 2] >>> (24 - 8 * (b % 4))) & 255; - d.push((e >>> 4).toString(16)); - d.push((e & 15).toString(16)); - } - return d.join(''); - }, - parse: function (a) { - for (var c = a.length, d = [], b = 0; b < c; b += 2) - d[b >>> 3] |= parseInt(a.substr(b, 2), 16) << (24 - 4 * (b % 8)); - return new r.init(d, c / 2); - }, - }), n = (l.Latin1 = { - stringify: function (a) { - var c = a.words; - a = a.sigBytes; - for (var d = [], b = 0; b < a; b++) - d.push(String.fromCharCode((c[b >>> 2] >>> (24 - 8 * (b % 4))) & 255)); - return d.join(''); - }, - parse: function (a) { - for (var c = a.length, d = [], b = 0; b < c; b++) - d[b >>> 2] |= (a.charCodeAt(b) & 255) << (24 - 8 * (b % 4)); - return new r.init(d, c); - }, - }), j = (l.Utf8 = { - stringify: function (a) { - try { - return decodeURIComponent(escape(n.stringify(a))); - } - catch (c) { - throw Error('Malformed UTF-8 data'); - } - }, - parse: function (a) { - return n.parse(unescape(encodeURIComponent(a))); - }, - }), u = (g.BufferedBlockAlgorithm = m.extend({ - reset: function () { - this._data = new r.init(); - this._nDataBytes = 0; - }, - _append: function (a) { - 'string' == typeof a && (a = j.parse(a)); - this._data.concat(a); - this._nDataBytes += a.sigBytes; - }, - _process: function (a) { - var c = this._data, d = c.words, b = c.sigBytes, e = this.blockSize, f = b / (4 * e), f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0); - a = f * e; - b = h.min(4 * a, b); - if (a) { - for (var g = 0; g < a; g += e) - this._doProcessBlock(d, g); - g = d.splice(0, a); - c.sigBytes -= b; - } - return new r.init(g, b); - }, - clone: function () { - var a = m.clone.call(this); - a._data = this._data.clone(); - return a; - }, - _minBufferSize: 0, - })); - g.Hasher = u.extend({ - cfg: m.extend(), - init: function (a) { - this.cfg = this.cfg.extend(a); - this.reset(); - }, - reset: function () { - u.reset.call(this); - this._doReset(); - }, - update: function (a) { - this._append(a); - this._process(); - return this; - }, - finalize: function (a) { - a && this._append(a); - return this._doFinalize(); - }, - blockSize: 16, - _createHelper: function (a) { - return function (c, d) { - return new a.init(d).finalize(c); - }; - }, - _createHmacHelper: function (a) { - return function (c, d) { - return new t.HMAC.init(a, d).finalize(c); - }; - }, - }); - var t = (f.algo = {}); - return f; - })(Math); -// SHA256 -(function (h) { - for (var s = CryptoJS, f = s.lib, g = f.WordArray, q = f.Hasher, f = s.algo, m = [], r = [], l = function (a) { - return (4294967296 * (a - (a | 0))) | 0; - }, k = 2, n = 0; 64 > n;) { - var j; - a: { - j = k; - for (var u = h.sqrt(j), t = 2; t <= u; t++) - if (!(j % t)) { - j = !1; - break a; - } - j = !0; - } - j && (8 > n && (m[n] = l(h.pow(k, 0.5))), (r[n] = l(h.pow(k, 1 / 3))), n++); - k++; - } - var a = [], f = (f.SHA256 = q.extend({ - _doReset: function () { - this._hash = new g.init(m.slice(0)); - }, - _doProcessBlock: function (c, d) { - for (var b = this._hash.words, e = b[0], f = b[1], g = b[2], j = b[3], h = b[4], m = b[5], n = b[6], q = b[7], p = 0; 64 > p; p++) { - if (16 > p) - a[p] = c[d + p] | 0; - else { - var k = a[p - 15], l = a[p - 2]; - a[p] = - (((k << 25) | (k >>> 7)) ^ ((k << 14) | (k >>> 18)) ^ (k >>> 3)) + - a[p - 7] + - (((l << 15) | (l >>> 17)) ^ ((l << 13) | (l >>> 19)) ^ (l >>> 10)) + - a[p - 16]; - } - k = - q + - (((h << 26) | (h >>> 6)) ^ ((h << 21) | (h >>> 11)) ^ ((h << 7) | (h >>> 25))) + - ((h & m) ^ (~h & n)) + - r[p] + - a[p]; - l = - (((e << 30) | (e >>> 2)) ^ ((e << 19) | (e >>> 13)) ^ ((e << 10) | (e >>> 22))) + - ((e & f) ^ (e & g) ^ (f & g)); - q = n; - n = m; - m = h; - h = (j + k) | 0; - j = g; - g = f; - f = e; - e = (k + l) | 0; - } - b[0] = (b[0] + e) | 0; - b[1] = (b[1] + f) | 0; - b[2] = (b[2] + g) | 0; - b[3] = (b[3] + j) | 0; - b[4] = (b[4] + h) | 0; - b[5] = (b[5] + m) | 0; - b[6] = (b[6] + n) | 0; - b[7] = (b[7] + q) | 0; - }, - _doFinalize: function () { - var a = this._data, d = a.words, b = 8 * this._nDataBytes, e = 8 * a.sigBytes; - d[e >>> 5] |= 128 << (24 - (e % 32)); - d[(((e + 64) >>> 9) << 4) + 14] = h.floor(b / 4294967296); - d[(((e + 64) >>> 9) << 4) + 15] = b; - a.sigBytes = 4 * d.length; - this._process(); - return this._hash; - }, - clone: function () { - var a = q.clone.call(this); - a._hash = this._hash.clone(); - return a; - }, - })); - s.SHA256 = q._createHelper(f); - s.HmacSHA256 = q._createHmacHelper(f); -})(Math); -// HMAC SHA256 -(function () { - var h = CryptoJS, s = h.enc.Utf8; - h.algo.HMAC = h.lib.Base.extend({ - init: function (f, g) { - f = this._hasher = new f.init(); - 'string' == typeof g && (g = s.parse(g)); - var h = f.blockSize, m = 4 * h; - g.sigBytes > m && (g = f.finalize(g)); - g.clamp(); - for (var r = (this._oKey = g.clone()), l = (this._iKey = g.clone()), k = r.words, n = l.words, j = 0; j < h; j++) - (k[j] ^= 1549556828), (n[j] ^= 909522486); - r.sigBytes = l.sigBytes = m; - this.reset(); - }, - reset: function () { - var f = this._hasher; - f.reset(); - f.update(this._iKey); - }, - update: function (f) { - this._hasher.update(f); - return this; - }, - finalize: function (f) { - var g = this._hasher; - f = g.finalize(f); - g.reset(); - return g.finalize(this._oKey.clone().concat(f)); - }, - }); -})(); -// Base64 -(function () { - var u = CryptoJS, p = u.lib.WordArray; - u.enc.Base64 = { - stringify: function (d) { - var l = d.words, p = d.sigBytes, t = this._map; - d.clamp(); - d = []; - for (var r = 0; r < p; r += 3) - for (var w = (((l[r >>> 2] >>> (24 - 8 * (r % 4))) & 255) << 16) | - (((l[(r + 1) >>> 2] >>> (24 - 8 * ((r + 1) % 4))) & 255) << 8) | - ((l[(r + 2) >>> 2] >>> (24 - 8 * ((r + 2) % 4))) & 255), v = 0; 4 > v && r + 0.75 * v < p; v++) - d.push(t.charAt((w >>> (6 * (3 - v))) & 63)); - if ((l = t.charAt(64))) - for (; d.length % 4;) - d.push(l); - return d.join(''); - }, - parse: function (d) { - var l = d.length, s = this._map, t = s.charAt(64); - t && ((t = d.indexOf(t)), -1 != t && (l = t)); - for (var t = [], r = 0, w = 0; w < l; w++) - if (w % 4) { - var v = s.indexOf(d.charAt(w - 1)) << (2 * (w % 4)), b = s.indexOf(d.charAt(w)) >>> (6 - 2 * (w % 4)); - t[r >>> 2] |= (v | b) << (24 - 8 * (r % 4)); - r++; - } - return p.create(t, r); - }, - _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', - }; -})(); -// BlockCipher -(function (u) { - function p(b, n, a, c, e, j, k) { - b = b + ((n & a) | (~n & c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function d(b, n, a, c, e, j, k) { - b = b + ((n & c) | (a & ~c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function l(b, n, a, c, e, j, k) { - b = b + (n ^ a ^ c) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function s(b, n, a, c, e, j, k) { - b = b + (a ^ (n | ~c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - for (var t = CryptoJS, r = t.lib, w = r.WordArray, v = r.Hasher, r = t.algo, b = [], x = 0; 64 > x; x++) - b[x] = (4294967296 * u.abs(u.sin(x + 1))) | 0; - r = r.MD5 = v.extend({ - _doReset: function () { - this._hash = new w.init([1732584193, 4023233417, 2562383102, 271733878]); - }, - _doProcessBlock: function (q, n) { - for (var a = 0; 16 > a; a++) { - var c = n + a, e = q[c]; - q[c] = (((e << 8) | (e >>> 24)) & 16711935) | (((e << 24) | (e >>> 8)) & 4278255360); - } - var a = this._hash.words, c = q[n + 0], e = q[n + 1], j = q[n + 2], k = q[n + 3], z = q[n + 4], r = q[n + 5], t = q[n + 6], w = q[n + 7], v = q[n + 8], A = q[n + 9], B = q[n + 10], C = q[n + 11], u = q[n + 12], D = q[n + 13], E = q[n + 14], x = q[n + 15], f = a[0], m = a[1], g = a[2], h = a[3], f = p(f, m, g, h, c, 7, b[0]), h = p(h, f, m, g, e, 12, b[1]), g = p(g, h, f, m, j, 17, b[2]), m = p(m, g, h, f, k, 22, b[3]), f = p(f, m, g, h, z, 7, b[4]), h = p(h, f, m, g, r, 12, b[5]), g = p(g, h, f, m, t, 17, b[6]), m = p(m, g, h, f, w, 22, b[7]), f = p(f, m, g, h, v, 7, b[8]), h = p(h, f, m, g, A, 12, b[9]), g = p(g, h, f, m, B, 17, b[10]), m = p(m, g, h, f, C, 22, b[11]), f = p(f, m, g, h, u, 7, b[12]), h = p(h, f, m, g, D, 12, b[13]), g = p(g, h, f, m, E, 17, b[14]), m = p(m, g, h, f, x, 22, b[15]), f = d(f, m, g, h, e, 5, b[16]), h = d(h, f, m, g, t, 9, b[17]), g = d(g, h, f, m, C, 14, b[18]), m = d(m, g, h, f, c, 20, b[19]), f = d(f, m, g, h, r, 5, b[20]), h = d(h, f, m, g, B, 9, b[21]), g = d(g, h, f, m, x, 14, b[22]), m = d(m, g, h, f, z, 20, b[23]), f = d(f, m, g, h, A, 5, b[24]), h = d(h, f, m, g, E, 9, b[25]), g = d(g, h, f, m, k, 14, b[26]), m = d(m, g, h, f, v, 20, b[27]), f = d(f, m, g, h, D, 5, b[28]), h = d(h, f, m, g, j, 9, b[29]), g = d(g, h, f, m, w, 14, b[30]), m = d(m, g, h, f, u, 20, b[31]), f = l(f, m, g, h, r, 4, b[32]), h = l(h, f, m, g, v, 11, b[33]), g = l(g, h, f, m, C, 16, b[34]), m = l(m, g, h, f, E, 23, b[35]), f = l(f, m, g, h, e, 4, b[36]), h = l(h, f, m, g, z, 11, b[37]), g = l(g, h, f, m, w, 16, b[38]), m = l(m, g, h, f, B, 23, b[39]), f = l(f, m, g, h, D, 4, b[40]), h = l(h, f, m, g, c, 11, b[41]), g = l(g, h, f, m, k, 16, b[42]), m = l(m, g, h, f, t, 23, b[43]), f = l(f, m, g, h, A, 4, b[44]), h = l(h, f, m, g, u, 11, b[45]), g = l(g, h, f, m, x, 16, b[46]), m = l(m, g, h, f, j, 23, b[47]), f = s(f, m, g, h, c, 6, b[48]), h = s(h, f, m, g, w, 10, b[49]), g = s(g, h, f, m, E, 15, b[50]), m = s(m, g, h, f, r, 21, b[51]), f = s(f, m, g, h, u, 6, b[52]), h = s(h, f, m, g, k, 10, b[53]), g = s(g, h, f, m, B, 15, b[54]), m = s(m, g, h, f, e, 21, b[55]), f = s(f, m, g, h, v, 6, b[56]), h = s(h, f, m, g, x, 10, b[57]), g = s(g, h, f, m, t, 15, b[58]), m = s(m, g, h, f, D, 21, b[59]), f = s(f, m, g, h, z, 6, b[60]), h = s(h, f, m, g, C, 10, b[61]), g = s(g, h, f, m, j, 15, b[62]), m = s(m, g, h, f, A, 21, b[63]); - a[0] = (a[0] + f) | 0; - a[1] = (a[1] + m) | 0; - a[2] = (a[2] + g) | 0; - a[3] = (a[3] + h) | 0; - }, - _doFinalize: function () { - var b = this._data, n = b.words, a = 8 * this._nDataBytes, c = 8 * b.sigBytes; - n[c >>> 5] |= 128 << (24 - (c % 32)); - var e = u.floor(a / 4294967296); - n[(((c + 64) >>> 9) << 4) + 15] = (((e << 8) | (e >>> 24)) & 16711935) | (((e << 24) | (e >>> 8)) & 4278255360); - n[(((c + 64) >>> 9) << 4) + 14] = (((a << 8) | (a >>> 24)) & 16711935) | (((a << 24) | (a >>> 8)) & 4278255360); - b.sigBytes = 4 * (n.length + 1); - this._process(); - b = this._hash; - n = b.words; - for (a = 0; 4 > a; a++) - (c = n[a]), (n[a] = (((c << 8) | (c >>> 24)) & 16711935) | (((c << 24) | (c >>> 8)) & 4278255360)); - return b; - }, - clone: function () { - var b = v.clone.call(this); - b._hash = this._hash.clone(); - return b; - }, - }); - t.MD5 = v._createHelper(r); - t.HmacMD5 = v._createHmacHelper(r); -})(Math); -(function () { - var u = CryptoJS, p = u.lib, d = p.Base, l = p.WordArray, p = u.algo, s = (p.EvpKDF = d.extend({ - cfg: d.extend({ keySize: 4, hasher: p.MD5, iterations: 1 }), - init: function (d) { - this.cfg = this.cfg.extend(d); - }, - compute: function (d, r) { - for (var p = this.cfg, s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; u.length < q;) { - n && s.update(n); - var n = s.update(d).finalize(r); - s.reset(); - for (var a = 1; a < p; a++) - (n = s.finalize(n)), s.reset(); - b.concat(n); - } - b.sigBytes = 4 * q; - return b; - }, - })); - u.EvpKDF = function (d, l, p) { - return s.create(p).compute(d, l); - }; -})(); -// Cipher -CryptoJS.lib.Cipher || - (function (u) { - var p = CryptoJS, d = p.lib, l = d.Base, s = d.WordArray, t = d.BufferedBlockAlgorithm, r = p.enc.Base64, w = p.algo.EvpKDF, v = (d.Cipher = t.extend({ - cfg: l.extend(), - createEncryptor: function (e, a) { - return this.create(this._ENC_XFORM_MODE, e, a); - }, - createDecryptor: function (e, a) { - return this.create(this._DEC_XFORM_MODE, e, a); - }, - init: function (e, a, b) { - this.cfg = this.cfg.extend(b); - this._xformMode = e; - this._key = a; - this.reset(); - }, - reset: function () { - t.reset.call(this); - this._doReset(); - }, - process: function (e) { - this._append(e); - return this._process(); - }, - finalize: function (e) { - e && this._append(e); - return this._doFinalize(); - }, - keySize: 4, - ivSize: 4, - _ENC_XFORM_MODE: 1, - _DEC_XFORM_MODE: 2, - _createHelper: function (e) { - return { - encrypt: function (b, k, d) { - return ('string' == typeof k ? c : a).encrypt(e, b, k, d); - }, - decrypt: function (b, k, d) { - return ('string' == typeof k ? c : a).decrypt(e, b, k, d); - }, - }; - }, - })); - d.StreamCipher = v.extend({ - _doFinalize: function () { - return this._process(!0); - }, - blockSize: 1, - }); - var b = (p.mode = {}), x = function (e, a, b) { - var c = this._iv; - c ? (this._iv = u) : (c = this._prevBlock); - for (var d = 0; d < b; d++) - e[a + d] ^= c[d]; - }, q = (d.BlockCipherMode = l.extend({ - createEncryptor: function (e, a) { - return this.Encryptor.create(e, a); - }, - createDecryptor: function (e, a) { - return this.Decryptor.create(e, a); - }, - init: function (e, a) { - this._cipher = e; - this._iv = a; - }, - })).extend(); - q.Encryptor = q.extend({ - processBlock: function (e, a) { - var b = this._cipher, c = b.blockSize; - x.call(this, e, a, c); - b.encryptBlock(e, a); - this._prevBlock = e.slice(a, a + c); - }, - }); - q.Decryptor = q.extend({ - processBlock: function (e, a) { - var b = this._cipher, c = b.blockSize, d = e.slice(a, a + c); - b.decryptBlock(e, a); - x.call(this, e, a, c); - this._prevBlock = d; - }, - }); - b = b.CBC = q; - q = (p.pad = {}).Pkcs7 = { - pad: function (a, b) { - for (var c = 4 * b, c = c - (a.sigBytes % c), d = (c << 24) | (c << 16) | (c << 8) | c, l = [], n = 0; n < c; n += 4) - l.push(d); - c = s.create(l, c); - a.concat(c); - }, - unpad: function (a) { - a.sigBytes -= a.words[(a.sigBytes - 1) >>> 2] & 255; - }, - }; - d.BlockCipher = v.extend({ - cfg: v.cfg.extend({ mode: b, padding: q }), - reset: function () { - v.reset.call(this); - var a = this.cfg, b = a.iv, a = a.mode; - if (this._xformMode == this._ENC_XFORM_MODE) - var c = a.createEncryptor; - else - (c = a.createDecryptor), (this._minBufferSize = 1); - this._mode = c.call(a, this, b && b.words); - }, - _doProcessBlock: function (a, b) { - this._mode.processBlock(a, b); - }, - _doFinalize: function () { - var a = this.cfg.padding; - if (this._xformMode == this._ENC_XFORM_MODE) { - a.pad(this._data, this.blockSize); - var b = this._process(!0); - } - else - (b = this._process(!0)), a.unpad(b); - return b; - }, - blockSize: 4, - }); - var n = (d.CipherParams = l.extend({ - init: function (a) { - this.mixIn(a); - }, - toString: function (a) { - return (a || this.formatter).stringify(this); - }, - })), b = ((p.format = {}).OpenSSL = { - stringify: function (a) { - var b = a.ciphertext; - a = a.salt; - return (a ? s.create([1398893684, 1701076831]).concat(a).concat(b) : b).toString(r); - }, - parse: function (a) { - a = r.parse(a); - var b = a.words; - if (1398893684 == b[0] && 1701076831 == b[1]) { - var c = s.create(b.slice(2, 4)); - b.splice(0, 4); - a.sigBytes -= 16; - } - return n.create({ ciphertext: a, salt: c }); - }, - }), a = (d.SerializableCipher = l.extend({ - cfg: l.extend({ format: b }), - encrypt: function (a, b, c, d) { - d = this.cfg.extend(d); - var l = a.createEncryptor(c, d); - b = l.finalize(b); - l = l.cfg; - return n.create({ - ciphertext: b, - key: c, - iv: l.iv, - algorithm: a, - mode: l.mode, - padding: l.padding, - blockSize: a.blockSize, - formatter: d.format, - }); - }, - decrypt: function (a, b, c, d) { - d = this.cfg.extend(d); - b = this._parse(b, d.format); - return a.createDecryptor(c, d).finalize(b.ciphertext); - }, - _parse: function (a, b) { - return 'string' == typeof a ? b.parse(a, this) : a; - }, - })), p = ((p.kdf = {}).OpenSSL = { - execute: function (a, b, c, d) { - d || (d = s.random(8)); - a = w.create({ keySize: b + c }).compute(a, d); - c = s.create(a.words.slice(b), 4 * c); - a.sigBytes = 4 * b; - return n.create({ key: a, iv: c, salt: d }); - }, - }), c = (d.PasswordBasedCipher = a.extend({ - cfg: a.cfg.extend({ kdf: p }), - encrypt: function (b, c, d, l) { - l = this.cfg.extend(l); - d = l.kdf.execute(d, b.keySize, b.ivSize); - l.iv = d.iv; - b = a.encrypt.call(this, b, c, d.key, l); - b.mixIn(d); - return b; - }, - decrypt: function (b, c, d, l) { - l = this.cfg.extend(l); - c = this._parse(c, l.format); - d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt); - l.iv = d.iv; - return a.decrypt.call(this, b, c, d.key, l); - }, - })); - })(); -// AES -(function () { - for (var u = CryptoJS, p = u.lib.BlockCipher, d = u.algo, l = [], s = [], t = [], r = [], w = [], v = [], b = [], x = [], q = [], n = [], a = [], c = 0; 256 > c; c++) - a[c] = 128 > c ? c << 1 : (c << 1) ^ 283; - for (var e = 0, j = 0, c = 0; 256 > c; c++) { - var k = j ^ (j << 1) ^ (j << 2) ^ (j << 3) ^ (j << 4), k = (k >>> 8) ^ (k & 255) ^ 99; - l[e] = k; - s[k] = e; - var z = a[e], F = a[z], G = a[F], y = (257 * a[k]) ^ (16843008 * k); - t[e] = (y << 24) | (y >>> 8); - r[e] = (y << 16) | (y >>> 16); - w[e] = (y << 8) | (y >>> 24); - v[e] = y; - y = (16843009 * G) ^ (65537 * F) ^ (257 * z) ^ (16843008 * e); - b[k] = (y << 24) | (y >>> 8); - x[k] = (y << 16) | (y >>> 16); - q[k] = (y << 8) | (y >>> 24); - n[k] = y; - e ? ((e = z ^ a[a[a[G ^ z]]]), (j ^= a[a[j]])) : (e = j = 1); - } - var H = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54], d = (d.AES = p.extend({ - _doReset: function () { - for (var a = this._key, c = a.words, d = a.sigBytes / 4, a = 4 * ((this._nRounds = d + 6) + 1), e = (this._keySchedule = []), j = 0; j < a; j++) - if (j < d) - e[j] = c[j]; - else { - var k = e[j - 1]; - j % d - ? 6 < d && - 4 == j % d && - (k = (l[k >>> 24] << 24) | (l[(k >>> 16) & 255] << 16) | (l[(k >>> 8) & 255] << 8) | l[k & 255]) - : ((k = (k << 8) | (k >>> 24)), - (k = (l[k >>> 24] << 24) | (l[(k >>> 16) & 255] << 16) | (l[(k >>> 8) & 255] << 8) | l[k & 255]), - (k ^= H[(j / d) | 0] << 24)); - e[j] = e[j - d] ^ k; - } - c = this._invKeySchedule = []; - for (d = 0; d < a; d++) - (j = a - d), - (k = d % 4 ? e[j] : e[j - 4]), - (c[d] = - 4 > d || 4 >= j ? k : b[l[k >>> 24]] ^ x[l[(k >>> 16) & 255]] ^ q[l[(k >>> 8) & 255]] ^ n[l[k & 255]]); - }, - encryptBlock: function (a, b) { - this._doCryptBlock(a, b, this._keySchedule, t, r, w, v, l); - }, - decryptBlock: function (a, c) { - var d = a[c + 1]; - a[c + 1] = a[c + 3]; - a[c + 3] = d; - this._doCryptBlock(a, c, this._invKeySchedule, b, x, q, n, s); - d = a[c + 1]; - a[c + 1] = a[c + 3]; - a[c + 3] = d; - }, - _doCryptBlock: function (a, b, c, d, e, j, l, f) { - for (var m = this._nRounds, g = a[b] ^ c[0], h = a[b + 1] ^ c[1], k = a[b + 2] ^ c[2], n = a[b + 3] ^ c[3], p = 4, r = 1; r < m; r++) - var q = d[g >>> 24] ^ e[(h >>> 16) & 255] ^ j[(k >>> 8) & 255] ^ l[n & 255] ^ c[p++], s = d[h >>> 24] ^ e[(k >>> 16) & 255] ^ j[(n >>> 8) & 255] ^ l[g & 255] ^ c[p++], t = d[k >>> 24] ^ e[(n >>> 16) & 255] ^ j[(g >>> 8) & 255] ^ l[h & 255] ^ c[p++], n = d[n >>> 24] ^ e[(g >>> 16) & 255] ^ j[(h >>> 8) & 255] ^ l[k & 255] ^ c[p++], g = q, h = s, k = t; - q = ((f[g >>> 24] << 24) | (f[(h >>> 16) & 255] << 16) | (f[(k >>> 8) & 255] << 8) | f[n & 255]) ^ c[p++]; - s = ((f[h >>> 24] << 24) | (f[(k >>> 16) & 255] << 16) | (f[(n >>> 8) & 255] << 8) | f[g & 255]) ^ c[p++]; - t = ((f[k >>> 24] << 24) | (f[(n >>> 16) & 255] << 16) | (f[(g >>> 8) & 255] << 8) | f[h & 255]) ^ c[p++]; - n = ((f[n >>> 24] << 24) | (f[(g >>> 16) & 255] << 16) | (f[(h >>> 8) & 255] << 8) | f[k & 255]) ^ c[p++]; - a[b] = q; - a[b + 1] = s; - a[b + 2] = t; - a[b + 3] = n; - }, - keySize: 8, - })); - u.AES = p._createHelper(d); -})(); -// Mode ECB -CryptoJS.mode.ECB = (function () { - var ECB = CryptoJS.lib.BlockCipherMode.extend(); - ECB.Encryptor = ECB.extend({ - processBlock: function (words, offset) { - this._cipher.encryptBlock(words, offset); - }, - }); - ECB.Decryptor = ECB.extend({ - processBlock: function (words, offset) { - this._cipher.decryptBlock(words, offset); - }, - }); - return ECB; -})(); -module.exports = CryptoJS; diff --git a/lib/core/components/cryptography/index.js b/lib/core/components/cryptography/index.js index 016228ca1..24af0e4c2 100644 --- a/lib/core/components/cryptography/index.js +++ b/lib/core/components/cryptography/index.js @@ -4,63 +4,34 @@ * * @internal */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); }; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../../utils"); +const bytes_1 = require("./bytes"); const base64_codec_1 = require("../base64_codec"); -const hmac_sha256_1 = __importDefault(require("./hmac-sha256")); /** - * Convert bytes array to words array. + * Legacy cryptography module for data encryption / decryption. * - * @param b - Bytes array (buffer) which should be converted. - * - * @returns Word sized array. + * Wire format (compatible with all PubNub SDKs): + * - key: first 32 characters of HEX encoded SHA-256 digest of the cipher key, used as UTF-8 bytes (AES-256), + * - cipher: AES-CBC with PKCS#7 padding, + * - random IV: `base64(iv + ciphertext)`, static IV (`0123456789012345`): `base64(ciphertext)`. * * @internal */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -function bufferToWordArray(b) { - const wa = []; - let i; - for (i = 0; i < b.length; i += 1) { - wa[(i / 4) | 0] |= b[i] << (24 - 8 * i); - } - // @ts-expect-error Bundled library without types. - return hmac_sha256_1.default.lib.WordArray.create(wa, b.length); -} -/** - * Legacy cryptography module for files and signature. - * - * @internal - */ -class default_1 { - constructor(configuration) { +class LegacyCrypto { + constructor(configuration, primitives) { this.configuration = configuration; - /** - * Crypto initialization vector. - */ - this.iv = '0123456789012345'; - /** - * List os allowed cipher key encodings. - */ - this.allowedKeyEncodings = ['hex', 'utf8', 'base64', 'binary']; - /** - * Allowed cipher key lengths. - */ - this.allowedKeyLengths = [128, 256]; - /** - * Allowed crypto modes. - */ - this.allowedModes = ['ecb', 'cbc']; + this.primitives = primitives; this.logger = configuration.logger; - this.defaultOptions = { - encryptKey: true, - keyEncoding: 'utf8', - keyLength: 256, - mode: 'cbc', - }; } /** * Update registered loggers' manager. @@ -88,251 +59,163 @@ class default_1 { get logger() { return this._logger; } - /** - * Generate HMAC-SHA256 hash from input data. - * - * @param data - Data from which hash should be generated. - * - * @returns HMAC-SHA256 hash from provided `data`. - */ - HMACSHA256(data) { - // @ts-expect-error Bundled library without types. - const hash = hmac_sha256_1.default.HmacSHA256(data, this.configuration.secretKey); - // @ts-expect-error Bundled library without types. - return hash.toString(hmac_sha256_1.default.enc.Base64); - } - /** - * Generate SHA256 hash from input data. - * - * @param data - Data from which hash should be generated. - * - * @returns SHA256 hash from provided `data`. - */ - SHA256(data) { - // @ts-expect-error Bundled library without types. - return hmac_sha256_1.default.SHA256(data).toString(hmac_sha256_1.default.enc.Hex); - } /** * Encrypt provided data. * * @param data - Source data which should be encrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Encrypted `data`. */ - encrypt(data, customCipherKey, options) { - if (this.configuration.customEncrypt) { - if (this.logger) - this.logger.warn('Crypto', "'customEncrypt' is deprecated. Consult docs for better alternative."); - return this.configuration.customEncrypt(data); - } - return this.pnEncrypt(data, customCipherKey, options); + encrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + if (this.configuration.customEncrypt) { + if (this.logger) + this.logger.warn('Crypto', "'customEncrypt' is deprecated. Consult docs for better alternative."); + return this.configuration.customEncrypt(data); + } + return this.pnEncrypt(typeof data === 'string' ? data : JSON.stringify(data), customCipherKey); + }); } /** * Decrypt provided data. * * @param data - Encrypted data which should be decrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Decrypted `data`. */ - decrypt(data, customCipherKey, options) { - if (this.configuration.customDecrypt) { - if (this.logger) - this.logger.warn('Crypto', "'customDecrypt' is deprecated. Consult docs for better alternative."); - return this.configuration.customDecrypt(data); - } - return this.pnDecrypt(data, customCipherKey, options); + decrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + if (this.configuration.customDecrypt) { + if (this.logger) + this.logger.warn('Crypto', "'customDecrypt' is deprecated. Consult docs for better alternative."); + return this.configuration.customDecrypt(data); + } + return this.pnDecrypt(data, customCipherKey); + }); } /** * Encrypt provided data. * * @param data - Source data which should be encrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Encrypted `data` as string. */ - pnEncrypt(data, customCipherKey, options) { - const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; - if (!decidedCipherKey) - return data; - if (this.logger) { - this.logger.debug('Crypto', () => ({ - messageType: 'object', - message: Object.assign({ data }, (options !== null && options !== void 0 ? options : {})), - details: 'Encrypt with parameters:', - ignoredKeys: utils_1.isSensitiveLogKey, - })); - } - options = this.parseOptions(options); - const mode = this.getMode(options); - const cipherKey = this.getPaddedKey(decidedCipherKey, options); - if (this.configuration.useRandomIVs) { - const waIv = this.getRandomIV(); - // @ts-expect-error Bundled library without types. - const waPayload = hmac_sha256_1.default.AES.encrypt(data, cipherKey, { iv: waIv, mode }).ciphertext; - // @ts-expect-error Bundled library without types. - return waIv.clone().concat(waPayload.clone()).toString(hmac_sha256_1.default.enc.Base64); - } - const iv = this.getIV(options); - // @ts-expect-error Bundled library without types. - const encryptedHexArray = hmac_sha256_1.default.AES.encrypt(data, cipherKey, { iv, mode }).ciphertext; - // @ts-expect-error Bundled library without types. - const base64Encrypted = encryptedHexArray.toString(hmac_sha256_1.default.enc.Base64); - return base64Encrypted || data; + pnEncrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; + if (!decidedCipherKey) + return data; + if (this.logger) { + this.logger.debug('Crypto', () => ({ + messageType: 'object', + message: { data }, + details: 'Encrypt with parameters:', + ignoredKeys: utils_1.isSensitiveLogKey, + })); + } + const key = yield this.getKey(decidedCipherKey); + const plaintext = LegacyCrypto.encoder.encode(data); + if (this.configuration.useRandomIVs) { + const iv = this.primitives.randomBytes(LegacyCrypto.IV_LENGTH); + const ciphertext = yield this.primitives.aesCbcEncrypt(key, iv, plaintext); + return (0, base64_codec_1.encode)(this.concat(iv, ciphertext)); + } + const ciphertext = yield this.primitives.aesCbcEncrypt(key, LegacyCrypto.staticIV, plaintext); + return (0, base64_codec_1.encode)((0, bytes_1.toArrayBuffer)(ciphertext)) || data; + }); } /** * Decrypt provided data. * * @param data - Encrypted data which should be decrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * - * @returns Decrypted `data`. + * @returns Decrypted `data` or `null` if it can't be decrypted. */ - pnDecrypt(data, customCipherKey, options) { - const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; - if (!decidedCipherKey) - return data; - if (this.logger) { - this.logger.debug('Crypto', () => ({ - messageType: 'object', - message: Object.assign({ data }, (options !== null && options !== void 0 ? options : {})), - details: 'Decrypt with parameters:', - ignoredKeys: utils_1.isSensitiveLogKey, - })); - } - options = this.parseOptions(options); - const mode = this.getMode(options); - const cipherKey = this.getPaddedKey(decidedCipherKey, options); - if (this.configuration.useRandomIVs) { - const ciphertext = new Uint8ClampedArray((0, base64_codec_1.decode)(data)); - const iv = bufferToWordArray(ciphertext.slice(0, 16)); - const payload = bufferToWordArray(ciphertext.slice(16)); - try { - // @ts-expect-error Bundled library without types. - const plainJSON = hmac_sha256_1.default.AES.decrypt({ ciphertext: payload }, cipherKey, { iv, mode }).toString( - // @ts-expect-error Bundled library without types. - hmac_sha256_1.default.enc.Utf8); - return JSON.parse(plainJSON); - } - catch (e) { - if (this.logger) - this.logger.error('Crypto', () => ({ messageType: 'error', message: e })); - return null; + pnDecrypt(data, customCipherKey) { + return __awaiter(this, void 0, void 0, function* () { + const decidedCipherKey = customCipherKey !== null && customCipherKey !== void 0 ? customCipherKey : this.configuration.cipherKey; + if (!decidedCipherKey) + return data; + if (this.logger) { + this.logger.debug('Crypto', () => ({ + messageType: 'object', + message: { data }, + details: 'Decrypt with parameters:', + ignoredKeys: utils_1.isSensitiveLogKey, + })); } - } - else { - const iv = this.getIV(options); try { - // @ts-expect-error Bundled library without types. - const ciphertext = hmac_sha256_1.default.enc.Base64.parse(data); - // @ts-expect-error Bundled library without types. - const plainJSON = hmac_sha256_1.default.AES.decrypt({ ciphertext }, cipherKey, { iv, mode }).toString(hmac_sha256_1.default.enc.Utf8); - return JSON.parse(plainJSON); + const key = yield this.getKey(decidedCipherKey); + const encrypted = new Uint8Array((0, base64_codec_1.decode)(data)); + let iv = LegacyCrypto.staticIV; + let ciphertext = encrypted; + if (this.configuration.useRandomIVs) { + iv = encrypted.slice(0, LegacyCrypto.IV_LENGTH); + ciphertext = encrypted.slice(LegacyCrypto.IV_LENGTH); + } + const plaintext = yield this.primitives.aesCbcDecrypt(key, iv, ciphertext); + return JSON.parse((0, bytes_1.decodeUtf8Strict)(plaintext)); } catch (e) { if (this.logger) this.logger.error('Crypto', () => ({ messageType: 'error', message: e })); return null; } - } - } - /** - * Pre-process provided custom crypto configuration. - * - * @param incomingOptions - Configuration which should be pre-processed before use. - * - * @returns Normalized crypto configuration options. - */ - parseOptions(incomingOptions) { - var _a, _b, _c, _d; - if (!incomingOptions) - return this.defaultOptions; - // Defaults - const options = { - encryptKey: (_a = incomingOptions.encryptKey) !== null && _a !== void 0 ? _a : this.defaultOptions.encryptKey, - keyEncoding: (_b = incomingOptions.keyEncoding) !== null && _b !== void 0 ? _b : this.defaultOptions.keyEncoding, - keyLength: (_c = incomingOptions.keyLength) !== null && _c !== void 0 ? _c : this.defaultOptions.keyLength, - mode: (_d = incomingOptions.mode) !== null && _d !== void 0 ? _d : this.defaultOptions.mode, - }; - // Validation - if (this.allowedKeyEncodings.indexOf(options.keyEncoding.toLowerCase()) === -1) - options.keyEncoding = this.defaultOptions.keyEncoding; - if (this.allowedKeyLengths.indexOf(options.keyLength) === -1) - options.keyLength = this.defaultOptions.keyLength; - if (this.allowedModes.indexOf(options.mode.toLowerCase()) === -1) - options.mode = this.defaultOptions.mode; - return options; - } - /** - * Decode provided cipher key. - * - * @param key - Key in `encoding` provided by `options`. - * @param options - Crypto configuration options with cipher key details. - * - * @returns Array buffer with decoded key. - */ - decodeKey(key, options) { - // @ts-expect-error Bundled library without types. - if (options.keyEncoding === 'base64') - return hmac_sha256_1.default.enc.Base64.parse(key); - // @ts-expect-error Bundled library without types. - if (options.keyEncoding === 'hex') - return hmac_sha256_1.default.enc.Hex.parse(key); - return key; - } - /** - * Add padding to the cipher key. - * - * @param key - Key which should be padded. - * @param options - Crypto configuration options with cipher key details. - * - * @returns Properly padded cipher key. - */ - getPaddedKey(key, options) { - key = this.decodeKey(key, options); - // @ts-expect-error Bundled library without types. - if (options.encryptKey) - return hmac_sha256_1.default.enc.Utf8.parse(this.SHA256(key).slice(0, 32)); - return key; + }); } /** - * Cipher mode. + * Derive AES-256 key from the cipher key (cached for configured cipher key). * - * @param options - Crypto configuration with information about cipher mode. + * @param cipherKey - User-provided cipher key. * - * @returns Crypto cipher mode. + * @returns First 32 characters of HEX encoded SHA-256 digest as UTF-8 bytes. */ - getMode(options) { - // @ts-expect-error Bundled library without types. - if (options.mode === 'ecb') - return hmac_sha256_1.default.mode.ECB; - // @ts-expect-error Bundled library without types. - return hmac_sha256_1.default.mode.CBC; + getKey(cipherKey) { + if (cipherKey !== this.configuration.cipherKey) + return this.deriveKey(cipherKey); + if (!this.configuredKey) { + this.configuredKey = this.deriveKey(cipherKey); + this.configuredKey.catch(() => (this.configuredKey = undefined)); + } + return this.configuredKey; } /** - * Cipher initialization vector. + * Derive AES-256 key from the cipher key. * - * @param options - Crypto configuration with information about cipher mode. + * @param cipherKey - User-provided cipher key. * - * @returns Initialization vector. + * @returns First 32 characters of HEX encoded SHA-256 digest as UTF-8 bytes. */ - getIV(options) { - // @ts-expect-error Bundled library without types. - return options.mode === 'cbc' ? hmac_sha256_1.default.enc.Utf8.parse(this.iv) : null; + deriveKey(cipherKey) { + return __awaiter(this, void 0, void 0, function* () { + const digest = yield this.primitives.sha256(LegacyCrypto.encoder.encode(cipherKey)); + return LegacyCrypto.encoder.encode((0, bytes_1.bytesToHex)(digest).slice(0, 32)); + }); } /** - * Random initialization vector. - * - * @returns Generated random initialization vector. + * Join two byte arrays into {@link ArrayBuffer}. */ - getRandomIV() { - // @ts-expect-error Bundled library without types. - return hmac_sha256_1.default.lib.WordArray.random(16); + concat(a, b) { + const result = new Uint8Array(a.byteLength + b.byteLength); + result.set(a, 0); + result.set(b, a.byteLength); + return result.buffer; } } -exports.default = default_1; +/** + * `string` to {@link Uint8Array} encoder. + */ +LegacyCrypto.encoder = new TextEncoder(); +/** + * Static crypto initialization vector (used when random IVs are disabled). + */ +LegacyCrypto.staticIV = new TextEncoder().encode('0123456789012345'); +/** + * Initialization vector length. + */ +LegacyCrypto.IV_LENGTH = 16; +exports.default = LegacyCrypto; diff --git a/lib/core/components/request.js b/lib/core/components/request.js index a3d38f3af..1fb50d844 100644 --- a/lib/core/components/request.js +++ b/lib/core/components/request.js @@ -85,6 +85,17 @@ class AbstractRequest { validate() { return undefined; } + /** + * Prepare request for sending. + * + * Called after successful {@link validate} and before {@link request}. Used by requests which need to + * perform asynchronous pre-processing (for example, payload encryption) before the transport request can + * be built synchronously. + * + * @returns `Promise` only if asynchronous pre-processing is required (request scheduling stays + * synchronous otherwise). + */ + prepare() { } /** * Parse service response. * diff --git a/lib/core/endpoints/fetch_messages.js b/lib/core/endpoints/fetch_messages.js index 34a822254..f8f787110 100644 --- a/lib/core/endpoints/fetch_messages.js +++ b/lib/core/endpoints/fetch_messages.js @@ -131,28 +131,30 @@ class FetchMessagesRequest extends request_1.AbstractRequest { const serviceResponse = this.deserializeResponse(response); const responseChannels = (_a = serviceResponse.channels) !== null && _a !== void 0 ? _a : {}; const channels = {}; - Object.keys(responseChannels).forEach((channel) => { - // Map service response to expected data object type structure. - channels[channel] = responseChannels[channel].map((payload) => { - // `null` message type means regular message. - if (payload.message_type === null) - payload.message_type = History.PubNubMessageType.Message; - const processedPayload = this.processPayload(channel, payload); - const item = Object.assign(Object.assign({ channel, timetoken: payload.timetoken, message: processedPayload.payload, messageType: payload.message_type }, (payload.custom_message_type ? { customMessageType: payload.custom_message_type } : {})), { uuid: payload.uuid }); - if (payload.actions) { - const itemWithActions = item; - itemWithActions.actions = payload.actions; - // Backward compatibility for existing users. - // TODO: Remove in next release. - itemWithActions.data = payload.actions; - } - if (payload.meta) - item.meta = payload.meta; - if (processedPayload.error) - item.error = processedPayload.error; - return item; - }); - }); + // Messages processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + const channelNames = Object.keys(responseChannels); + const channelsMessages = yield Promise.all(channelNames.map((channel) => + // Map service response to expected data object type structure. + Promise.all(responseChannels[channel].map((payload) => __awaiter(this, void 0, void 0, function* () { + // `null` message type means regular message. + if (payload.message_type === null) + payload.message_type = History.PubNubMessageType.Message; + const processedPayload = yield this.processPayload(channel, payload); + const item = Object.assign(Object.assign({ channel, timetoken: payload.timetoken, message: processedPayload.payload, messageType: payload.message_type }, (payload.custom_message_type ? { customMessageType: payload.custom_message_type } : {})), { uuid: payload.uuid }); + if (payload.actions) { + const itemWithActions = item; + itemWithActions.actions = payload.actions; + // Backward compatibility for existing users. + // TODO: Remove in next release. + itemWithActions.data = payload.actions; + } + if (payload.meta) + item.meta = payload.meta; + if (processedPayload.error) + item.error = processedPayload.error; + return item; + }))))); + channelNames.forEach((channel, index) => (channels[channel] = channelsMessages[index])); if (serviceResponse.more) return { channels, more: serviceResponse.more }; return { channels }; @@ -178,39 +180,41 @@ class FetchMessagesRequest extends request_1.AbstractRequest { * @returns */ processPayload(channel, payload) { - const { crypto, logVerbosity } = this.parameters; - if (!crypto || typeof payload.message !== 'string') - return { payload: payload.message }; - let decryptedPayload; - let error; - try { - const decryptedData = crypto.decrypt(payload.message); - decryptedPayload = - decryptedData instanceof ArrayBuffer - ? JSON.parse(FetchMessagesRequest.decoder.decode(decryptedData)) - : decryptedData; - } - catch (err) { - if (logVerbosity) - console.log(`decryption error`, err.message); - decryptedPayload = payload.message; - error = `Error while decrypting message content: ${err.message}`; - } - if (!error && - decryptedPayload && - payload.message_type == History.PubNubMessageType.Files && - typeof decryptedPayload === 'object' && - this.isFileMessage(decryptedPayload)) { - const fileMessage = decryptedPayload; - return { - payload: { - message: fileMessage.message, - file: Object.assign(Object.assign({}, fileMessage.file), { url: this.parameters.getFileUrl({ channel, id: fileMessage.file.id, name: fileMessage.file.name }) }), - }, - error, - }; - } - return { payload: decryptedPayload, error }; + return __awaiter(this, void 0, void 0, function* () { + const { crypto, logVerbosity } = this.parameters; + if (!crypto || typeof payload.message !== 'string') + return { payload: payload.message }; + let decryptedPayload; + let error; + try { + const decryptedData = yield crypto.decrypt(payload.message); + decryptedPayload = + decryptedData instanceof ArrayBuffer + ? JSON.parse(FetchMessagesRequest.decoder.decode(decryptedData)) + : decryptedData; + } + catch (err) { + if (logVerbosity) + console.log(`decryption error`, err.message); + decryptedPayload = payload.message; + error = `Error while decrypting message content: ${err.message}`; + } + if (!error && + decryptedPayload && + payload.message_type == History.PubNubMessageType.Files && + typeof decryptedPayload === 'object' && + this.isFileMessage(decryptedPayload)) { + const fileMessage = decryptedPayload; + return { + payload: { + message: fileMessage.message, + file: Object.assign(Object.assign({}, fileMessage.file), { url: this.parameters.getFileUrl({ channel, id: fileMessage.file.id, name: fileMessage.file.name }) }), + }, + error, + }; + } + return { payload: decryptedPayload, error }; + }); } /** * Check whether `payload` potentially represents file message. diff --git a/lib/core/endpoints/file_upload/publish_file.js b/lib/core/endpoints/file_upload/publish_file.js index ea75057aa..f1aa1154b 100644 --- a/lib/core/endpoints/file_upload/publish_file.js +++ b/lib/core/endpoints/file_upload/publish_file.js @@ -20,7 +20,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.PublishFileMessageRequest = void 0; const request_1 = require("../../components/request"); const operations_1 = __importDefault(require("../../constants/operations")); -const base64_codec_1 = require("../../components/base64_codec"); const utils_1 = require("../../utils"); // -------------------------------------------------------- // ----------------------- Defaults ----------------------- @@ -57,18 +56,34 @@ class PublishFileMessageRequest extends request_1.AbstractRequest { if (!fileName) return "file name can't be empty"; } + prepare() { + const { crypto } = this.parameters; + if (!crypto) + return; + return (0, utils_1.encryptMessagePayload)(crypto, this.fileMessage).then((encrypted) => { + this.encryptedPayload = encrypted; + }); + } parse(response) { return __awaiter(this, void 0, void 0, function* () { return { timetoken: this.deserializeResponse(response)[2] }; }); } get path() { - const { message, channel, keySet: { publishKey, subscribeKey }, fileId, fileName, } = this.parameters; - const fileMessage = Object.assign({ file: { + const { channel, keySet: { publishKey, subscribeKey }, } = this.parameters; + return `/v1/files/publish-file/${publishKey}/${subscribeKey}/0/${(0, utils_1.encodeString)(channel)}/0/${(0, utils_1.encodeString)(this.prepareMessagePayload(this.fileMessage))}`; + } + /** + * File message which should be published. + * + * @returns File message payload. + */ + get fileMessage() { + const { message, fileId, fileName } = this.parameters; + return Object.assign({ file: { name: fileName, id: fileId, } }, (message ? { message } : {})); - return `/v1/files/publish-file/${publishKey}/${subscribeKey}/0/${(0, utils_1.encodeString)(channel)}/0/${(0, utils_1.encodeString)(this.prepareMessagePayload(fileMessage))}`; } get queryParameters() { const { customMessageType, storeInHistory, ttl, meta } = this.parameters; @@ -77,20 +92,21 @@ class PublishFileMessageRequest extends request_1.AbstractRequest { /** * Pre-process provided data. * - * Data will be "normalized" and encrypted if `cryptoModule` has been provided. + * Data will be "normalized" and use encrypted payload computed by {@link prepare} if `cryptoModule` has + * been provided. * * @param payload - User-provided data which should be pre-processed before use. * * @returns Payload which can be used as part of request URL or body. * - * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. + * @throws {Error} in case if provided `payload` can't be stringified or request hasn't been prepared. */ prepareMessagePayload(payload) { - const { crypto } = this.parameters; - if (!crypto) + if (!this.parameters.crypto) return JSON.stringify(payload) || ''; - const encrypted = crypto.encrypt(JSON.stringify(payload)); - return JSON.stringify(typeof encrypted === 'string' ? encrypted : (0, base64_codec_1.encode)(encrypted)); + if (this.encryptedPayload === undefined) + throw new Error('Encryption error: request payload not prepared'); + return this.encryptedPayload; } } exports.PublishFileMessageRequest = PublishFileMessageRequest; diff --git a/lib/core/endpoints/history/get_history.js b/lib/core/endpoints/history/get_history.js index 388598710..542d05e87 100644 --- a/lib/core/endpoints/history/get_history.js +++ b/lib/core/endpoints/history/get_history.js @@ -80,8 +80,9 @@ class GetHistoryRequest extends request_1.AbstractRequest { if (!Array.isArray(messages)) return { messages: [], startTimeToken, endTimeToken }; return { - messages: messages.map((payload) => { - const processedPayload = this.processPayload(payload.message); + // Messages processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + messages: yield Promise.all(messages.map((payload) => __awaiter(this, void 0, void 0, function* () { + const processedPayload = yield this.processPayload(payload.message); const item = { entry: processedPayload.payload, timetoken: payload.timetoken, @@ -91,7 +92,7 @@ class GetHistoryRequest extends request_1.AbstractRequest { if (payload.meta) item.meta = payload.meta; return item; - }), + }))), startTimeToken, endTimeToken, }; @@ -106,28 +107,30 @@ class GetHistoryRequest extends request_1.AbstractRequest { return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ count: count, include_token: 'true' }, (start ? { start } : {})), (end ? { end } : {})), (stringifiedTimeToken ? { string_message_token: 'true' } : {})), (reverse !== undefined && reverse !== null ? { reverse: reverse.toString() } : {})), (includeMeta ? { include_meta: 'true' } : {})); } processPayload(payload) { - const { crypto, logVerbosity } = this.parameters; - if (!crypto || typeof payload !== 'string') - return { payload }; - let decryptedPayload; - let error; - try { - const decryptedData = crypto.decrypt(payload); - decryptedPayload = - decryptedData instanceof ArrayBuffer - ? JSON.parse(GetHistoryRequest.decoder.decode(decryptedData)) - : decryptedData; - } - catch (err) { - if (logVerbosity) - console.log(`decryption error`, err.message); - decryptedPayload = payload; - error = `Error while decrypting message content: ${err.message}`; - } - return { - payload: decryptedPayload, - error, - }; + return __awaiter(this, void 0, void 0, function* () { + const { crypto, logVerbosity } = this.parameters; + if (!crypto || typeof payload !== 'string') + return { payload }; + let decryptedPayload; + let error; + try { + const decryptedData = yield crypto.decrypt(payload); + decryptedPayload = + decryptedData instanceof ArrayBuffer + ? JSON.parse(GetHistoryRequest.decoder.decode(decryptedData)) + : decryptedData; + } + catch (err) { + if (logVerbosity) + console.log(`decryption error`, err.message); + decryptedPayload = payload; + error = `Error while decrypting message content: ${err.message}`; + } + return { + payload: decryptedPayload, + error, + }; + }); } } exports.GetHistoryRequest = GetHistoryRequest; diff --git a/lib/core/endpoints/publish.js b/lib/core/endpoints/publish.js index 0a3e2aab1..6f4f6feac 100644 --- a/lib/core/endpoints/publish.js +++ b/lib/core/endpoints/publish.js @@ -19,7 +19,6 @@ exports.PublishRequest = void 0; const transport_request_1 = require("../types/transport-request"); const request_1 = require("../components/request"); const operations_1 = __importDefault(require("../constants/operations")); -const base64_codec_1 = require("../components/base64_codec"); const utils_1 = require("../utils"); // -------------------------------------------------------- // ----------------------- Defaults ----------------------- @@ -64,6 +63,14 @@ class PublishRequest extends request_1.AbstractRequest { if (!publishKey) return "Missing 'publishKey'"; } + prepare() { + const { crypto, message } = this.parameters; + if (!crypto) + return; + return (0, utils_1.encryptMessagePayload)(crypto, message).then((encrypted) => { + this.encryptedPayload = encrypted; + }); + } parse(response) { return __awaiter(this, void 0, void 0, function* () { return { timetoken: this.deserializeResponse(response)[2] }; @@ -101,20 +108,21 @@ class PublishRequest extends request_1.AbstractRequest { /** * Pre-process provided data. * - * Data will be "normalized" and encrypted if `cryptoModule` has been provided. + * Data will be "normalized" and use encrypted payload computed by {@link prepare} if `cryptoModule` has + * been provided. * * @param payload - User-provided data which should be pre-processed before use. * * @returns Payload which can be used as part of request URL or body. * - * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. + * @throws {Error} in case if provided `payload` can't be stringified or request hasn't been prepared. */ prepareMessagePayload(payload) { - const { crypto } = this.parameters; - if (!crypto) + if (!this.parameters.crypto) return JSON.stringify(payload) || ''; - const encrypted = crypto.encrypt(JSON.stringify(payload)); - return JSON.stringify(typeof encrypted === 'string' ? encrypted : (0, base64_codec_1.encode)(encrypted)); + if (this.encryptedPayload === undefined) + throw new Error('Encryption error: request payload not prepared'); + return this.encryptedPayload; } } exports.PublishRequest = PublishRequest; diff --git a/lib/core/endpoints/subscribe.js b/lib/core/endpoints/subscribe.js index 7666d6fab..792e96124 100644 --- a/lib/core/endpoints/subscribe.js +++ b/lib/core/endpoints/subscribe.js @@ -147,13 +147,13 @@ class BaseSubscribeRequest extends request_1.AbstractRequest { if (!serviceResponse) { throw new pubnub_error_1.PubNubError('Service response error, check status for details', (0, pubnub_error_1.createMalformedResponseError)(responseText, response.status)); } - const events = serviceResponse.m - .filter((envelope) => { + const envelopes = serviceResponse.m.filter((envelope) => { const subscribable = envelope.b === undefined ? envelope.c : envelope.b; return ((this.parameters.channels && this.parameters.channels.includes(subscribable)) || (this.parameters.channelGroups && this.parameters.channelGroups.includes(subscribable))); - }) - .map((envelope) => { + }); + // Envelopes processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + const parsedEvents = yield Promise.all(envelopes.map((envelope) => __awaiter(this, void 0, void 0, function* () { let { e: eventType } = envelope; // Events delivered on a presence channel are always presence events: the `-pnpres` suffix wins over // the service-reported type. `envelope.c` is the actual channel (`envelope.b` is the channel group @@ -174,20 +174,20 @@ class BaseSubscribeRequest extends request_1.AbstractRequest { if (eventType == PubNubEventType.Message) { return { type: PubNubEventType.Message, - data: this.messageFromEnvelope(envelope), + data: yield this.messageFromEnvelope(envelope), pn_mfp, }; } return { type: PubNubEventType.Files, - data: this.fileFromEnvelope(envelope), + data: yield this.fileFromEnvelope(envelope), pn_mfp, }; } else if (eventType == PubNubEventType.Message) { return { type: PubNubEventType.Message, - data: this.messageFromEnvelope(envelope), + data: yield this.messageFromEnvelope(envelope), pn_mfp, }; } @@ -224,16 +224,16 @@ class BaseSubscribeRequest extends request_1.AbstractRequest { // Guard: only treat as DataSync when the service marks it so; otherwise fall back to message. if (dataSync) return { type: PubNubEventType.DataSync, data: dataSync, pn_mfp }; - return { type: PubNubEventType.Message, data: this.messageFromEnvelope(envelope), pn_mfp }; + return { type: PubNubEventType.Message, data: yield this.messageFromEnvelope(envelope), pn_mfp }; } // The only known event type left is a file event. return { type: PubNubEventType.Files, - data: this.fileFromEnvelope(envelope), + data: yield this.fileFromEnvelope(envelope), pn_mfp, }; - }) - .filter((event) => event !== undefined); + }))); + const events = parsedEvents.filter((event) => event !== undefined); return { cursor: { timetoken: serviceResponse.t.t, region: serviceResponse.t.r }, messages: events, @@ -289,28 +289,30 @@ class BaseSubscribeRequest extends request_1.AbstractRequest { subscribedChannel, timetoken: envelope.p.t }, (presenceData !== null && presenceData !== void 0 ? presenceData : {})); } messageFromEnvelope(envelope) { - const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); - const [message, decryptionError] = this.decryptedData(envelope.d); - // Backward compatibility with deprecated properties. - const actualChannel = subscription !== null ? channel : null; - const subscribedChannel = subscription !== null ? subscription : channel; - // Basic message event payload. - const event = { - channel, - subscription, - actualChannel, - subscribedChannel, - timetoken: envelope.p.t, - publisher: envelope.i, - message, - }; - if (envelope.u) - event.userMetadata = envelope.u; - if (envelope.cmt) - event.customMessageType = envelope.cmt; - if (decryptionError) - event.error = decryptionError; - return event; + return __awaiter(this, void 0, void 0, function* () { + const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); + const [message, decryptionError] = yield this.decryptedData(envelope.d); + // Backward compatibility with deprecated properties. + const actualChannel = subscription !== null ? channel : null; + const subscribedChannel = subscription !== null ? subscription : channel; + // Basic message event payload. + const event = { + channel, + subscription, + actualChannel, + subscribedChannel, + timetoken: envelope.p.t, + publisher: envelope.i, + message, + }; + if (envelope.u) + event.userMetadata = envelope.u; + if (envelope.cmt) + event.customMessageType = envelope.cmt; + if (decryptionError) + event.error = decryptionError; + return event; + }); } signalFromEnvelope(envelope) { const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); @@ -429,37 +431,39 @@ class BaseSubscribeRequest extends request_1.AbstractRequest { }; } fileFromEnvelope(envelope) { - const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); - const [file, decryptionError] = this.decryptedData(envelope.d); - let errorMessage = decryptionError; - // Basic file event payload. - const event = { - channel, - subscription, - timetoken: envelope.p.t, - publisher: envelope.i, - }; - if (envelope.u) - event.userMetadata = envelope.u; - if (!file) - errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `File information payload is missing.`); - else if (typeof file === 'string') - errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `Unexpected file information payload data type.`); - else { - event.message = file.message; - if (file.file) { - event.file = { - id: file.file.id, - name: file.file.name, - url: this.parameters.getFileUrl({ id: file.file.id, name: file.file.name, channel }), - }; + return __awaiter(this, void 0, void 0, function* () { + const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); + const [file, decryptionError] = yield this.decryptedData(envelope.d); + let errorMessage = decryptionError; + // Basic file event payload. + const event = { + channel, + subscription, + timetoken: envelope.p.t, + publisher: envelope.i, + }; + if (envelope.u) + event.userMetadata = envelope.u; + if (!file) + errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `File information payload is missing.`); + else if (typeof file === 'string') + errorMessage !== null && errorMessage !== void 0 ? errorMessage : (errorMessage = `Unexpected file information payload data type.`); + else { + event.message = file.message; + if (file.file) { + event.file = { + id: file.file.id, + name: file.file.name, + url: this.parameters.getFileUrl({ id: file.file.id, name: file.file.name, channel }), + }; + } } - } - if (envelope.cmt) - event.customMessageType = envelope.cmt; - if (errorMessage) - event.error = errorMessage; - return event; + if (envelope.cmt) + event.customMessageType = envelope.cmt; + if (errorMessage) + event.error = errorMessage; + return event; + }); } // endregion subscriptionChannelFromEnvelope(envelope) { @@ -473,22 +477,24 @@ class BaseSubscribeRequest extends request_1.AbstractRequest { * @returns Tuple with decrypted data and decryption error (if any). */ decryptedData(data) { - if (!this.parameters.crypto || typeof data !== 'string') - return [data, undefined]; - let payload; - let error; - try { - const decryptedData = this.parameters.crypto.decrypt(data); - payload = - decryptedData instanceof ArrayBuffer - ? JSON.parse(SubscribeRequest.decoder.decode(decryptedData)) - : decryptedData; - } - catch (err) { - payload = null; - error = `Error while decrypting message content: ${err.message}`; - } - return [(payload !== null && payload !== void 0 ? payload : data), error]; + return __awaiter(this, void 0, void 0, function* () { + if (!this.parameters.crypto || typeof data !== 'string') + return [data, undefined]; + let payload; + let error; + try { + const decryptedData = yield this.parameters.crypto.decrypt(data); + payload = + decryptedData instanceof ArrayBuffer + ? JSON.parse(SubscribeRequest.decoder.decode(decryptedData)) + : decryptedData; + } + catch (err) { + payload = null; + error = `Error while decrypting message content: ${err.message}`; + } + return [(payload !== null && payload !== void 0 ? payload : data), error]; + }); } } exports.BaseSubscribeRequest = BaseSubscribeRequest; diff --git a/lib/core/interfaces/crypto-module.js b/lib/core/interfaces/crypto-module.js index 06414eb72..36270ef0d 100644 --- a/lib/core/interfaces/crypto-module.js +++ b/lib/core/interfaces/crypto-module.js @@ -4,6 +4,7 @@ */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractCryptoModule = void 0; +const pubnub_error_1 = require("../../errors/pubnub-error"); class AbstractCryptoModule { // -------------------------------------------------------- // --------------- Convenience functions ------------------ @@ -39,9 +40,12 @@ class AbstractCryptoModule { } // endregion constructor(configuration) { - var _a; + var _a, _b; + // AES-GCM cryptor is decrypt-only and can be registered only as an additional cryptor. + if (((_a = configuration.default) === null || _a === void 0 ? void 0 : _a.identifier) === 'ACGM') + throw new pubnub_error_1.PubNubError("Crypto module error: AES-GCM cryptor (ACGM) can't be used as default cryptor."); this.defaultCryptor = configuration.default; - this.cryptors = (_a = configuration.cryptors) !== null && _a !== void 0 ? _a : []; + this.cryptors = (_b = configuration.cryptors) !== null && _b !== void 0 ? _b : []; } /** * Assign registered loggers' manager. diff --git a/lib/core/interfaces/crypto-primitives.js b/lib/core/interfaces/crypto-primitives.js new file mode 100644 index 000000000..a8a1dfc0d --- /dev/null +++ b/lib/core/interfaces/crypto-primitives.js @@ -0,0 +1,8 @@ +"use strict"; +/** + * Platform-specific cryptographic primitives. + * + * Core SDK code doesn't depend on any concrete crypto implementation. Each platform entry point injects + * primitives backed by the native crypto available on it (`crypto.subtle`, `node:crypto` or `@noble/*`). + */ +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/core/pubnub-common.js b/lib/core/pubnub-common.js index 97d630a9e..e8b092904 100644 --- a/lib/core/pubnub-common.js +++ b/lib/core/pubnub-common.js @@ -178,6 +178,15 @@ class PubNubCore { * @internal */ constructor(configuration) { + /** + * Tail of the asynchronous requests preparation queue. + * + * Used to schedule requests which require asynchronous preparation (for example, payload encryption) in the + * same order in which they have been called. + * + * @internal + */ + this.requestsPreparation = Promise.resolve(); /** * List of subscribe capable objects with active subscriptions. * @@ -865,6 +874,19 @@ class PubNubCore { return callback(validationError, null); throw new pubnub_error_1.PubNubError('Validation failed, check status for details', validationError); } + // Asynchronous request pre-processing (for example, payload encryption). + const preparation = request.prepare(); + if (preparation) { + // Requests which require preparation are scheduled in call order (same as synchronous requests). + const orderedPreparation = this.requestsPreparation.then(() => preparation); + this.requestsPreparation = orderedPreparation.catch(() => { }); + try { + yield orderedPreparation; + } + catch (error) { + return this.handleRequestError(error, request.operation(), callback); + } + } // Complete request configuration. const transportRequest = request.request(); const operation = request.operation(); @@ -918,25 +940,39 @@ class PubNubCore { return callback(status, parsed); return parsed; }) - .catch((error) => { - const apiError = !(error instanceof pubnub_api_error_1.PubNubAPIError) ? pubnub_api_error_1.PubNubAPIError.create(error) : error; - // Notify callback (if possible). - if (callback) { - if (apiError.category !== categories_2.default.PNCancelledCategory) { - this.logger.error('PubNub', () => ({ - messageType: 'error', - message: apiError.toPubNubError(operation, 'REST API request processing error, check status for details'), - })); - } - return callback(apiError.toStatus(operation), null); - } - const pubNubError = apiError.toPubNubError(operation, 'REST API request processing error, check status for details'); - if (apiError.category !== categories_2.default.PNCancelledCategory) - this.logger.error('PubNub', () => ({ messageType: 'error', message: pubNubError })); - throw pubNubError; - }); + .catch((error) => this.handleRequestError(error, operation, callback)); }); } + /** + * Handle REST API request processing error. + * + * @internal + * + * @param error - Request processing error. + * @param operation - Type of failed REST API operation. + * @param [callback] - Request completion handler callback. + * + * @returns `void` in case if `callback` provided. + * + * @throws PubNubError in case if `callback` not provided. + */ + handleRequestError(error, operation, callback) { + const apiError = !(error instanceof pubnub_api_error_1.PubNubAPIError) ? pubnub_api_error_1.PubNubAPIError.create(error) : error; + // Notify callback (if possible). + if (callback) { + if (apiError.category !== categories_2.default.PNCancelledCategory) { + this.logger.error('PubNub', () => ({ + messageType: 'error', + message: apiError.toPubNubError(operation, 'REST API request processing error, check status for details'), + })); + } + return callback(apiError.toStatus(operation), null); + } + const pubNubError = apiError.toPubNubError(operation, 'REST API request processing error, check status for details'); + if (apiError.category !== categories_2.default.PNCancelledCategory) + this.logger.error('PubNub', () => ({ messageType: 'error', message: pubNubError })); + throw pubNubError; + } /** * Unsubscribe from all channels and groups. * @@ -3268,24 +3304,26 @@ class PubNubCore { * @param [customCipherKey] - Cipher key which should be used to encrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data encryption result as a string. + * @returns Asynchronous data encryption result as a string. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ encrypt(data, customCipherKey) { - this.logger.warn('PubNub', "'encrypt' is deprecated. Use cryptoModule instead."); - const cryptoModule = this._configuration.getCryptoModule(); - if (!customCipherKey && cryptoModule && typeof data === 'string') { - const encrypted = cryptoModule.encrypt(data); - return typeof encrypted === 'string' ? encrypted : (0, base64_codec_1.encode)(encrypted); - } - if (!this.crypto) - throw new Error('Encryption error: cypher key not set'); - if (process.env.CRYPTO_MODULE !== 'disabled') { - return this.crypto.encrypt(data, customCipherKey); - } - else - throw new Error('Encryption error: crypto module disabled'); + return __awaiter(this, void 0, void 0, function* () { + this.logger.warn('PubNub', "'encrypt' is deprecated. Use cryptoModule instead."); + const cryptoModule = this._configuration.getCryptoModule(); + if (!customCipherKey && cryptoModule && typeof data === 'string') { + const encrypted = yield cryptoModule.encrypt(data); + return typeof encrypted === 'string' ? encrypted : (0, base64_codec_1.encode)(encrypted); + } + if (!this.crypto) + throw new Error('Encryption error: cypher key not set'); + if (process.env.CRYPTO_MODULE !== 'disabled') { + return this.crypto.encrypt(data, customCipherKey); + } + else + throw new Error('Encryption error: crypto module disabled'); + }); } /** * Decrypt data. @@ -3294,24 +3332,26 @@ class PubNubCore { * @param [customCipherKey] - Cipher key which should be used to decrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data decryption result as an object. + * @returns Asynchronous data decryption result as an object. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ decrypt(data, customCipherKey) { - this.logger.warn('PubNub', "'decrypt' is deprecated. Use cryptoModule instead."); - const cryptoModule = this._configuration.getCryptoModule(); - if (!customCipherKey && cryptoModule) { - const decrypted = cryptoModule.decrypt(data); - return decrypted instanceof ArrayBuffer ? JSON.parse(new TextDecoder().decode(decrypted)) : decrypted; - } - if (!this.crypto) - throw new Error('Decryption error: cypher key not set'); - if (process.env.CRYPTO_MODULE !== 'disabled') { - return this.crypto.decrypt(data, customCipherKey); - } - else - throw new Error('Decryption error: crypto module disabled'); + return __awaiter(this, void 0, void 0, function* () { + this.logger.warn('PubNub', "'decrypt' is deprecated. Use cryptoModule instead."); + const cryptoModule = this._configuration.getCryptoModule(); + if (!customCipherKey && cryptoModule) { + const decrypted = yield cryptoModule.decrypt(data); + return decrypted instanceof ArrayBuffer ? JSON.parse(new TextDecoder().decode(decrypted)) : decrypted; + } + if (!this.crypto) + throw new Error('Decryption error: cypher key not set'); + if (process.env.CRYPTO_MODULE !== 'disabled') { + return this.crypto.decrypt(data, customCipherKey); + } + else + throw new Error('Decryption error: crypto module disabled'); + }); } /** * Encrypt file content. diff --git a/lib/core/utils.js b/lib/core/utils.js index d2809ed2a..7d0d172e7 100644 --- a/lib/core/utils.js +++ b/lib/core/utils.js @@ -4,8 +4,18 @@ * * @internal */ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; Object.defineProperty(exports, "__esModule", { value: true }); -exports.messageFingerprint = exports.adjustedTimetokenBy = exports.referenceSubscribeTimetoken = exports.subscriptionTimetokenFromReference = exports.queryStringFromObject = exports.findUniqueCommonElements = exports.removeSingleOccurrence = exports.encodeNames = exports.encodeString = exports.isSensitiveLogKey = exports.SENSITIVE_LOG_KEYS = void 0; +exports.encryptMessagePayload = exports.messageFingerprint = exports.adjustedTimetokenBy = exports.referenceSubscribeTimetoken = exports.subscriptionTimetokenFromReference = exports.queryStringFromObject = exports.findUniqueCommonElements = exports.removeSingleOccurrence = exports.encodeNames = exports.encodeString = exports.isSensitiveLogKey = exports.SENSITIVE_LOG_KEYS = void 0; +const base64_codec_1 = require("./components/base64_codec"); /** * Object log property names that must never appear in SDK logs. * @@ -197,3 +207,20 @@ const messageFingerprint = (input) => { return mfp[0].toString(16).padStart(8, '0'); }; exports.messageFingerprint = messageFingerprint; +/** + * Encrypt message payload which should be sent as part of request URL or body. + * + * @param crypto - Module which should be used to encrypt data. + * @param payload - User-provided data which should be encrypted. + * + * @returns Stringified encrypted payload (base64 encoded if encryption result is binary). + * + * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. + * + * @internal + */ +const encryptMessagePayload = (crypto, payload) => __awaiter(void 0, void 0, void 0, function* () { + const encrypted = yield crypto.encrypt(JSON.stringify(payload)); + return JSON.stringify(typeof encrypted === 'string' ? encrypted : (0, base64_codec_1.encode)(encrypted)); +}); +exports.encryptMessagePayload = encryptMessagePayload; diff --git a/lib/crypto/modules/LegacyCryptoModule.js b/lib/crypto/modules/LegacyCryptoModule.js index 60cfc9f61..178bce731 100644 --- a/lib/crypto/modules/LegacyCryptoModule.js +++ b/lib/crypto/modules/LegacyCryptoModule.js @@ -49,20 +49,22 @@ class LegacyCryptoModule { * @throws {Error} When data is null/undefined or encryption fails */ encrypt(data) { - if (data === null || data === undefined) { - throw new Error('Encryption data cannot be null or undefined'); - } - try { - const plaintext = typeof data === 'string' ? data : buffer_1.Buffer.from(new Uint8Array(data)).toString('utf8'); - const encrypted = this.legacy.encrypt(plaintext); - if (typeof encrypted !== 'string') { - throw new Error('Legacy encryption failed: expected string result'); + return __awaiter(this, void 0, void 0, function* () { + if (data === null || data === undefined) { + throw new Error('Encryption data cannot be null or undefined'); } - return encrypted; - } - catch (error) { - throw new Error(`Encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`); - } + try { + const plaintext = typeof data === 'string' ? data : buffer_1.Buffer.from(new Uint8Array(data)).toString('utf8'); + const encrypted = yield this.legacy.encrypt(plaintext); + if (typeof encrypted !== 'string') { + throw new Error('Legacy encryption failed: expected string result'); + } + return encrypted; + } + catch (error) { + throw new Error(`Encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + }); } encryptFile(_file, _File) { return __awaiter(this, void 0, void 0, function* () { @@ -81,30 +83,32 @@ class LegacyCryptoModule { * @throws {Error} When data is null/undefined/empty or decryption fails */ decrypt(data) { - if (data === null || data === undefined) { - throw new Error('Decryption data cannot be null or undefined'); - } - try { - let ciphertextB64; - if (typeof data === 'string') { - if (data.trim() === '') { - throw new Error('Decryption data cannot be empty string'); - } - ciphertextB64 = data; + return __awaiter(this, void 0, void 0, function* () { + if (data === null || data === undefined) { + throw new Error('Decryption data cannot be null or undefined'); } - else { - if (data.byteLength === 0) { - throw new Error('Decryption data cannot be empty ArrayBuffer'); + try { + let ciphertextB64; + if (typeof data === 'string') { + if (data.trim() === '') { + throw new Error('Decryption data cannot be empty string'); + } + ciphertextB64 = data; + } + else { + if (data.byteLength === 0) { + throw new Error('Decryption data cannot be empty ArrayBuffer'); + } + ciphertextB64 = buffer_1.Buffer.from(new Uint8Array(data)).toString('base64'); } - ciphertextB64 = buffer_1.Buffer.from(new Uint8Array(data)).toString('base64'); + const decrypted = yield this.legacy.decrypt(ciphertextB64); + // The legacy decrypt method returns Payload | null, so no unsafe casting needed + return decrypted; } - const decrypted = this.legacy.decrypt(ciphertextB64); - // The legacy decrypt method returns Payload | null, so no unsafe casting needed - return decrypted; - } - catch (error) { - throw new Error(`Decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`); - } + catch (error) { + throw new Error(`Decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + }); } decryptFile(_file, _File) { return __awaiter(this, void 0, void 0, function* () { diff --git a/lib/crypto/modules/NodeCryptoModule/aesGcmCryptor.js b/lib/crypto/modules/NodeCryptoModule/aesGcmCryptor.js new file mode 100644 index 000000000..0169ae9bb --- /dev/null +++ b/lib/crypto/modules/NodeCryptoModule/aesGcmCryptor.js @@ -0,0 +1,190 @@ +"use strict"; +/** + * AES-GCM cryptor module. + * + * @internal + */ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = require("stream"); +const bytes_1 = require("../../../core/components/cryptography/bytes"); +const node_1 = __importDefault(require("../../primitives/node")); +/** + * AES-GCM cryptor. + * + * Decrypt-only AES-256-GCM cryptor for payloads produced by other PubNub SDKs. Cryptor metadata stored in the + * header is `nonce (12 bytes)` and the authentication tag (16 bytes) is appended to the ciphertext: + * `ciphertext || authentication tag`. + * + * **Note:** Encryption isn't supported and cryptor can't be used as crypto module default cryptor. + * + * @internal + */ +class AesGcmCryptor { + constructor({ cipherKey }) { + this.cipherKey = cipherKey; + } + // -------------------------------------------------------- + // --------------------- Encryption ----------------------- + // -------------------------------------------------------- + // region Encryption + encrypt(_data) { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + } + encryptStream(_stream) { + return __awaiter(this, void 0, void 0, function* () { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + }); + } + // endregion + // -------------------------------------------------------- + // --------------------- Decryption ----------------------- + // -------------------------------------------------------- + // region Decryption + decrypt(input) { + return __awaiter(this, void 0, void 0, function* () { + const data = typeof input.data === 'string' ? AesGcmCryptor.encoder.encode(input.data) : input.data; + if (!input.metadata || input.metadata.byteLength !== AesGcmCryptor.METADATA_LENGTH) + throw new Error('Decryption error: invalid metadata'); + if (data.byteLength <= AesGcmCryptor.TAG_LENGTH) + throw new Error('Decryption error: empty content'); + return (0, bytes_1.toArrayBuffer)(yield this.decryptBytes(new Uint8Array(input.metadata), new Uint8Array(data))); + }); + } + /** + * Decrypt provided encrypted stream object. + * + * **Note:** AES-GCM authenticates the whole message (authentication tag is the last 16 bytes of the stream), so the + * stream is buffered in memory and decrypted data is written to the output stream only after successful + * authentication. + * + * @param stream - Encrypted stream object for decryption. + * + * @returns Decrypted data as {@link Readable} stream. + */ + decryptStream(stream) { + return __awaiter(this, void 0, void 0, function* () { + if (stream.metadataLength !== AesGcmCryptor.METADATA_LENGTH) + throw new Error('Decryption error: invalid metadata'); + const decryptedStream = new stream_1.PassThrough(); + const chunks = []; + const onReadable = () => { + let data = stream.stream.read(); + while (data !== null) { + if (data) + chunks.push(typeof data === 'string' ? Buffer.from(data) : data); + data = stream.stream.read(); + } + }; + stream.stream.on('readable', onReadable); + stream.stream.on('end', () => { + const payload = Buffer.concat(chunks); + // Content validated asynchronously (like authentication) to report errors after the stream has been returned. + const decrypt = () => __awaiter(this, void 0, void 0, function* () { + if (payload.byteLength <= AesGcmCryptor.METADATA_LENGTH + AesGcmCryptor.TAG_LENGTH) + throw new Error('Decryption error: empty content'); + return this.decryptBytes(payload.subarray(0, AesGcmCryptor.METADATA_LENGTH), payload.subarray(AesGcmCryptor.METADATA_LENGTH)); + }); + decrypt().then((decrypted) => decryptedStream.end(Buffer.from(decrypted)), (error) => decryptedStream.destroy(error)); + }); + // Header has been read from the `readable` event handler: drain data which already has been buffered, because + // a fully buffered source may not emit `readable` again. + onReadable(); + return decryptedStream; + }); + } + // endregion + // -------------------------------------------------------- + // ----------------------- Helpers ------------------------ + // -------------------------------------------------------- + // region Helpers + get identifier() { + return AesGcmCryptor.IDENTIFIER; + } + /** + * Authenticate and decrypt data. + * + * @param nonce - Cryptor metadata (nonce). + * @param data - Encrypted data with appended authentication tag. + * + * @returns Decrypted data. + * + * @throws Error with generic message if data can't be authenticated. + */ + decryptBytes(nonce, data) { + return __awaiter(this, void 0, void 0, function* () { + try { + return yield AesGcmCryptor.primitives.aesGcmDecrypt(yield this.getKey(), nonce, data); + } + catch (_error) { + // Native error intentionally not exposed: wrong key and modified payload should be indistinguishable. + throw new Error(AesGcmCryptor.DECRYPTION_ERROR); + } + }); + } + /** + * Compute raw decryption key (cached). + * + * @returns SHA-256 digest of the cipher key. + */ + getKey() { + if (!this.key) { + this.key = AesGcmCryptor.primitives.sha256(AesGcmCryptor.encoder.encode(this.cipherKey)); + this.key.catch(() => (this.key = undefined)); + } + return this.key; + } + // endregion + /** + * Serialize cryptor information to string. + * + * @returns Serialized cryptor information. + */ + toString() { + return 'AesGcmCryptor {}'; + } +} +/** + * Cryptor unique identifier. + */ +AesGcmCryptor.IDENTIFIER = 'ACGM'; +/** + * Nonce length. + */ +AesGcmCryptor.NONCE_LENGTH = 12; +/** + * Authentication tag length. + */ +AesGcmCryptor.TAG_LENGTH = 16; +/** + * Cryptor metadata length (nonce only). + */ +AesGcmCryptor.METADATA_LENGTH = AesGcmCryptor.NONCE_LENGTH; +/** + * Generic decryption error message (doesn't reveal which part of the payload failed authentication). + */ +AesGcmCryptor.DECRYPTION_ERROR = 'Decryption error: unable to decrypt data'; +/** + * Unsupported encryption error message. + */ +AesGcmCryptor.ENCRYPTION_ERROR = 'Encryption error: AES-GCM cryptor (ACGM) supports decryption only.'; +/** + * {@link string|String} to {@link ArrayBuffer} response decoder. + */ +AesGcmCryptor.encoder = new TextEncoder(); +/** + * Native crypto primitives. + */ +AesGcmCryptor.primitives = new node_1.default(); +exports.default = AesGcmCryptor; diff --git a/lib/crypto/modules/NodeCryptoModule/legacyCryptor.js b/lib/crypto/modules/NodeCryptoModule/legacyCryptor.js index 8e446b76a..773753556 100644 --- a/lib/crypto/modules/NodeCryptoModule/legacyCryptor.js +++ b/lib/crypto/modules/NodeCryptoModule/legacyCryptor.js @@ -21,7 +21,8 @@ const index_1 = __importDefault(require("../../../core/components/cryptography/i const base64_codec_1 = require("../../../core/components/base64_codec"); const pubnub_error_1 = require("../../../errors/pubnub-error"); const utils_1 = require("../../../core/utils"); -const node_1 = __importDefault(require("../node")); +const node_1 = __importDefault(require("../../primitives/node")); +const node_2 = __importDefault(require("../node")); /** * Legacy cryptor. * @@ -30,8 +31,8 @@ const node_1 = __importDefault(require("../node")); class LegacyCryptor { constructor(config) { this.config = config; - this.cryptor = new index_1.default(Object.assign({}, config)); - this.fileCryptor = new node_1.default(); + this.cryptor = new index_1.default(Object.assign({}, config), new node_1.default()); + this.fileCryptor = new node_2.default(); } /** * Update registered loggers' manager. @@ -50,12 +51,14 @@ class LegacyCryptor { // -------------------------------------------------------- // region Encryption encrypt(data) { - if (data.length === 0) - throw new Error('Encryption error: empty content'); - return { - data: this.cryptor.encrypt(data), - metadata: null, - }; + return __awaiter(this, void 0, void 0, function* () { + if (data.length === 0) + throw new Error('Encryption error: empty content'); + return { + data: yield this.cryptor.encrypt(data), + metadata: null, + }; + }); } encryptFile(file, File) { return __awaiter(this, void 0, void 0, function* () { @@ -70,8 +73,10 @@ class LegacyCryptor { // -------------------------------------------------------- // region Decryption decrypt(encryptedData) { - const data = typeof encryptedData.data === 'string' ? encryptedData.data : (0, base64_codec_1.encode)(encryptedData.data); - return this.cryptor.decrypt(data); + return __awaiter(this, void 0, void 0, function* () { + const data = typeof encryptedData.data === 'string' ? encryptedData.data : (0, base64_codec_1.encode)(encryptedData.data); + return this.cryptor.decrypt(data); + }); } decryptFile(file, File) { return __awaiter(this, void 0, void 0, function* () { diff --git a/lib/crypto/modules/NodeCryptoModule/nodeCryptoModule.js b/lib/crypto/modules/NodeCryptoModule/nodeCryptoModule.js index 3a410f02b..17ae847ef 100644 --- a/lib/crypto/modules/NodeCryptoModule/nodeCryptoModule.js +++ b/lib/crypto/modules/NodeCryptoModule/nodeCryptoModule.js @@ -15,7 +15,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.NodeCryptoModule = exports.AesCbcCryptor = exports.LegacyCryptor = void 0; +exports.NodeCryptoModule = exports.AesGcmCryptor = exports.AesCbcCryptor = exports.LegacyCryptor = void 0; const stream_1 = require("stream"); const buffer_1 = require("buffer"); const crypto_module_1 = require("../../../core/interfaces/crypto-module"); @@ -23,6 +23,8 @@ const base64_codec_1 = require("../../../core/components/base64_codec"); const pubnub_error_1 = require("../../../errors/pubnub-error"); const aesCbcCryptor_1 = __importDefault(require("./aesCbcCryptor")); exports.AesCbcCryptor = aesCbcCryptor_1.default; +const aesGcmCryptor_1 = __importDefault(require("./aesGcmCryptor")); +exports.AesGcmCryptor = aesGcmCryptor_1.default; const legacyCryptor_1 = __importDefault(require("./legacyCryptor")); exports.LegacyCryptor = legacyCryptor_1.default; /** @@ -70,7 +72,10 @@ class NodeCryptoModule extends crypto_module_1.AbstractCryptoModule { } return new this({ default: new legacyCryptor_1.default(Object.assign(Object.assign({}, config), { useRandomIVs: (_a = config.useRandomIVs) !== null && _a !== void 0 ? _a : true })), - cryptors: [new aesCbcCryptor_1.default({ cipherKey: config.cipherKey })], + cryptors: [ + new aesCbcCryptor_1.default({ cipherKey: config.cipherKey }), + new aesGcmCryptor_1.default({ cipherKey: config.cipherKey }), + ], }); } static aesCbcCryptoModule(config) { @@ -81,9 +86,35 @@ class NodeCryptoModule extends crypto_module_1.AbstractCryptoModule { default: new aesCbcCryptor_1.default({ cipherKey: config.cipherKey }), cryptors: [ new legacyCryptor_1.default(Object.assign(Object.assign({}, config), { useRandomIVs: (_a = config.useRandomIVs) !== null && _a !== void 0 ? _a : true })), + new aesGcmCryptor_1.default({ cipherKey: config.cipherKey }), ], }); } + /** + * Construct decrypt-only AES-GCM cryptor. + * + * Modules created with {@link aesCbcCryptoModule}, {@link legacyCryptoModule} or `cipherKey` already register it. + * Use it to let a custom crypto module decrypt AES-GCM (`ACGM`) payloads produced by other PubNub SDKs: + * + * @example + * ```typescript + * const cryptoModule = new PubNub.CryptoModule({ + * default: myCryptor, + * cryptors: [PubNub.CryptoModule.aesGcmCryptor({ cipherKey: 'my-key' })], + * }); + * ``` + * + * @param config - Cryptor configuration options. + * + * @returns AES-GCM cryptor which can be used only as an additional (decryption) cryptor. + * + * @throws Error if `config.cipherKey` not set. + */ + static aesGcmCryptor(config) { + if (!config.cipherKey) + throw new pubnub_error_1.PubNubError('Crypto module error: cipher key not set.'); + return new aesGcmCryptor_1.default({ cipherKey: config.cipherKey }); + } /** * Construct crypto module with `cryptor` as default for data encryption and decryption. * @@ -100,18 +131,20 @@ class NodeCryptoModule extends crypto_module_1.AbstractCryptoModule { // -------------------------------------------------------- // region Encryption encrypt(data) { - // Encrypt data. - const encrypted = data instanceof ArrayBuffer && this.defaultCryptor.identifier === NodeCryptoModule.LEGACY_IDENTIFIER - ? this.defaultCryptor.encrypt(NodeCryptoModule.decoder.decode(data)) - : this.defaultCryptor.encrypt(data); - if (!encrypted.metadata) - return encrypted.data; - const headerData = this.getHeaderData(encrypted); - // Write encrypted data payload content. - const encryptedData = typeof encrypted.data === 'string' - ? NodeCryptoModule.encoder.encode(encrypted.data).buffer - : encrypted.data.buffer.slice(encrypted.data.byteOffset, encrypted.data.byteOffset + encrypted.data.length); - return this.concatArrayBuffer(headerData, encryptedData); + return __awaiter(this, void 0, void 0, function* () { + // Encrypt data. + const encrypted = data instanceof ArrayBuffer && this.defaultCryptor.identifier === NodeCryptoModule.LEGACY_IDENTIFIER + ? yield this.defaultCryptor.encrypt(NodeCryptoModule.decoder.decode(data)) + : yield this.defaultCryptor.encrypt(data); + if (!encrypted.metadata) + return encrypted.data; + const headerData = this.getHeaderData(encrypted); + // Write encrypted data payload content. + const encryptedData = typeof encrypted.data === 'string' + ? NodeCryptoModule.encoder.encode(encrypted.data).buffer + : encrypted.data.buffer.slice(encrypted.data.byteOffset, encrypted.data.byteOffset + encrypted.data.length); + return this.concatArrayBuffer(headerData, encryptedData); + }); } encryptFile(file, File) { return __awaiter(this, void 0, void 0, function* () { @@ -122,7 +155,7 @@ class NodeCryptoModule extends crypto_module_1.AbstractCryptoModule { if (this.defaultCryptor.identifier === CryptorHeader.LEGACY_IDENTIFIER) return this.defaultCryptor.encryptFile(file, File); if (file.data instanceof buffer_1.Buffer) { - const encryptedData = this.encrypt(file.data); + const encryptedData = yield this.encrypt(file.data); return File.create({ name: file.name, mimeType: 'application/octet-stream', @@ -160,17 +193,19 @@ class NodeCryptoModule extends crypto_module_1.AbstractCryptoModule { // -------------------------------------------------------- // region Decryption decrypt(data) { - const encryptedData = buffer_1.Buffer.from(typeof data === 'string' ? (0, base64_codec_1.decode)(data) : data); - const header = CryptorHeader.tryParse(encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length)); - const cryptor = this.getCryptor(header); - const metadata = header.length > 0 - ? encryptedData.slice(header.length - header.metadataLength, header.length) - : null; - if (encryptedData.slice(header.length).byteLength <= 0) - throw new Error('Decryption error: empty content'); - return cryptor.decrypt({ - data: encryptedData.slice(header.length), - metadata: metadata, + return __awaiter(this, void 0, void 0, function* () { + const encryptedData = buffer_1.Buffer.from(typeof data === 'string' ? (0, base64_codec_1.decode)(data) : data); + const header = CryptorHeader.tryParse(encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length)); + const cryptor = this.getCryptor(header); + const metadata = header.length > 0 + ? encryptedData.slice(header.length - header.metadataLength, header.length) + : null; + if (encryptedData.slice(header.length).byteLength <= 0) + throw new Error('Decryption error: empty content'); + return cryptor.decrypt({ + data: encryptedData.slice(header.length), + metadata: metadata, + }); }); } decryptFile(file, File) { @@ -186,7 +221,7 @@ class NodeCryptoModule extends crypto_module_1.AbstractCryptoModule { return cryptor.decryptFile(file, File); return File.create({ name: file.name, - data: buffer_1.Buffer.from(this.decrypt(file.data)), + data: buffer_1.Buffer.from((yield this.decrypt(file.data))), }); } if (file.data && file.data instanceof stream_1.Readable) { diff --git a/lib/crypto/primitives/node.js b/lib/crypto/primitives/node.js new file mode 100644 index 000000000..a1e574d0a --- /dev/null +++ b/lib/crypto/primitives/node.js @@ -0,0 +1,99 @@ +"use strict"; +/** + * Node.js cryptographic primitives backed by `node:crypto`. + * + * @internal + */ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.hmacSha256 = void 0; +const crypto_1 = require("crypto"); +/** + * Compute HMAC-SHA256 (used for request signature). + * + * @param data - Data for which HMAC should be computed. + * @param secretKey - HMAC secret key. + * + * @returns Base64 encoded HMAC-SHA256 digest. + * + * @internal + */ +const hmacSha256 = (data, secretKey) => (0, crypto_1.createHmac)('sha256', secretKey).update(data, 'utf8').digest('base64'); +exports.hmacSha256 = hmacSha256; +/** + * Node.js cryptographic primitives. + * + * @internal + */ +class NodeCryptoPrimitives { + sha256(data) { + return __awaiter(this, void 0, void 0, function* () { + return new Uint8Array((0, crypto_1.createHash)('sha256').update(data).digest()); + }); + } + aesCbcEncrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + const cipher = (0, crypto_1.createCipheriv)(this.algorithm(key), key, iv); + return new Uint8Array(Buffer.concat([cipher.update(data), cipher.final()])); + }); + } + aesCbcDecrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + const decipher = (0, crypto_1.createDecipheriv)(this.algorithm(key), key, iv); + return new Uint8Array(Buffer.concat([decipher.update(data), decipher.final()])); + }); + } + /** + * Decrypt and authenticate data using AES-256-GCM (128-bit tag, no additional authenticated data). + * + * Used only by the decrypt-only `ACGM` cryptor (Web and Node.js). Intentionally not part of + * {@link CryptoPrimitives}: React Native supports only the legacy cryptor. + * + * @param key - 32 bytes long raw key. + * @param nonce - 12 bytes long nonce. + * @param data - Data which should be decrypted, with 16 bytes long authentication tag appended to the ciphertext. + * + * @returns Decrypted data (returned only after the authentication tag has been verified). + * + * @throws Error if data can't be authenticated (for example: wrong key or modified nonce, ciphertext or tag). + */ + aesGcmDecrypt(key, nonce, data) { + return __awaiter(this, void 0, void 0, function* () { + if (data.byteLength < 16) + throw new Error('Decryption error: authentication tag is missing'); + const decipher = (0, crypto_1.createDecipheriv)('aes-256-gcm', key, nonce, { authTagLength: 16 }); + decipher.setAuthTag(data.subarray(data.byteLength - 16)); + // `update` returns not yet authenticated data: release it only after `final` verified the tag. + const decrypted = decipher.update(data.subarray(0, data.byteLength - 16)); + try { + return new Uint8Array(Buffer.concat([decrypted, decipher.final()])); + } + catch (error) { + decrypted.fill(0); + throw error; + } + }); + } + randomBytes(length) { + return new Uint8Array((0, crypto_1.randomBytes)(length)); + } + /** + * AES-CBC algorithm name for provided key size. + * + * @param key - Raw key bytes. + * + * @returns `node:crypto` cipher name. + */ + algorithm(key) { + return `aes-${key.byteLength * 8}-cbc`; + } +} +exports.default = NodeCryptoPrimitives; diff --git a/lib/crypto/primitives/react-native.js b/lib/crypto/primitives/react-native.js new file mode 100644 index 000000000..feb0d3d59 --- /dev/null +++ b/lib/crypto/primitives/react-native.js @@ -0,0 +1,76 @@ +"use strict"; +/** + * React Native cryptographic primitives backed by `@noble/ciphers` and `@noble/hashes`. + * + * Hermes doesn't provide `crypto.subtle`, so pure JS audited implementations are used. This module is imported + * only by the React Native entry point, so `@noble/*` never becomes part of Web or Node.js builds. Modules are + * loaded lazily on first encryption / decryption, so apps which don't use encryption don't evaluate them. + * + * @internal + */ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const pubnub_error_1 = require("../../errors/pubnub-error"); +/** + * Loaded `@noble/*` modules. + */ +let noble; +/** + * Lazily load `@noble/*` packages. + * + * @returns Loaded modules. + * + * @throws PubNubError if packages can't be loaded (for example, excluded from the app bundle). + */ +const loadNoble = () => { + if (noble) + return noble; + try { + /* eslint-disable @typescript-eslint/no-require-imports */ + noble = { aes: require('@noble/ciphers/aes.js'), sha2: require('@noble/hashes/sha2.js') }; + /* eslint-enable @typescript-eslint/no-require-imports */ + } + catch (_error) { + throw new pubnub_error_1.PubNubError("React Native encryption requires '@noble/ciphers' and '@noble/hashes'. " + + 'Install them: npm install @noble/ciphers @noble/hashes'); + } + return noble; +}; +/** + * React Native cryptographic primitives. + * + * @internal + */ +class NobleCryptoPrimitives { + sha256(data) { + return __awaiter(this, void 0, void 0, function* () { + return loadNoble().sha2.sha256(data); + }); + } + aesCbcEncrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + return loadNoble().aes.cbc(key, iv).encrypt(data); + }); + } + aesCbcDecrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + return loadNoble().aes.cbc(key, iv).decrypt(data); + }); + } + randomBytes(length) { + const cryptoApi = globalThis.crypto; + if (!cryptoApi || typeof cryptoApi.getRandomValues !== 'function') + throw new pubnub_error_1.PubNubError("Crypto error: secure random number generator unavailable. Install 'react-native-get-random-values' " + + 'and import it at the top of your app entry file.'); + return cryptoApi.getRandomValues(new Uint8Array(length)); + } +} +exports.default = NobleCryptoPrimitives; diff --git a/lib/crypto/primitives/web.js b/lib/crypto/primitives/web.js new file mode 100644 index 000000000..ece7c50ff --- /dev/null +++ b/lib/crypto/primitives/web.js @@ -0,0 +1,110 @@ +"use strict"; +/** + * Browser cryptographic primitives backed by `crypto.subtle`. + * + * @internal + */ +/* global crypto */ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const bytes_1 = require("../../core/components/cryptography/bytes"); +const pubnub_error_1 = require("../../errors/pubnub-error"); +/** + * Browser cryptographic primitives. + * + * @internal + */ +class WebCryptoPrimitives { + constructor() { + /** + * Imported keys cache (keyed by the algorithm and hex representation of the raw key). + */ + this.keys = new Map(); + } + sha256(data) { + return __awaiter(this, void 0, void 0, function* () { + return new Uint8Array(yield this.subtle.digest('SHA-256', data)); + }); + } + aesCbcEncrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + const cryptoKey = yield this.importKey(key, 'AES-CBC'); + return new Uint8Array(yield this.subtle.encrypt({ name: 'AES-CBC', iv }, cryptoKey, data)); + }); + } + aesCbcDecrypt(key, iv, data) { + return __awaiter(this, void 0, void 0, function* () { + const cryptoKey = yield this.importKey(key, 'AES-CBC'); + return new Uint8Array(yield this.subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, data)); + }); + } + /** + * Decrypt and authenticate data using AES-256-GCM (128-bit tag, no additional authenticated data). + * + * Used only by the decrypt-only `ACGM` cryptor (Web and Node.js). Intentionally not part of + * {@link CryptoPrimitives}: React Native supports only the legacy cryptor. + * + * @param key - 32 bytes long raw key. + * @param nonce - 12 bytes long nonce. + * @param data - Data which should be decrypted, with 16 bytes long authentication tag appended to the ciphertext. + * + * @returns Decrypted data (returned only after the authentication tag has been verified). + * + * @throws Error if data can't be authenticated (for example: wrong key or modified nonce, ciphertext or tag). + */ + aesGcmDecrypt(key, nonce, data) { + return __awaiter(this, void 0, void 0, function* () { + const cryptoKey = yield this.importKey(key, 'AES-GCM'); + return new Uint8Array(yield this.subtle.decrypt({ name: 'AES-GCM', iv: nonce, tagLength: 128 }, cryptoKey, data)); + }); + } + randomBytes(length) { + return crypto.getRandomValues(new Uint8Array(length)); + } + /** + * Retrieve `SubtleCrypto` interface. + * + * @returns `SubtleCrypto` interface. + * + * @throws PubNubError if `crypto.subtle` isn't available (non-secure context). + */ + get subtle() { + if (typeof crypto === 'undefined' || !crypto.subtle) + throw new pubnub_error_1.PubNubError('Crypto error: crypto.subtle is unavailable. Encryption requires a secure context (HTTPS or localhost).'); + return crypto.subtle; + } + /** + * Import raw AES key (cached, least recently imported keys evicted first). + * + * @param key - Raw key bytes. + * @param algorithm - Algorithm for which key will be used. + * + * @returns Imported `CryptoKey`. + */ + importKey(key, algorithm) { + const id = `${algorithm}:${(0, bytes_1.bytesToHex)(key)}`; + let cryptoKey = this.keys.get(id); + if (!cryptoKey) { + if (this.keys.size >= WebCryptoPrimitives.MAX_CACHED_KEYS) + this.keys.delete(this.keys.keys().next().value); + const usages = algorithm === 'AES-CBC' ? ['encrypt', 'decrypt'] : ['decrypt']; + cryptoKey = this.subtle.importKey('raw', key, algorithm, false, usages); + cryptoKey.catch(() => this.keys.delete(id)); + this.keys.set(id, cryptoKey); + } + return cryptoKey; + } +} +/** + * Maximum number of imported keys kept in cache. + */ +WebCryptoPrimitives.MAX_CACHED_KEYS = 8; +exports.default = WebCryptoPrimitives; diff --git a/lib/node/index.js b/lib/node/index.js index 3d52c5667..26e176faf 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -1,4 +1,37 @@ "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -14,6 +47,7 @@ const middleware_1 = require("../transport/middleware"); const base64_codec_1 = require("../core/components/base64_codec"); const node_2 = __importDefault(require("../crypto/modules/node")); const cryptography_1 = __importDefault(require("../core/components/cryptography")); +const node_3 = __importStar(require("../crypto/primitives/node")); const pubnub_error_1 = require("../errors/pubnub-error"); const pubnub_common_1 = require("../core/pubnub-common"); const common_1 = __importDefault(require("../cbor/common")); @@ -40,7 +74,10 @@ class PubNub extends pubnub_common_1.PubNubCore { if (process.env.CRYPTO_MODULE !== 'disabled') { return new nodeCryptoModule_1.NodeCryptoModule({ default: new nodeCryptoModule_1.LegacyCryptor(Object.assign(Object.assign({}, cryptoConfiguration), (!cryptoConfiguration.logger ? { logger: clientConfiguration.logger() } : {}))), - cryptors: [new nodeCryptoModule_1.AesCbcCryptor({ cipherKey: cryptoConfiguration.cipherKey })], + cryptors: [ + new nodeCryptoModule_1.AesCbcCryptor({ cipherKey: cryptoConfiguration.cipherKey }), + new nodeCryptoModule_1.AesGcmCryptor({ cipherKey: cryptoConfiguration.cipherKey }), + ], }); } else @@ -66,7 +103,7 @@ class PubNub extends pubnub_common_1.PubNubCore { customEncrypt: clientConfiguration.getCustomEncrypt(), customDecrypt: clientConfiguration.getCustomDecrypt(), logger: clientConfiguration.logger(), - }); + }, new node_3.default()); } let cryptography; if (process.env.CRYPTO_MODULE !== 'disabled') @@ -77,7 +114,7 @@ class PubNub extends pubnub_common_1.PubNubCore { clientConfiguration, tokenManager, transport, - shaHMAC: process.env.CRYPTO_MODULE !== 'disabled' ? crypto === null || crypto === void 0 ? void 0 : crypto.HMACSHA256.bind(crypto) : undefined, + shaHMAC: process.env.CRYPTO_MODULE !== 'disabled' ? node_3.hmacSha256 : undefined, }); super({ configuration: clientConfiguration, diff --git a/lib/react_native/index.js b/lib/react_native/index.js index 18f7800d1..a6bf889e5 100644 --- a/lib/react_native/index.js +++ b/lib/react_native/index.js @@ -16,6 +16,7 @@ const base64_codec_1 = require("../core/components/base64_codec"); const react_native_1 = __importDefault(require("../file/modules/react-native")); const cryptography_1 = __importDefault(require("../core/components/cryptography")); const LegacyCryptoModule_1 = __importDefault(require("../crypto/modules/LegacyCryptoModule")); +const react_native_2 = __importDefault(require("../crypto/primitives/react-native")); const pubnub_common_1 = require("../core/pubnub-common"); const configuration_2 = require("./configuration"); const common_1 = __importDefault(require("../cbor/common")); @@ -50,7 +51,7 @@ class PubNub extends pubnub_common_1.PubNubCore { customEncrypt: platformConfiguration.customEncrypt, customDecrypt: platformConfiguration.customDecrypt, logger: cryptoConfiguration.logger, - }); + }, new react_native_2.default()); return new LegacyCryptoModule_1.default(legacy); } return undefined; @@ -71,7 +72,7 @@ class PubNub extends pubnub_common_1.PubNubCore { customEncrypt: clientConfiguration.getCustomEncrypt(), customDecrypt: clientConfiguration.getCustomDecrypt(), logger: clientConfiguration.logger(), - }); + }, new react_native_2.default()); } } // Setup transport layer. diff --git a/lib/types/index.d.ts b/lib/types/index.d.ts index 3c45e86a4..dfadc523d 100644 --- a/lib/types/index.d.ts +++ b/lib/types/index.d.ts @@ -1483,11 +1483,11 @@ declare class PubNubCore< * @param [customCipherKey] - Cipher key which should be used to encrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data encryption result as a string. + * @returns Asynchronous data encryption result as a string. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ - encrypt(data: string | PubNub.Payload, customCipherKey?: string): string; + encrypt(data: string | PubNub.Payload, customCipherKey?: string): Promise; /** * Decrypt data. * @@ -1495,11 +1495,11 @@ declare class PubNubCore< * @param [customCipherKey] - Cipher key which should be used to decrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data decryption result as an object. + * @returns Asynchronous data decryption result as an object. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ - decrypt(data: string, customCipherKey?: string): PubNub.Payload | null; + decrypt(data: string, customCipherKey?: string): Promise; /** * Encrypt file content. * @@ -1585,6 +1585,27 @@ declare namespace PubNub { */ static legacyCryptoModule(config: CryptorConfiguration): NodeCryptoModule; static aesCbcCryptoModule(config: CryptorConfiguration): NodeCryptoModule; + /** + * Construct decrypt-only AES-GCM cryptor. + * + * Modules created with {@link aesCbcCryptoModule}, {@link legacyCryptoModule} or `cipherKey` already register it. + * Use it to let a custom crypto module decrypt AES-GCM (`ACGM`) payloads produced by other PubNub SDKs: + * + * @example + * ```typescript + * const cryptoModule = new PubNub.CryptoModule({ + * default: myCryptor, + * cryptors: [PubNub.CryptoModule.aesGcmCryptor({ cipherKey: 'my-key' })], + * }); + * ``` + * + * @param config - Cryptor configuration options. + * + * @returns AES-GCM cryptor which can be used only as an additional (decryption) cryptor. + * + * @throws Error if `config.cipherKey` not set. + */ + static aesGcmCryptor(config: CryptorConfiguration): ICryptor; /** * Construct crypto module with `cryptor` as default for data encryption and decryption. * @@ -1593,12 +1614,12 @@ declare namespace PubNub { * @returns Crypto module with pre-configured default cryptor. */ static withDefaultCryptor(defaultCryptor: CryptorType): NodeCryptoModule; - encrypt(data: ArrayBuffer | string): string | ArrayBuffer; + encrypt(data: ArrayBuffer | string): Promise; encryptFile( file: PubNubFile, File: PubNubFileConstructor, ): Promise; - decrypt(data: ArrayBuffer | string): ArrayBuffer | Payload | null; + decrypt(data: ArrayBuffer | string): Promise; decryptFile( file: PubNubFile, File: PubNubFileConstructor, @@ -1642,13 +1663,13 @@ declare namespace PubNub { * * @deprecated Instead use {@link cryptoModule} for data encryption. */ - customEncrypt?: (data: string | Payload) => string; + customEncrypt?: (data: string | Payload) => MaybePromise; /** * Custom data decryption method. * * @deprecated Instead use {@link cryptoModule} for data decryption. */ - customDecrypt?: (data: string) => string; + customDecrypt?: (data: string) => MaybePromise; }; /** @@ -1660,9 +1681,9 @@ declare namespace PubNub { * * @param data - Data which should be encrypted using `CryptoModule`. * - * @returns Data encryption result. + * @returns Asynchronous data encryption result. */ - encrypt(data: ArrayBuffer | string): ArrayBuffer | string; + encrypt(data: ArrayBuffer | string): Promise; /** * Encrypt file object. * @@ -1680,9 +1701,9 @@ declare namespace PubNub { * * @param data - Dta which should be encrypted using `CryptoModule`. * - * @returns Data decryption result. + * @returns Asynchronous data decryption result. */ - decrypt(data: ArrayBuffer | string): ArrayBuffer | Payload | null; + decrypt(data: ArrayBuffer | string): Promise; /** * Decrypt file object. * @@ -1730,9 +1751,9 @@ declare namespace PubNub { * * @param data - Data which should be encrypted using {@link ICryptoModule}. * - * @returns Data encryption result. + * @returns Asynchronous data encryption result. */ - abstract encrypt(data: ArrayBuffer | string): ArrayBuffer | string; + abstract encrypt(data: ArrayBuffer | string): Promise; /** * Encrypt file object. * @@ -1750,9 +1771,9 @@ declare namespace PubNub { * * @param data - Dta which should be encrypted using `ICryptoModule`. * - * @returns Data decryption result. + * @returns Asynchronous data decryption result. */ - abstract decrypt(data: ArrayBuffer | string): ArrayBuffer | Payload | null; + abstract decrypt(data: ArrayBuffer | string): Promise; /** * Decrypt file object. * @@ -2411,6 +2432,11 @@ declare namespace PubNub { PNSharedWorkerUpdatedCategory = 'PNSharedWorkerUpdatedCategory', } + /** + * Value which can be returned synchronously or asynchronously. + */ + export type MaybePromise = T | Promise; + /** * PubNub File instance creation parameters. */ @@ -2590,7 +2616,7 @@ declare namespace PubNub { * * @throws Error if unknown data type has been passed. */ - encrypt(data: BufferSource | string): EncryptedDataType; + encrypt(data: BufferSource | string): MaybePromise; /** * Encrypt provided source {@link Readable} stream. * @@ -2610,7 +2636,7 @@ declare namespace PubNub { * * @throws Error if unknown data type has been passed. */ - decrypt(data: EncryptedDataType): ArrayBuffer; + decrypt(data: EncryptedDataType): MaybePromise; /** * Decrypt provided encrypted stream object. * @@ -2640,7 +2666,7 @@ declare namespace PubNub { * * @throws Error if unknown data type has been passed. */ - encrypt(data: string): EncryptedDataType; + encrypt(data: string): MaybePromise; /** * Encrypt provided source {@link PubNub} File object. * @@ -2665,7 +2691,7 @@ declare namespace PubNub { * * @throws Error if unknown data type has been passed. */ - decrypt(data: EncryptedDataType): Payload | null; + decrypt(data: EncryptedDataType): MaybePromise; /** * Decrypt provided encrypted {@link PubNub} File object. * diff --git a/package-lock.json b/package-lock.json index f393329b7..69ed40128 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pubnub", - "version": "13.1.2", + "version": "13.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pubnub", - "version": "13.1.2", + "version": "13.1.3", "license": "SEE LICENSE IN LICENSE", "dependencies": { "agentkeepalive": "^3.5.2", @@ -19,6 +19,8 @@ "undici": "^7.30.1" }, "devDependencies": { + "@noble/ciphers": "^2.4.0", + "@noble/hashes": "^2.4.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", @@ -87,9 +89,17 @@ "node": ">=22.0.0" }, "peerDependencies": { + "@noble/ciphers": "^2.4.0", + "@noble/hashes": "^2.4.0", "react-native-url-polyfill": "^2.0.0" }, "peerDependenciesMeta": { + "@noble/ciphers": { + "optional": true + }, + "@noble/hashes": { + "optional": true + }, "react-native-url-polyfill": { "optional": true } @@ -3686,6 +3696,32 @@ "node": ">=18" } }, + "node_modules/@noble/ciphers": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.4.0.tgz", + "integrity": "sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index 40e5b5112..0124a2d70 100644 --- a/package.json +++ b/package.json @@ -69,9 +69,17 @@ "undici": "^7.30.1" }, "peerDependencies": { + "@noble/ciphers": "^2.4.0", + "@noble/hashes": "^2.4.0", "react-native-url-polyfill": "^2.0.0" }, "peerDependenciesMeta": { + "@noble/ciphers": { + "optional": true + }, + "@noble/hashes": { + "optional": true + }, "react-native-url-polyfill": { "optional": true } @@ -80,6 +88,8 @@ "basic-ftp": "5.3.1" }, "devDependencies": { + "@noble/ciphers": "^2.4.0", + "@noble/hashes": "^2.4.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", diff --git a/src/core/components/cryptography/bytes.ts b/src/core/components/cryptography/bytes.ts new file mode 100644 index 000000000..b2d16d131 --- /dev/null +++ b/src/core/components/cryptography/bytes.ts @@ -0,0 +1,128 @@ +/** + * Byte helpers shared by cryptors and crypto primitives. + * + * @internal + */ + +/** + * Lazily created UTF-8 decoder. + * + * **Note:** Not created at module load and doesn't use `fatal` option because some polyfills (for example + * `fast-text-encoding` used on React Native) throw when it is requested. Input is validated by + * {@link isWellFormedUtf8} instead, so the decoder only ever receives well-formed data. + */ +let decoder: TextDecoder | undefined; + +/** + * Whether {@link decoder} strips a leading BOM despite `ignoreBOM` option (detected once, on first use). + */ +let decoderStripsBOM = false; + +/** + * Convert bytes to lowercase HEX string. + * + * @param bytes - Source bytes. + * + * @returns HEX encoded bytes. + * + * @internal + */ +export const bytesToHex = (bytes: Uint8Array) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + +/** + * Copy bytes into standalone {@link ArrayBuffer}. + * + * @param bytes - Source bytes (may be a view into a larger buffer). + * + * @returns {@link ArrayBuffer} which contains only source bytes. + * + * @internal + */ +export const toArrayBuffer = (bytes: Uint8Array): ArrayBuffer => + bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer; + +/** + * Check whether bytes are well-formed UTF-8. + * + * Implements Unicode "Well-Formed UTF-8 Byte Sequences" (Table 3-7): rejects stray continuation bytes, overlong + * forms (`C0`, `C1`, `E0 80-9F`, `F0 80-8F`), surrogates (`ED A0-BF`), code points above U+10FFFF (`F4 90-BF`, + * `F5-FF`) and truncated sequences. Same rules as CryptoJS `Utf8.stringify` (`decodeURIComponent`). + * + * @param bytes - Bytes which should be checked. + * + * @returns `true` if bytes are well-formed UTF-8. + * + * @internal + */ +export const isWellFormedUtf8 = (bytes: Uint8Array): boolean => { + const length = bytes.length; + let index = 0; + + while (index < length) { + const lead = bytes[index]; + + if (lead < 0x80) { + index += 1; + continue; + } + + let continuationBytes: number; + // Allowed range of the first continuation byte (narrower for some lead bytes). + let lower = 0x80; + let upper = 0xbf; + + if (lead >= 0xc2 && lead <= 0xdf) continuationBytes = 1; + else if (lead >= 0xe0 && lead <= 0xef) { + continuationBytes = 2; + if (lead === 0xe0) lower = 0xa0; + else if (lead === 0xed) upper = 0x9f; + } else if (lead >= 0xf0 && lead <= 0xf4) { + continuationBytes = 3; + if (lead === 0xf0) lower = 0x90; + else if (lead === 0xf4) upper = 0x8f; + } else return false; + + if (index + continuationBytes >= length) return false; + + const first = bytes[index + 1]; + if (first < lower || first > upper) return false; + + for (let offset = 2; offset <= continuationBytes; offset++) { + const next = bytes[index + offset]; + if (next < 0x80 || next > 0xbf) return false; + } + + index += continuationBytes + 1; + } + + return true; +}; + +/** + * Strictly decode UTF-8 bytes. + * + * Behaves like CryptoJS `Utf8.stringify` used by previous SDK versions: malformed sequences are rejected instead of + * being replaced, and a leading BOM is preserved (regular decoders silently strip it). Result doesn't depend on the + * platform `TextDecoder` implementation or polyfill. + * + * @param bytes - UTF-8 encoded bytes. + * + * @returns Decoded string. + * + * @throws Error if bytes aren't a valid UTF-8 sequence. + * + * @internal + */ +export const decodeUtf8Strict = (bytes: Uint8Array): string => { + if (!isWellFormedUtf8(bytes)) throw new Error('Malformed UTF-8 data'); + + if (!decoder) { + decoder = new TextDecoder('utf-8', { ignoreBOM: true }); + decoderStripsBOM = decoder.decode(new Uint8Array([0xef, 0xbb, 0xbf])).length === 0; + } + + const decoded = decoder.decode(bytes); + const hasBOM = bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf; + + return hasBOM && decoderStripsBOM ? `\uFEFF${decoded}` : decoded; +}; diff --git a/src/core/components/cryptography/hmac-sha256.js b/src/core/components/cryptography/hmac-sha256.js deleted file mode 100755 index b0e785b15..000000000 --- a/src/core/components/cryptography/hmac-sha256.js +++ /dev/null @@ -1,915 +0,0 @@ -/** - * CryptoJS implementation. - * - * @internal - */ - -/*eslint-disable */ - -/* - CryptoJS v3.1.2 - code.google.com/p/crypto-js - (c) 2009-2013 by Jeff Mott. All rights reserved. - code.google.com/p/crypto-js/wiki/License - */ -var CryptoJS = - CryptoJS || - (function (h, s) { - var f = {}, - g = (f.lib = {}), - q = function () {}, - m = (g.Base = { - extend: function (a) { - q.prototype = this; - var c = new q(); - a && c.mixIn(a); - c.hasOwnProperty('init') || - (c.init = function () { - c.$super.init.apply(this, arguments); - }); - c.init.prototype = c; - c.$super = this; - return c; - }, - create: function () { - var a = this.extend(); - a.init.apply(a, arguments); - return a; - }, - init: function () {}, - mixIn: function (a) { - for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]); - a.hasOwnProperty('toString') && (this.toString = a.toString); - }, - clone: function () { - return this.init.prototype.extend(this); - }, - }), - r = (g.WordArray = m.extend({ - init: function (a, c) { - a = this.words = a || []; - this.sigBytes = c != s ? c : 4 * a.length; - }, - toString: function (a) { - return (a || k).stringify(this); - }, - concat: function (a) { - var c = this.words, - d = a.words, - b = this.sigBytes; - a = a.sigBytes; - this.clamp(); - if (b % 4) - for (var e = 0; e < a; e++) - c[(b + e) >>> 2] |= ((d[e >>> 2] >>> (24 - 8 * (e % 4))) & 255) << (24 - 8 * ((b + e) % 4)); - else if (65535 < d.length) for (e = 0; e < a; e += 4) c[(b + e) >>> 2] = d[e >>> 2]; - else c.push.apply(c, d); - this.sigBytes += a; - return this; - }, - clamp: function () { - var a = this.words, - c = this.sigBytes; - a[c >>> 2] &= 4294967295 << (32 - 8 * (c % 4)); - a.length = h.ceil(c / 4); - }, - clone: function () { - var a = m.clone.call(this); - a.words = this.words.slice(0); - return a; - }, - random: function (a) { - for (var c = [], d = 0; d < a; d += 4) c.push((4294967296 * h.random()) | 0); - return new r.init(c, a); - }, - })), - l = (f.enc = {}), - k = (l.Hex = { - stringify: function (a) { - var c = a.words; - a = a.sigBytes; - for (var d = [], b = 0; b < a; b++) { - var e = (c[b >>> 2] >>> (24 - 8 * (b % 4))) & 255; - d.push((e >>> 4).toString(16)); - d.push((e & 15).toString(16)); - } - return d.join(''); - }, - parse: function (a) { - for (var c = a.length, d = [], b = 0; b < c; b += 2) - d[b >>> 3] |= parseInt(a.substr(b, 2), 16) << (24 - 4 * (b % 8)); - return new r.init(d, c / 2); - }, - }), - n = (l.Latin1 = { - stringify: function (a) { - var c = a.words; - a = a.sigBytes; - for (var d = [], b = 0; b < a; b++) d.push(String.fromCharCode((c[b >>> 2] >>> (24 - 8 * (b % 4))) & 255)); - return d.join(''); - }, - parse: function (a) { - for (var c = a.length, d = [], b = 0; b < c; b++) d[b >>> 2] |= (a.charCodeAt(b) & 255) << (24 - 8 * (b % 4)); - return new r.init(d, c); - }, - }), - j = (l.Utf8 = { - stringify: function (a) { - try { - return decodeURIComponent(escape(n.stringify(a))); - } catch (c) { - throw Error('Malformed UTF-8 data'); - } - }, - parse: function (a) { - return n.parse(unescape(encodeURIComponent(a))); - }, - }), - u = (g.BufferedBlockAlgorithm = m.extend({ - reset: function () { - this._data = new r.init(); - this._nDataBytes = 0; - }, - _append: function (a) { - 'string' == typeof a && (a = j.parse(a)); - this._data.concat(a); - this._nDataBytes += a.sigBytes; - }, - _process: function (a) { - var c = this._data, - d = c.words, - b = c.sigBytes, - e = this.blockSize, - f = b / (4 * e), - f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0); - a = f * e; - b = h.min(4 * a, b); - if (a) { - for (var g = 0; g < a; g += e) this._doProcessBlock(d, g); - g = d.splice(0, a); - c.sigBytes -= b; - } - return new r.init(g, b); - }, - clone: function () { - var a = m.clone.call(this); - a._data = this._data.clone(); - return a; - }, - _minBufferSize: 0, - })); - g.Hasher = u.extend({ - cfg: m.extend(), - init: function (a) { - this.cfg = this.cfg.extend(a); - this.reset(); - }, - reset: function () { - u.reset.call(this); - this._doReset(); - }, - update: function (a) { - this._append(a); - this._process(); - return this; - }, - finalize: function (a) { - a && this._append(a); - return this._doFinalize(); - }, - blockSize: 16, - _createHelper: function (a) { - return function (c, d) { - return new a.init(d).finalize(c); - }; - }, - _createHmacHelper: function (a) { - return function (c, d) { - return new t.HMAC.init(a, d).finalize(c); - }; - }, - }); - var t = (f.algo = {}); - return f; - })(Math); - -// SHA256 -(function (h) { - for ( - var s = CryptoJS, - f = s.lib, - g = f.WordArray, - q = f.Hasher, - f = s.algo, - m = [], - r = [], - l = function (a) { - return (4294967296 * (a - (a | 0))) | 0; - }, - k = 2, - n = 0; - 64 > n; - - ) { - var j; - a: { - j = k; - for (var u = h.sqrt(j), t = 2; t <= u; t++) - if (!(j % t)) { - j = !1; - break a; - } - j = !0; - } - j && (8 > n && (m[n] = l(h.pow(k, 0.5))), (r[n] = l(h.pow(k, 1 / 3))), n++); - k++; - } - var a = [], - f = (f.SHA256 = q.extend({ - _doReset: function () { - this._hash = new g.init(m.slice(0)); - }, - _doProcessBlock: function (c, d) { - for ( - var b = this._hash.words, - e = b[0], - f = b[1], - g = b[2], - j = b[3], - h = b[4], - m = b[5], - n = b[6], - q = b[7], - p = 0; - 64 > p; - p++ - ) { - if (16 > p) a[p] = c[d + p] | 0; - else { - var k = a[p - 15], - l = a[p - 2]; - a[p] = - (((k << 25) | (k >>> 7)) ^ ((k << 14) | (k >>> 18)) ^ (k >>> 3)) + - a[p - 7] + - (((l << 15) | (l >>> 17)) ^ ((l << 13) | (l >>> 19)) ^ (l >>> 10)) + - a[p - 16]; - } - k = - q + - (((h << 26) | (h >>> 6)) ^ ((h << 21) | (h >>> 11)) ^ ((h << 7) | (h >>> 25))) + - ((h & m) ^ (~h & n)) + - r[p] + - a[p]; - l = - (((e << 30) | (e >>> 2)) ^ ((e << 19) | (e >>> 13)) ^ ((e << 10) | (e >>> 22))) + - ((e & f) ^ (e & g) ^ (f & g)); - q = n; - n = m; - m = h; - h = (j + k) | 0; - j = g; - g = f; - f = e; - e = (k + l) | 0; - } - b[0] = (b[0] + e) | 0; - b[1] = (b[1] + f) | 0; - b[2] = (b[2] + g) | 0; - b[3] = (b[3] + j) | 0; - b[4] = (b[4] + h) | 0; - b[5] = (b[5] + m) | 0; - b[6] = (b[6] + n) | 0; - b[7] = (b[7] + q) | 0; - }, - _doFinalize: function () { - var a = this._data, - d = a.words, - b = 8 * this._nDataBytes, - e = 8 * a.sigBytes; - d[e >>> 5] |= 128 << (24 - (e % 32)); - d[(((e + 64) >>> 9) << 4) + 14] = h.floor(b / 4294967296); - d[(((e + 64) >>> 9) << 4) + 15] = b; - a.sigBytes = 4 * d.length; - this._process(); - return this._hash; - }, - clone: function () { - var a = q.clone.call(this); - a._hash = this._hash.clone(); - return a; - }, - })); - s.SHA256 = q._createHelper(f); - s.HmacSHA256 = q._createHmacHelper(f); -})(Math); - -// HMAC SHA256 -(function () { - var h = CryptoJS, - s = h.enc.Utf8; - h.algo.HMAC = h.lib.Base.extend({ - init: function (f, g) { - f = this._hasher = new f.init(); - 'string' == typeof g && (g = s.parse(g)); - var h = f.blockSize, - m = 4 * h; - g.sigBytes > m && (g = f.finalize(g)); - g.clamp(); - for (var r = (this._oKey = g.clone()), l = (this._iKey = g.clone()), k = r.words, n = l.words, j = 0; j < h; j++) - (k[j] ^= 1549556828), (n[j] ^= 909522486); - r.sigBytes = l.sigBytes = m; - this.reset(); - }, - reset: function () { - var f = this._hasher; - f.reset(); - f.update(this._iKey); - }, - update: function (f) { - this._hasher.update(f); - return this; - }, - finalize: function (f) { - var g = this._hasher; - f = g.finalize(f); - g.reset(); - return g.finalize(this._oKey.clone().concat(f)); - }, - }); -})(); - -// Base64 -(function () { - var u = CryptoJS, - p = u.lib.WordArray; - u.enc.Base64 = { - stringify: function (d) { - var l = d.words, - p = d.sigBytes, - t = this._map; - d.clamp(); - d = []; - for (var r = 0; r < p; r += 3) - for ( - var w = - (((l[r >>> 2] >>> (24 - 8 * (r % 4))) & 255) << 16) | - (((l[(r + 1) >>> 2] >>> (24 - 8 * ((r + 1) % 4))) & 255) << 8) | - ((l[(r + 2) >>> 2] >>> (24 - 8 * ((r + 2) % 4))) & 255), - v = 0; - 4 > v && r + 0.75 * v < p; - v++ - ) - d.push(t.charAt((w >>> (6 * (3 - v))) & 63)); - if ((l = t.charAt(64))) for (; d.length % 4; ) d.push(l); - return d.join(''); - }, - parse: function (d) { - var l = d.length, - s = this._map, - t = s.charAt(64); - t && ((t = d.indexOf(t)), -1 != t && (l = t)); - for (var t = [], r = 0, w = 0; w < l; w++) - if (w % 4) { - var v = s.indexOf(d.charAt(w - 1)) << (2 * (w % 4)), - b = s.indexOf(d.charAt(w)) >>> (6 - 2 * (w % 4)); - t[r >>> 2] |= (v | b) << (24 - 8 * (r % 4)); - r++; - } - return p.create(t, r); - }, - _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', - }; -})(); - -// BlockCipher -(function (u) { - function p(b, n, a, c, e, j, k) { - b = b + ((n & a) | (~n & c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function d(b, n, a, c, e, j, k) { - b = b + ((n & c) | (a & ~c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function l(b, n, a, c, e, j, k) { - b = b + (n ^ a ^ c) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - function s(b, n, a, c, e, j, k) { - b = b + (a ^ (n | ~c)) + e + k; - return ((b << j) | (b >>> (32 - j))) + n; - } - for (var t = CryptoJS, r = t.lib, w = r.WordArray, v = r.Hasher, r = t.algo, b = [], x = 0; 64 > x; x++) - b[x] = (4294967296 * u.abs(u.sin(x + 1))) | 0; - r = r.MD5 = v.extend({ - _doReset: function () { - this._hash = new w.init([1732584193, 4023233417, 2562383102, 271733878]); - }, - _doProcessBlock: function (q, n) { - for (var a = 0; 16 > a; a++) { - var c = n + a, - e = q[c]; - q[c] = (((e << 8) | (e >>> 24)) & 16711935) | (((e << 24) | (e >>> 8)) & 4278255360); - } - var a = this._hash.words, - c = q[n + 0], - e = q[n + 1], - j = q[n + 2], - k = q[n + 3], - z = q[n + 4], - r = q[n + 5], - t = q[n + 6], - w = q[n + 7], - v = q[n + 8], - A = q[n + 9], - B = q[n + 10], - C = q[n + 11], - u = q[n + 12], - D = q[n + 13], - E = q[n + 14], - x = q[n + 15], - f = a[0], - m = a[1], - g = a[2], - h = a[3], - f = p(f, m, g, h, c, 7, b[0]), - h = p(h, f, m, g, e, 12, b[1]), - g = p(g, h, f, m, j, 17, b[2]), - m = p(m, g, h, f, k, 22, b[3]), - f = p(f, m, g, h, z, 7, b[4]), - h = p(h, f, m, g, r, 12, b[5]), - g = p(g, h, f, m, t, 17, b[6]), - m = p(m, g, h, f, w, 22, b[7]), - f = p(f, m, g, h, v, 7, b[8]), - h = p(h, f, m, g, A, 12, b[9]), - g = p(g, h, f, m, B, 17, b[10]), - m = p(m, g, h, f, C, 22, b[11]), - f = p(f, m, g, h, u, 7, b[12]), - h = p(h, f, m, g, D, 12, b[13]), - g = p(g, h, f, m, E, 17, b[14]), - m = p(m, g, h, f, x, 22, b[15]), - f = d(f, m, g, h, e, 5, b[16]), - h = d(h, f, m, g, t, 9, b[17]), - g = d(g, h, f, m, C, 14, b[18]), - m = d(m, g, h, f, c, 20, b[19]), - f = d(f, m, g, h, r, 5, b[20]), - h = d(h, f, m, g, B, 9, b[21]), - g = d(g, h, f, m, x, 14, b[22]), - m = d(m, g, h, f, z, 20, b[23]), - f = d(f, m, g, h, A, 5, b[24]), - h = d(h, f, m, g, E, 9, b[25]), - g = d(g, h, f, m, k, 14, b[26]), - m = d(m, g, h, f, v, 20, b[27]), - f = d(f, m, g, h, D, 5, b[28]), - h = d(h, f, m, g, j, 9, b[29]), - g = d(g, h, f, m, w, 14, b[30]), - m = d(m, g, h, f, u, 20, b[31]), - f = l(f, m, g, h, r, 4, b[32]), - h = l(h, f, m, g, v, 11, b[33]), - g = l(g, h, f, m, C, 16, b[34]), - m = l(m, g, h, f, E, 23, b[35]), - f = l(f, m, g, h, e, 4, b[36]), - h = l(h, f, m, g, z, 11, b[37]), - g = l(g, h, f, m, w, 16, b[38]), - m = l(m, g, h, f, B, 23, b[39]), - f = l(f, m, g, h, D, 4, b[40]), - h = l(h, f, m, g, c, 11, b[41]), - g = l(g, h, f, m, k, 16, b[42]), - m = l(m, g, h, f, t, 23, b[43]), - f = l(f, m, g, h, A, 4, b[44]), - h = l(h, f, m, g, u, 11, b[45]), - g = l(g, h, f, m, x, 16, b[46]), - m = l(m, g, h, f, j, 23, b[47]), - f = s(f, m, g, h, c, 6, b[48]), - h = s(h, f, m, g, w, 10, b[49]), - g = s(g, h, f, m, E, 15, b[50]), - m = s(m, g, h, f, r, 21, b[51]), - f = s(f, m, g, h, u, 6, b[52]), - h = s(h, f, m, g, k, 10, b[53]), - g = s(g, h, f, m, B, 15, b[54]), - m = s(m, g, h, f, e, 21, b[55]), - f = s(f, m, g, h, v, 6, b[56]), - h = s(h, f, m, g, x, 10, b[57]), - g = s(g, h, f, m, t, 15, b[58]), - m = s(m, g, h, f, D, 21, b[59]), - f = s(f, m, g, h, z, 6, b[60]), - h = s(h, f, m, g, C, 10, b[61]), - g = s(g, h, f, m, j, 15, b[62]), - m = s(m, g, h, f, A, 21, b[63]); - a[0] = (a[0] + f) | 0; - a[1] = (a[1] + m) | 0; - a[2] = (a[2] + g) | 0; - a[3] = (a[3] + h) | 0; - }, - _doFinalize: function () { - var b = this._data, - n = b.words, - a = 8 * this._nDataBytes, - c = 8 * b.sigBytes; - n[c >>> 5] |= 128 << (24 - (c % 32)); - var e = u.floor(a / 4294967296); - n[(((c + 64) >>> 9) << 4) + 15] = (((e << 8) | (e >>> 24)) & 16711935) | (((e << 24) | (e >>> 8)) & 4278255360); - n[(((c + 64) >>> 9) << 4) + 14] = (((a << 8) | (a >>> 24)) & 16711935) | (((a << 24) | (a >>> 8)) & 4278255360); - b.sigBytes = 4 * (n.length + 1); - this._process(); - b = this._hash; - n = b.words; - for (a = 0; 4 > a; a++) - (c = n[a]), (n[a] = (((c << 8) | (c >>> 24)) & 16711935) | (((c << 24) | (c >>> 8)) & 4278255360)); - return b; - }, - clone: function () { - var b = v.clone.call(this); - b._hash = this._hash.clone(); - return b; - }, - }); - t.MD5 = v._createHelper(r); - t.HmacMD5 = v._createHmacHelper(r); -})(Math); -(function () { - var u = CryptoJS, - p = u.lib, - d = p.Base, - l = p.WordArray, - p = u.algo, - s = (p.EvpKDF = d.extend({ - cfg: d.extend({ keySize: 4, hasher: p.MD5, iterations: 1 }), - init: function (d) { - this.cfg = this.cfg.extend(d); - }, - compute: function (d, r) { - for ( - var p = this.cfg, s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; - u.length < q; - - ) { - n && s.update(n); - var n = s.update(d).finalize(r); - s.reset(); - for (var a = 1; a < p; a++) (n = s.finalize(n)), s.reset(); - b.concat(n); - } - b.sigBytes = 4 * q; - return b; - }, - })); - u.EvpKDF = function (d, l, p) { - return s.create(p).compute(d, l); - }; -})(); - -// Cipher -CryptoJS.lib.Cipher || - (function (u) { - var p = CryptoJS, - d = p.lib, - l = d.Base, - s = d.WordArray, - t = d.BufferedBlockAlgorithm, - r = p.enc.Base64, - w = p.algo.EvpKDF, - v = (d.Cipher = t.extend({ - cfg: l.extend(), - createEncryptor: function (e, a) { - return this.create(this._ENC_XFORM_MODE, e, a); - }, - createDecryptor: function (e, a) { - return this.create(this._DEC_XFORM_MODE, e, a); - }, - init: function (e, a, b) { - this.cfg = this.cfg.extend(b); - this._xformMode = e; - this._key = a; - this.reset(); - }, - reset: function () { - t.reset.call(this); - this._doReset(); - }, - process: function (e) { - this._append(e); - return this._process(); - }, - finalize: function (e) { - e && this._append(e); - return this._doFinalize(); - }, - keySize: 4, - ivSize: 4, - _ENC_XFORM_MODE: 1, - _DEC_XFORM_MODE: 2, - _createHelper: function (e) { - return { - encrypt: function (b, k, d) { - return ('string' == typeof k ? c : a).encrypt(e, b, k, d); - }, - decrypt: function (b, k, d) { - return ('string' == typeof k ? c : a).decrypt(e, b, k, d); - }, - }; - }, - })); - d.StreamCipher = v.extend({ - _doFinalize: function () { - return this._process(!0); - }, - blockSize: 1, - }); - var b = (p.mode = {}), - x = function (e, a, b) { - var c = this._iv; - c ? (this._iv = u) : (c = this._prevBlock); - for (var d = 0; d < b; d++) e[a + d] ^= c[d]; - }, - q = (d.BlockCipherMode = l.extend({ - createEncryptor: function (e, a) { - return this.Encryptor.create(e, a); - }, - createDecryptor: function (e, a) { - return this.Decryptor.create(e, a); - }, - init: function (e, a) { - this._cipher = e; - this._iv = a; - }, - })).extend(); - q.Encryptor = q.extend({ - processBlock: function (e, a) { - var b = this._cipher, - c = b.blockSize; - x.call(this, e, a, c); - b.encryptBlock(e, a); - this._prevBlock = e.slice(a, a + c); - }, - }); - q.Decryptor = q.extend({ - processBlock: function (e, a) { - var b = this._cipher, - c = b.blockSize, - d = e.slice(a, a + c); - b.decryptBlock(e, a); - x.call(this, e, a, c); - this._prevBlock = d; - }, - }); - b = b.CBC = q; - q = (p.pad = {}).Pkcs7 = { - pad: function (a, b) { - for ( - var c = 4 * b, c = c - (a.sigBytes % c), d = (c << 24) | (c << 16) | (c << 8) | c, l = [], n = 0; - n < c; - n += 4 - ) - l.push(d); - c = s.create(l, c); - a.concat(c); - }, - unpad: function (a) { - a.sigBytes -= a.words[(a.sigBytes - 1) >>> 2] & 255; - }, - }; - d.BlockCipher = v.extend({ - cfg: v.cfg.extend({ mode: b, padding: q }), - reset: function () { - v.reset.call(this); - var a = this.cfg, - b = a.iv, - a = a.mode; - if (this._xformMode == this._ENC_XFORM_MODE) var c = a.createEncryptor; - else (c = a.createDecryptor), (this._minBufferSize = 1); - this._mode = c.call(a, this, b && b.words); - }, - _doProcessBlock: function (a, b) { - this._mode.processBlock(a, b); - }, - _doFinalize: function () { - var a = this.cfg.padding; - if (this._xformMode == this._ENC_XFORM_MODE) { - a.pad(this._data, this.blockSize); - var b = this._process(!0); - } else (b = this._process(!0)), a.unpad(b); - return b; - }, - blockSize: 4, - }); - var n = (d.CipherParams = l.extend({ - init: function (a) { - this.mixIn(a); - }, - toString: function (a) { - return (a || this.formatter).stringify(this); - }, - })), - b = ((p.format = {}).OpenSSL = { - stringify: function (a) { - var b = a.ciphertext; - a = a.salt; - return (a ? s.create([1398893684, 1701076831]).concat(a).concat(b) : b).toString(r); - }, - parse: function (a) { - a = r.parse(a); - var b = a.words; - if (1398893684 == b[0] && 1701076831 == b[1]) { - var c = s.create(b.slice(2, 4)); - b.splice(0, 4); - a.sigBytes -= 16; - } - return n.create({ ciphertext: a, salt: c }); - }, - }), - a = (d.SerializableCipher = l.extend({ - cfg: l.extend({ format: b }), - encrypt: function (a, b, c, d) { - d = this.cfg.extend(d); - var l = a.createEncryptor(c, d); - b = l.finalize(b); - l = l.cfg; - return n.create({ - ciphertext: b, - key: c, - iv: l.iv, - algorithm: a, - mode: l.mode, - padding: l.padding, - blockSize: a.blockSize, - formatter: d.format, - }); - }, - decrypt: function (a, b, c, d) { - d = this.cfg.extend(d); - b = this._parse(b, d.format); - return a.createDecryptor(c, d).finalize(b.ciphertext); - }, - _parse: function (a, b) { - return 'string' == typeof a ? b.parse(a, this) : a; - }, - })), - p = ((p.kdf = {}).OpenSSL = { - execute: function (a, b, c, d) { - d || (d = s.random(8)); - a = w.create({ keySize: b + c }).compute(a, d); - c = s.create(a.words.slice(b), 4 * c); - a.sigBytes = 4 * b; - return n.create({ key: a, iv: c, salt: d }); - }, - }), - c = (d.PasswordBasedCipher = a.extend({ - cfg: a.cfg.extend({ kdf: p }), - encrypt: function (b, c, d, l) { - l = this.cfg.extend(l); - d = l.kdf.execute(d, b.keySize, b.ivSize); - l.iv = d.iv; - b = a.encrypt.call(this, b, c, d.key, l); - b.mixIn(d); - return b; - }, - decrypt: function (b, c, d, l) { - l = this.cfg.extend(l); - c = this._parse(c, l.format); - d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt); - l.iv = d.iv; - return a.decrypt.call(this, b, c, d.key, l); - }, - })); - })(); - -// AES -(function () { - for ( - var u = CryptoJS, - p = u.lib.BlockCipher, - d = u.algo, - l = [], - s = [], - t = [], - r = [], - w = [], - v = [], - b = [], - x = [], - q = [], - n = [], - a = [], - c = 0; - 256 > c; - c++ - ) - a[c] = 128 > c ? c << 1 : (c << 1) ^ 283; - for (var e = 0, j = 0, c = 0; 256 > c; c++) { - var k = j ^ (j << 1) ^ (j << 2) ^ (j << 3) ^ (j << 4), - k = (k >>> 8) ^ (k & 255) ^ 99; - l[e] = k; - s[k] = e; - var z = a[e], - F = a[z], - G = a[F], - y = (257 * a[k]) ^ (16843008 * k); - t[e] = (y << 24) | (y >>> 8); - r[e] = (y << 16) | (y >>> 16); - w[e] = (y << 8) | (y >>> 24); - v[e] = y; - y = (16843009 * G) ^ (65537 * F) ^ (257 * z) ^ (16843008 * e); - b[k] = (y << 24) | (y >>> 8); - x[k] = (y << 16) | (y >>> 16); - q[k] = (y << 8) | (y >>> 24); - n[k] = y; - e ? ((e = z ^ a[a[a[G ^ z]]]), (j ^= a[a[j]])) : (e = j = 1); - } - var H = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54], - d = (d.AES = p.extend({ - _doReset: function () { - for ( - var a = this._key, - c = a.words, - d = a.sigBytes / 4, - a = 4 * ((this._nRounds = d + 6) + 1), - e = (this._keySchedule = []), - j = 0; - j < a; - j++ - ) - if (j < d) e[j] = c[j]; - else { - var k = e[j - 1]; - j % d - ? 6 < d && - 4 == j % d && - (k = (l[k >>> 24] << 24) | (l[(k >>> 16) & 255] << 16) | (l[(k >>> 8) & 255] << 8) | l[k & 255]) - : ((k = (k << 8) | (k >>> 24)), - (k = (l[k >>> 24] << 24) | (l[(k >>> 16) & 255] << 16) | (l[(k >>> 8) & 255] << 8) | l[k & 255]), - (k ^= H[(j / d) | 0] << 24)); - e[j] = e[j - d] ^ k; - } - c = this._invKeySchedule = []; - for (d = 0; d < a; d++) - (j = a - d), - (k = d % 4 ? e[j] : e[j - 4]), - (c[d] = - 4 > d || 4 >= j ? k : b[l[k >>> 24]] ^ x[l[(k >>> 16) & 255]] ^ q[l[(k >>> 8) & 255]] ^ n[l[k & 255]]); - }, - encryptBlock: function (a, b) { - this._doCryptBlock(a, b, this._keySchedule, t, r, w, v, l); - }, - decryptBlock: function (a, c) { - var d = a[c + 1]; - a[c + 1] = a[c + 3]; - a[c + 3] = d; - this._doCryptBlock(a, c, this._invKeySchedule, b, x, q, n, s); - d = a[c + 1]; - a[c + 1] = a[c + 3]; - a[c + 3] = d; - }, - _doCryptBlock: function (a, b, c, d, e, j, l, f) { - for ( - var m = this._nRounds, - g = a[b] ^ c[0], - h = a[b + 1] ^ c[1], - k = a[b + 2] ^ c[2], - n = a[b + 3] ^ c[3], - p = 4, - r = 1; - r < m; - r++ - ) - var q = d[g >>> 24] ^ e[(h >>> 16) & 255] ^ j[(k >>> 8) & 255] ^ l[n & 255] ^ c[p++], - s = d[h >>> 24] ^ e[(k >>> 16) & 255] ^ j[(n >>> 8) & 255] ^ l[g & 255] ^ c[p++], - t = d[k >>> 24] ^ e[(n >>> 16) & 255] ^ j[(g >>> 8) & 255] ^ l[h & 255] ^ c[p++], - n = d[n >>> 24] ^ e[(g >>> 16) & 255] ^ j[(h >>> 8) & 255] ^ l[k & 255] ^ c[p++], - g = q, - h = s, - k = t; - q = ((f[g >>> 24] << 24) | (f[(h >>> 16) & 255] << 16) | (f[(k >>> 8) & 255] << 8) | f[n & 255]) ^ c[p++]; - s = ((f[h >>> 24] << 24) | (f[(k >>> 16) & 255] << 16) | (f[(n >>> 8) & 255] << 8) | f[g & 255]) ^ c[p++]; - t = ((f[k >>> 24] << 24) | (f[(n >>> 16) & 255] << 16) | (f[(g >>> 8) & 255] << 8) | f[h & 255]) ^ c[p++]; - n = ((f[n >>> 24] << 24) | (f[(g >>> 16) & 255] << 16) | (f[(h >>> 8) & 255] << 8) | f[k & 255]) ^ c[p++]; - a[b] = q; - a[b + 1] = s; - a[b + 2] = t; - a[b + 3] = n; - }, - keySize: 8, - })); - u.AES = p._createHelper(d); -})(); - -// Mode ECB -CryptoJS.mode.ECB = (function () { - var ECB = CryptoJS.lib.BlockCipherMode.extend(); - - ECB.Encryptor = ECB.extend({ - processBlock: function (words, offset) { - this._cipher.encryptBlock(words, offset); - }, - }); - - ECB.Decryptor = ECB.extend({ - processBlock: function (words, offset) { - this._cipher.decryptBlock(words, offset); - }, - }); - - return ECB; -})(); - -module.exports = CryptoJS; diff --git a/src/core/components/cryptography/index.ts b/src/core/components/cryptography/index.ts index 5d2ae929c..f69a0e0cb 100644 --- a/src/core/components/cryptography/index.ts +++ b/src/core/components/cryptography/index.ts @@ -4,91 +4,55 @@ * @internal */ +import { CryptoPrimitives } from '../../interfaces/crypto-primitives'; import { CryptorConfiguration } from '../../interfaces/crypto-module'; import { LoggerManager } from '../logger-manager'; import { isSensitiveLogKey } from '../../utils'; import { Payload } from '../../types/api'; -import { decode } from '../base64_codec'; -import CryptoJS from './hmac-sha256'; +import { bytesToHex, decodeUtf8Strict, toArrayBuffer } from './bytes'; +import { decode, encode } from '../base64_codec'; /** - * Convert bytes array to words array. + * Legacy cryptography module for data encryption / decryption. * - * @param b - Bytes array (buffer) which should be converted. - * - * @returns Word sized array. - * - * @internal - */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -function bufferToWordArray(b: string | any[] | Uint8ClampedArray) { - const wa: number[] = []; - let i; - for (i = 0; i < b.length; i += 1) { - wa[(i / 4) | 0] |= b[i] << (24 - 8 * i); - } - - // @ts-expect-error Bundled library without types. - return CryptoJS.lib.WordArray.create(wa, b.length); -} - -/** - * Legacy cryptor configuration options. - * - * @internal - */ -type CryptoConfiguration = { - encryptKey?: boolean; - keyEncoding?: 'hex' | 'utf8' | 'base64' | 'binary'; - keyLength?: 128 | 256; - mode?: 'ecb' | 'cbc'; -}; - -/** - * Legacy cryptography module for files and signature. + * Wire format (compatible with all PubNub SDKs): + * - key: first 32 characters of HEX encoded SHA-256 digest of the cipher key, used as UTF-8 bytes (AES-256), + * - cipher: AES-CBC with PKCS#7 padding, + * - random IV: `base64(iv + ciphertext)`, static IV (`0123456789012345`): `base64(ciphertext)`. * * @internal */ -export default class { - /** - * Crypto initialization vector. - */ - private iv = '0123456789012345'; - +export default class LegacyCrypto { /** - * List os allowed cipher key encodings. + * `string` to {@link Uint8Array} encoder. */ - private allowedKeyEncodings = ['hex', 'utf8', 'base64', 'binary']; + private static readonly encoder = new TextEncoder(); /** - * Allowed cipher key lengths. + * Static crypto initialization vector (used when random IVs are disabled). */ - private allowedKeyLengths = [128, 256]; + private static readonly staticIV = new TextEncoder().encode('0123456789012345'); /** - * Allowed crypto modes. + * Initialization vector length. */ - private allowedModes = ['ecb', 'cbc']; + private static readonly IV_LENGTH = 16; /** - * Default cryptor configuration options. + * Derived key for configured cipher key (keys for per-call custom cipher keys aren't cached). */ - private readonly defaultOptions: Required; + private configuredKey?: Promise; /** * Registered loggers' manager. */ private _logger?: LoggerManager; - constructor(private readonly configuration: CryptorConfiguration) { + constructor( + private readonly configuration: CryptorConfiguration, + private readonly primitives: CryptoPrimitives, + ) { this.logger = configuration.logger; - - this.defaultOptions = { - encryptKey: true, - keyEncoding: 'utf8', - keyLength: 256, - mode: 'cbc', - }; } /** @@ -120,42 +84,15 @@ export default class { return this._logger; } - /** - * Generate HMAC-SHA256 hash from input data. - * - * @param data - Data from which hash should be generated. - * - * @returns HMAC-SHA256 hash from provided `data`. - */ - public HMACSHA256(data: string): string { - // @ts-expect-error Bundled library without types. - const hash = CryptoJS.HmacSHA256(data, this.configuration.secretKey); - // @ts-expect-error Bundled library without types. - return hash.toString(CryptoJS.enc.Base64); - } - - /** - * Generate SHA256 hash from input data. - * - * @param data - Data from which hash should be generated. - * - * @returns SHA256 hash from provided `data`. - */ - public SHA256(data: string): string { - // @ts-expect-error Bundled library without types. - return CryptoJS.SHA256(data).toString(CryptoJS.enc.Hex); - } - /** * Encrypt provided data. * * @param data - Source data which should be encrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Encrypted `data`. */ - public encrypt(data: string | Payload, customCipherKey?: string, options?: CryptoConfiguration) { + public async encrypt(data: string | Payload, customCipherKey?: string): Promise { if (this.configuration.customEncrypt) { if (this.logger) this.logger.warn('Crypto', "'customEncrypt' is deprecated. Consult docs for better alternative."); @@ -163,7 +100,7 @@ export default class { return this.configuration.customEncrypt(data); } - return this.pnEncrypt(data as string, customCipherKey, options); + return this.pnEncrypt(typeof data === 'string' ? data : JSON.stringify(data), customCipherKey); } /** @@ -171,11 +108,10 @@ export default class { * * @param data - Encrypted data which should be decrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Decrypted `data`. */ - public decrypt(data: string, customCipherKey?: string, options?: CryptoConfiguration) { + public async decrypt(data: string, customCipherKey?: string): Promise { if (this.configuration.customDecrypt) { if (this.logger) this.logger.warn('Crypto', "'customDecrypt' is deprecated. Consult docs for better alternative."); @@ -183,7 +119,7 @@ export default class { return this.configuration.customDecrypt(data); } - return this.pnDecrypt(data, customCipherKey, options); + return this.pnDecrypt(data, customCipherKey); } /** @@ -191,43 +127,35 @@ export default class { * * @param data - Source data which should be encrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * * @returns Encrypted `data` as string. */ - private pnEncrypt(data: string, customCipherKey?: string, options?: CryptoConfiguration): string { + private async pnEncrypt(data: string, customCipherKey?: string): Promise { const decidedCipherKey = customCipherKey ?? this.configuration.cipherKey; if (!decidedCipherKey) return data; if (this.logger) { this.logger.debug('Crypto', () => ({ messageType: 'object', - message: { data, ...(options ?? {}) }, + message: { data }, details: 'Encrypt with parameters:', ignoredKeys: isSensitiveLogKey, })); } - options = this.parseOptions(options); - const mode = this.getMode(options); - const cipherKey = this.getPaddedKey(decidedCipherKey, options); + const key = await this.getKey(decidedCipherKey); + const plaintext = LegacyCrypto.encoder.encode(data); if (this.configuration.useRandomIVs) { - const waIv = this.getRandomIV(); - // @ts-expect-error Bundled library without types. - const waPayload = CryptoJS.AES.encrypt(data, cipherKey, { iv: waIv, mode }).ciphertext; + const iv = this.primitives.randomBytes(LegacyCrypto.IV_LENGTH); + const ciphertext = await this.primitives.aesCbcEncrypt(key, iv, plaintext); - // @ts-expect-error Bundled library without types. - return waIv.clone().concat(waPayload.clone()).toString(CryptoJS.enc.Base64); + return encode(this.concat(iv, ciphertext)); } - const iv = this.getIV(options); - // @ts-expect-error Bundled library without types. - const encryptedHexArray = CryptoJS.AES.encrypt(data, cipherKey, { iv, mode }).ciphertext; - // @ts-expect-error Bundled library without types. - const base64Encrypted = encryptedHexArray.toString(CryptoJS.enc.Base64); + const ciphertext = await this.primitives.aesCbcEncrypt(key, LegacyCrypto.staticIV, plaintext); - return base64Encrypted || data; + return encode(toArrayBuffer(ciphertext)) || data; } /** @@ -235,156 +163,82 @@ export default class { * * @param data - Encrypted data which should be decrypted. * @param [customCipherKey] - Custom cipher key (different from defined on client level). - * @param [options] - Specific crypto configuration options. * - * @returns Decrypted `data`. + * @returns Decrypted `data` or `null` if it can't be decrypted. */ - private pnDecrypt(data: string, customCipherKey?: string, options?: CryptoConfiguration): Payload | null { + private async pnDecrypt(data: string, customCipherKey?: string): Promise { const decidedCipherKey = customCipherKey ?? this.configuration.cipherKey; if (!decidedCipherKey) return data; if (this.logger) { this.logger.debug('Crypto', () => ({ messageType: 'object', - message: { data, ...(options ?? {}) }, + message: { data }, details: 'Decrypt with parameters:', ignoredKeys: isSensitiveLogKey, })); } - options = this.parseOptions(options); - const mode = this.getMode(options); - const cipherKey = this.getPaddedKey(decidedCipherKey, options); + try { + const key = await this.getKey(decidedCipherKey); + const encrypted = new Uint8Array(decode(data)); + let iv = LegacyCrypto.staticIV; + let ciphertext = encrypted; - if (this.configuration.useRandomIVs) { - const ciphertext = new Uint8ClampedArray(decode(data)); - - const iv = bufferToWordArray(ciphertext.slice(0, 16)); - const payload = bufferToWordArray(ciphertext.slice(16)); - - try { - // @ts-expect-error Bundled library without types. - const plainJSON = CryptoJS.AES.decrypt({ ciphertext: payload }, cipherKey, { iv, mode }).toString( - // @ts-expect-error Bundled library without types. - CryptoJS.enc.Utf8, - ); - return JSON.parse(plainJSON); - } catch (e) { - if (this.logger) this.logger.error('Crypto', () => ({ messageType: 'error', message: e })); - - return null; - } - } else { - const iv = this.getIV(options); - try { - // @ts-expect-error Bundled library without types. - const ciphertext = CryptoJS.enc.Base64.parse(data); - // @ts-expect-error Bundled library without types. - const plainJSON = CryptoJS.AES.decrypt({ ciphertext }, cipherKey, { iv, mode }).toString(CryptoJS.enc.Utf8); - return JSON.parse(plainJSON); - } catch (e) { - if (this.logger) this.logger.error('Crypto', () => ({ messageType: 'error', message: e })); - - return null; + if (this.configuration.useRandomIVs) { + iv = encrypted.slice(0, LegacyCrypto.IV_LENGTH); + ciphertext = encrypted.slice(LegacyCrypto.IV_LENGTH); } - } - } - /** - * Pre-process provided custom crypto configuration. - * - * @param incomingOptions - Configuration which should be pre-processed before use. - * - * @returns Normalized crypto configuration options. - */ - private parseOptions(incomingOptions?: CryptoConfiguration): Required { - if (!incomingOptions) return this.defaultOptions; - - // Defaults - const options = { - encryptKey: incomingOptions.encryptKey ?? this.defaultOptions.encryptKey, - keyEncoding: incomingOptions.keyEncoding ?? this.defaultOptions.keyEncoding, - keyLength: incomingOptions.keyLength ?? this.defaultOptions.keyLength, - mode: incomingOptions.mode ?? this.defaultOptions.mode, - }; - - // Validation - if (this.allowedKeyEncodings.indexOf(options.keyEncoding!.toLowerCase()) === -1) - options.keyEncoding = this.defaultOptions.keyEncoding; - if (this.allowedKeyLengths.indexOf(options.keyLength!) === -1) options.keyLength = this.defaultOptions.keyLength; - if (this.allowedModes.indexOf(options.mode!.toLowerCase()) === -1) options.mode = this.defaultOptions.mode; - - return options; - } + const plaintext = await this.primitives.aesCbcDecrypt(key, iv, ciphertext); - /** - * Decode provided cipher key. - * - * @param key - Key in `encoding` provided by `options`. - * @param options - Crypto configuration options with cipher key details. - * - * @returns Array buffer with decoded key. - */ - private decodeKey(key: string, options: CryptoConfiguration) { - // @ts-expect-error Bundled library without types. - if (options.keyEncoding === 'base64') return CryptoJS.enc.Base64.parse(key); - // @ts-expect-error Bundled library without types. - if (options.keyEncoding === 'hex') return CryptoJS.enc.Hex.parse(key); + return JSON.parse(decodeUtf8Strict(plaintext)); + } catch (e) { + if (this.logger) this.logger.error('Crypto', () => ({ messageType: 'error', message: e })); - return key; + return null; + } } /** - * Add padding to the cipher key. + * Derive AES-256 key from the cipher key (cached for configured cipher key). * - * @param key - Key which should be padded. - * @param options - Crypto configuration options with cipher key details. + * @param cipherKey - User-provided cipher key. * - * @returns Properly padded cipher key. + * @returns First 32 characters of HEX encoded SHA-256 digest as UTF-8 bytes. */ - private getPaddedKey(key: string, options: CryptoConfiguration) { - key = this.decodeKey(key, options); + private getKey(cipherKey: string): Promise { + if (cipherKey !== this.configuration.cipherKey) return this.deriveKey(cipherKey); - // @ts-expect-error Bundled library without types. - if (options.encryptKey) return CryptoJS.enc.Utf8.parse(this.SHA256(key).slice(0, 32)); + if (!this.configuredKey) { + this.configuredKey = this.deriveKey(cipherKey); + this.configuredKey.catch(() => (this.configuredKey = undefined)); + } - return key; + return this.configuredKey; } /** - * Cipher mode. + * Derive AES-256 key from the cipher key. * - * @param options - Crypto configuration with information about cipher mode. + * @param cipherKey - User-provided cipher key. * - * @returns Crypto cipher mode. + * @returns First 32 characters of HEX encoded SHA-256 digest as UTF-8 bytes. */ - private getMode(options: CryptoConfiguration) { - // @ts-expect-error Bundled library without types. - if (options.mode === 'ecb') return CryptoJS.mode.ECB; + private async deriveKey(cipherKey: string): Promise { + const digest = await this.primitives.sha256(LegacyCrypto.encoder.encode(cipherKey)); - // @ts-expect-error Bundled library without types. - return CryptoJS.mode.CBC; + return LegacyCrypto.encoder.encode(bytesToHex(digest).slice(0, 32)); } /** - * Cipher initialization vector. - * - * @param options - Crypto configuration with information about cipher mode. - * - * @returns Initialization vector. + * Join two byte arrays into {@link ArrayBuffer}. */ - private getIV(options: CryptoConfiguration) { - // @ts-expect-error Bundled library without types. - return options.mode === 'cbc' ? CryptoJS.enc.Utf8.parse(this.iv) : null; - } + private concat(a: Uint8Array, b: Uint8Array): ArrayBuffer { + const result = new Uint8Array(a.byteLength + b.byteLength); + result.set(a, 0); + result.set(b, a.byteLength); - /** - * Random initialization vector. - * - * @returns Generated random initialization vector. - */ - private getRandomIV() { - // @ts-expect-error Bundled library without types. - return CryptoJS.lib.WordArray.random(16); + return result.buffer; } } diff --git a/src/core/components/request.ts b/src/core/components/request.ts index 17c8d577c..c2ba1c545 100644 --- a/src/core/components/request.ts +++ b/src/core/components/request.ts @@ -93,6 +93,18 @@ export abstract class AbstractRequest | void {} + /** * Parse service response. * diff --git a/src/core/endpoints/fetch_messages.ts b/src/core/endpoints/fetch_messages.ts index da9963882..464a41db9 100644 --- a/src/core/endpoints/fetch_messages.ts +++ b/src/core/endpoints/fetch_messages.ts @@ -215,37 +215,44 @@ export class FetchMessagesRequest extends AbstractRequest { - // Map service response to expected data object type structure. - channels[channel] = responseChannels[channel].map((payload) => { - // `null` message type means regular message. - if (payload.message_type === null) payload.message_type = History.PubNubMessageType.Message; - const processedPayload = this.processPayload(channel, payload); - - const item = { - channel, - timetoken: payload.timetoken, - message: processedPayload.payload, - messageType: payload.message_type, - ...(payload.custom_message_type ? { customMessageType: payload.custom_message_type } : {}), - uuid: payload.uuid, - }; - - if (payload.actions) { - const itemWithActions = item as unknown as History.FetchedMessageWithActions; - itemWithActions.actions = payload.actions; - - // Backward compatibility for existing users. - // TODO: Remove in next release. - itemWithActions.data = payload.actions; - } - - if (payload.meta) (item as History.FetchedMessage).meta = payload.meta; - if (processedPayload.error) (item as History.FetchedMessage).error = processedPayload.error; - - return item as History.FetchedMessage; - }); - }); + // Messages processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + const channelNames = Object.keys(responseChannels); + const channelsMessages = await Promise.all( + channelNames.map((channel) => + // Map service response to expected data object type structure. + Promise.all( + responseChannels[channel].map(async (payload) => { + // `null` message type means regular message. + if (payload.message_type === null) payload.message_type = History.PubNubMessageType.Message; + const processedPayload = await this.processPayload(channel, payload); + + const item = { + channel, + timetoken: payload.timetoken, + message: processedPayload.payload, + messageType: payload.message_type, + ...(payload.custom_message_type ? { customMessageType: payload.custom_message_type } : {}), + uuid: payload.uuid, + }; + + if (payload.actions) { + const itemWithActions = item as unknown as History.FetchedMessageWithActions; + itemWithActions.actions = payload.actions; + + // Backward compatibility for existing users. + // TODO: Remove in next release. + itemWithActions.data = payload.actions; + } + + if (payload.meta) (item as History.FetchedMessage).meta = payload.meta; + if (processedPayload.error) (item as History.FetchedMessage).error = processedPayload.error; + + return item as History.FetchedMessage; + }), + ), + ), + ); + channelNames.forEach((channel, index) => (channels[channel] = channelsMessages[index])); if (serviceResponse.more) return { channels, more: serviceResponse.more } as History.FetchMessagesWithActionsResponse; @@ -298,13 +305,13 @@ export class FetchMessagesRequest extends AbstractRequest { const { crypto, logVerbosity } = this.parameters; if (!crypto || typeof payload.message !== 'string') return { payload: payload.message }; @@ -312,7 +319,7 @@ export class FetchMessagesRequest extends AbstractRequest { + /** + * Stringified encrypted file message (computed by {@link prepare} when `crypto` is set). + */ + private encryptedPayload?: string; + constructor(private readonly parameters: RequestParameters) { super(); @@ -78,30 +82,45 @@ export class PublishFileMessageRequest extends AbstractRequest< if (!fileName) return "file name can't be empty"; } + prepare(): Promise | void { + const { crypto } = this.parameters; + if (!crypto) return; + + return encryptMessagePayload(crypto, this.fileMessage).then((encrypted) => { + this.encryptedPayload = encrypted; + }); + } + async parse(response: TransportResponse): Promise { return { timetoken: this.deserializeResponse(response)[2] }; } protected get path(): string { const { - message, channel, keySet: { publishKey, subscribeKey }, - fileId, - fileName, } = this.parameters; - const fileMessage = { + return `/v1/files/publish-file/${publishKey}/${subscribeKey}/0/${encodeString(channel)}/0/${encodeString( + this.prepareMessagePayload(this.fileMessage), + )}`; + } + + /** + * File message which should be published. + * + * @returns File message payload. + */ + private get fileMessage(): Payload { + const { message, fileId, fileName } = this.parameters; + + return { file: { name: fileName, id: fileId, }, ...(message ? { message } : {}), }; - - return `/v1/files/publish-file/${publishKey}/${subscribeKey}/0/${encodeString(channel)}/0/${encodeString( - this.prepareMessagePayload(fileMessage), - )}`; } protected get queryParameters(): Query { @@ -117,20 +136,19 @@ export class PublishFileMessageRequest extends AbstractRequest< /** * Pre-process provided data. * - * Data will be "normalized" and encrypted if `cryptoModule` has been provided. + * Data will be "normalized" and use encrypted payload computed by {@link prepare} if `cryptoModule` has + * been provided. * * @param payload - User-provided data which should be pre-processed before use. * * @returns Payload which can be used as part of request URL or body. * - * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. + * @throws {Error} in case if provided `payload` can't be stringified or request hasn't been prepared. */ private prepareMessagePayload(payload: Payload): string { - const { crypto } = this.parameters; - if (!crypto) return JSON.stringify(payload) || ''; - - const encrypted = crypto.encrypt(JSON.stringify(payload)); + if (!this.parameters.crypto) return JSON.stringify(payload) || ''; + if (this.encryptedPayload === undefined) throw new Error('Encryption error: request payload not prepared'); - return JSON.stringify(typeof encrypted === 'string' ? encrypted : encode(encrypted)); + return this.encryptedPayload; } } diff --git a/src/core/endpoints/history/get_history.ts b/src/core/endpoints/history/get_history.ts index 99c970c96..afdc20b32 100644 --- a/src/core/endpoints/history/get_history.ts +++ b/src/core/endpoints/history/get_history.ts @@ -139,18 +139,21 @@ export class GetHistoryRequest extends AbstractRequest { - const processedPayload = this.processPayload(payload.message); - const item: History.GetHistoryResponse['messages'][number] = { - entry: processedPayload.payload, - timetoken: payload.timetoken, - }; - - if (processedPayload.error) item.error = processedPayload.error; - if (payload.meta) item.meta = payload.meta; - - return item; - }), + // Messages processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + messages: await Promise.all( + messages.map(async (payload) => { + const processedPayload = await this.processPayload(payload.message); + const item: History.GetHistoryResponse['messages'][number] = { + entry: processedPayload.payload, + timetoken: payload.timetoken, + }; + + if (processedPayload.error) item.error = processedPayload.error; + if (payload.meta) item.meta = payload.meta; + + return item; + }), + ), startTimeToken, endTimeToken, }; @@ -179,7 +182,7 @@ export class GetHistoryRequest extends AbstractRequest { const { crypto, logVerbosity } = this.parameters; if (!crypto || typeof payload !== 'string') return { payload }; @@ -187,7 +190,7 @@ export class GetHistoryRequest extends AbstractRequest { + /** + * Stringified encrypted payload (computed by {@link prepare} when `crypto` is set). + */ + private encryptedPayload?: string; + /** * Construct data publish request. * @@ -163,6 +167,15 @@ export class PublishRequest extends AbstractRequest | void { + const { crypto, message } = this.parameters; + if (!crypto) return; + + return encryptMessagePayload(crypto, message).then((encrypted) => { + this.encryptedPayload = encrypted; + }); + } + async parse(response: TransportResponse): Promise { return { timetoken: this.deserializeResponse(response)[2] }; } @@ -201,20 +214,19 @@ export class PublishRequest extends AbstractRequest { - const subscribable = envelope.b === undefined ? envelope.c : envelope.b; - return ( - (this.parameters.channels && this.parameters.channels.includes(subscribable)) || - (this.parameters.channelGroups && this.parameters.channelGroups.includes(subscribable)) - ); - }) - .map((envelope) => { + const envelopes = serviceResponse.m.filter((envelope) => { + const subscribable = envelope.b === undefined ? envelope.c : envelope.b; + return ( + (this.parameters.channels && this.parameters.channels.includes(subscribable)) || + (this.parameters.channelGroups && this.parameters.channelGroups.includes(subscribable)) + ); + }); + + // Envelopes processed concurrently (payload decryption is asynchronous); `Promise.all` preserves order. + const parsedEvents = await Promise.all( + envelopes.map(async (envelope) => { let { e: eventType } = envelope; // Events delivered on a presence channel are always presence events: the `-pnpres` suffix wins over @@ -988,20 +990,20 @@ export class BaseSubscribeRequest extends AbstractRequest event !== undefined); + }), + ); + const events: Subscription.SubscriptionResponse['messages'] = parsedEvents.filter( + (event): event is Subscription.SubscriptionResponse['messages'][number] => event !== undefined, + ); return { cursor: { timetoken: serviceResponse.t.t, region: serviceResponse.t.r }, @@ -1109,9 +1114,9 @@ export class BaseSubscribeRequest extends AbstractRequest { const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); - const [message, decryptionError] = this.decryptedData(envelope.d); + const [message, decryptionError] = await this.decryptedData(envelope.d); // Backward compatibility with deprecated properties. const actualChannel = subscription !== null ? channel : null; @@ -1263,9 +1268,9 @@ export class BaseSubscribeRequest extends AbstractRequest { const [channel, subscription] = this.subscriptionChannelFromEnvelope(envelope); - const [file, decryptionError] = this.decryptedData(envelope.d); + const [file, decryptionError] = await this.decryptedData(envelope.d); let errorMessage = decryptionError; // Basic file event payload. @@ -1308,14 +1313,14 @@ export class BaseSubscribeRequest extends AbstractRequest(data: Payload): [T, string | undefined] { + private async decryptedData(data: Payload): Promise<[T, string | undefined]> { if (!this.parameters.crypto || typeof data !== 'string') return [data as T, undefined]; let payload: Payload | null; let error: string | undefined; try { - const decryptedData = this.parameters.crypto.decrypt(data); + const decryptedData = await this.parameters.crypto.decrypt(data); payload = decryptedData instanceof ArrayBuffer ? JSON.parse(SubscribeRequest.decoder.decode(decryptedData)) diff --git a/src/core/interfaces/crypto-module.ts b/src/core/interfaces/crypto-module.ts index 669eb2399..a35b9fd38 100644 --- a/src/core/interfaces/crypto-module.ts +++ b/src/core/interfaces/crypto-module.ts @@ -5,6 +5,8 @@ import { PubNubFileConstructor, PubNubFileInterface } from '../types/file'; import { LoggerManager } from '../components/logger-manager'; import { Payload } from '../types/api'; +import { MaybePromise } from './crypto-primitives'; +import { PubNubError } from '../../errors/pubnub-error'; /** * Crypto module configuration. @@ -43,14 +45,14 @@ export type CryptorConfiguration = { * * @deprecated Instead use {@link cryptoModule} for data encryption. */ - customEncrypt?: (data: string | Payload) => string; + customEncrypt?: (data: string | Payload) => MaybePromise; /** * Custom data decryption method. * * @deprecated Instead use {@link cryptoModule} for data decryption. */ - customDecrypt?: (data: string) => string; + customDecrypt?: (data: string) => MaybePromise; /** * Registered loggers' manager. @@ -83,9 +85,9 @@ export interface ICryptoModule { * * @param data - Data which should be encrypted using `CryptoModule`. * - * @returns Data encryption result. + * @returns Asynchronous data encryption result. */ - encrypt(data: ArrayBuffer | string): ArrayBuffer | string; + encrypt(data: ArrayBuffer | string): Promise; /** * Encrypt file object. @@ -112,9 +114,9 @@ export interface ICryptoModule { * * @param data - Dta which should be encrypted using `CryptoModule`. * - * @returns Data decryption result. + * @returns Asynchronous data decryption result. */ - decrypt(data: ArrayBuffer | string): ArrayBuffer | Payload | null; + decrypt(data: ArrayBuffer | string): Promise; /** * Decrypt file object. @@ -194,6 +196,10 @@ export abstract class AbstractCryptoModule implements ICryptoModule { // endregion constructor(configuration: CryptoModuleConfiguration) { + // AES-GCM cryptor is decrypt-only and can be registered only as an additional cryptor. + if ((configuration.default as { identifier?: string } | undefined)?.identifier === 'ACGM') + throw new PubNubError("Crypto module error: AES-GCM cryptor (ACGM) can't be used as default cryptor."); + this.defaultCryptor = configuration.default; this.cryptors = configuration.cryptors ?? []; } @@ -219,9 +225,9 @@ export abstract class AbstractCryptoModule implements ICryptoModule { * * @param data - Data which should be encrypted using {@link ICryptoModule}. * - * @returns Data encryption result. + * @returns Asynchronous data encryption result. */ - abstract encrypt(data: ArrayBuffer | string): ArrayBuffer | string; + abstract encrypt(data: ArrayBuffer | string): Promise; /** * Encrypt file object. @@ -248,9 +254,9 @@ export abstract class AbstractCryptoModule implements ICryptoModule { * * @param data - Dta which should be encrypted using `ICryptoModule`. * - * @returns Data decryption result. + * @returns Asynchronous data decryption result. */ - abstract decrypt(data: ArrayBuffer | string): ArrayBuffer | Payload | null; + abstract decrypt(data: ArrayBuffer | string): Promise; /** * Decrypt file object. diff --git a/src/core/interfaces/crypto-primitives.ts b/src/core/interfaces/crypto-primitives.ts new file mode 100644 index 000000000..126887249 --- /dev/null +++ b/src/core/interfaces/crypto-primitives.ts @@ -0,0 +1,60 @@ +/** + * Platform-specific cryptographic primitives. + * + * Core SDK code doesn't depend on any concrete crypto implementation. Each platform entry point injects + * primitives backed by the native crypto available on it (`crypto.subtle`, `node:crypto` or `@noble/*`). + */ + +/** + * Value which can be returned synchronously or asynchronously. + */ +export type MaybePromise = T | Promise; + +/** + * Cryptographic primitives required by the SDK cryptors. + * + * @internal + */ +export interface CryptoPrimitives { + /** + * Compute SHA-256 digest. + * + * @param data - Data for which digest should be computed. + * + * @returns 32 bytes digest. + */ + sha256(data: Uint8Array): Promise; + + /** + * Encrypt data using AES-CBC with PKCS#7 padding. + * + * @param key - 16, 24 or 32 bytes long raw key. + * @param iv - 16 bytes long initialization vector. + * @param data - Data which should be encrypted. + * + * @returns Encrypted data. + */ + aesCbcEncrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise; + + /** + * Decrypt data using AES-CBC with PKCS#7 padding. + * + * @param key - 16, 24 or 32 bytes long raw key. + * @param iv - 16 bytes long initialization vector. + * @param data - Data which should be decrypted. + * + * @returns Decrypted data. + * + * @throws Error if data can't be decrypted (for example: wrong key or malformed padding). + */ + aesCbcDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise; + + /** + * Generate cryptographically secure random bytes. + * + * @param length - Number of bytes which should be generated. + * + * @returns Random bytes. + */ + randomBytes(length: number): Uint8Array; +} diff --git a/src/core/pubnub-common.ts b/src/core/pubnub-common.ts index 6d0e972b6..50279c727 100644 --- a/src/core/pubnub-common.ts +++ b/src/core/pubnub-common.ts @@ -249,6 +249,16 @@ export class PubNubCore< */ private readonly cryptography?: Cryptography; + /** + * Tail of the asynchronous requests preparation queue. + * + * Used to schedule requests which require asynchronous preparation (for example, payload encryption) in the + * same order in which they have been called. + * + * @internal + */ + private requestsPreparation: Promise = Promise.resolve(); + /** * Legacy crypto (legacy data encryption / decryption and request signature support). * @@ -1208,6 +1218,20 @@ export class PubNubCore< throw new PubNubError('Validation failed, check status for details', validationError); } + // Asynchronous request pre-processing (for example, payload encryption). + const preparation = request.prepare(); + if (preparation) { + // Requests which require preparation are scheduled in call order (same as synchronous requests). + const orderedPreparation = this.requestsPreparation.then(() => preparation); + this.requestsPreparation = orderedPreparation.catch(() => {}); + + try { + await orderedPreparation; + } catch (error) { + return this.handleRequestError(error as Error, request.operation(), callback); + } + } + // Complete request configuration. const transportRequest = request.request(); const operation = request.operation(); @@ -1267,31 +1291,50 @@ export class PubNubCore< return parsed; }) - .catch((error: Error) => { - const apiError = !(error instanceof PubNubAPIError) ? PubNubAPIError.create(error) : error; + .catch((error: Error) => this.handleRequestError(error, operation, callback)); + } - // Notify callback (if possible). - if (callback) { - if (apiError.category !== Categories.PNCancelledCategory) { - this.logger.error('PubNub', () => ({ - messageType: 'error', - message: apiError.toPubNubError(operation, 'REST API request processing error, check status for details'), - })); - } + /** + * Handle REST API request processing error. + * + * @internal + * + * @param error - Request processing error. + * @param operation - Type of failed REST API operation. + * @param [callback] - Request completion handler callback. + * + * @returns `void` in case if `callback` provided. + * + * @throws PubNubError in case if `callback` not provided. + */ + private handleRequestError( + error: Error, + operation: RequestOperation, + callback?: ResultCallback, + ): void { + const apiError = !(error instanceof PubNubAPIError) ? PubNubAPIError.create(error) : error; + + // Notify callback (if possible). + if (callback) { + if (apiError.category !== Categories.PNCancelledCategory) { + this.logger.error('PubNub', () => ({ + messageType: 'error', + message: apiError.toPubNubError(operation, 'REST API request processing error, check status for details'), + })); + } - return callback(apiError.toStatus(operation), null); - } + return callback(apiError.toStatus(operation), null); + } - const pubNubError = apiError.toPubNubError( - operation, - 'REST API request processing error, check status for details', - ); + const pubNubError = apiError.toPubNubError( + operation, + 'REST API request processing error, check status for details', + ); - if (apiError.category !== Categories.PNCancelledCategory) - this.logger.error('PubNub', () => ({ messageType: 'error', message: pubNubError })); + if (apiError.category !== Categories.PNCancelledCategory) + this.logger.error('PubNub', () => ({ messageType: 'error', message: pubNubError })); - throw pubNubError; - }); + throw pubNubError; } /** @@ -4921,17 +4964,17 @@ export class PubNubCore< * @param [customCipherKey] - Cipher key which should be used to encrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data encryption result as a string. + * @returns Asynchronous data encryption result as a string. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ - public encrypt(data: string | Payload, customCipherKey?: string): string { + public async encrypt(data: string | Payload, customCipherKey?: string): Promise { this.logger.warn('PubNub', "'encrypt' is deprecated. Use cryptoModule instead."); const cryptoModule = this._configuration.getCryptoModule(); if (!customCipherKey && cryptoModule && typeof data === 'string') { - const encrypted = cryptoModule.encrypt(data); + const encrypted = await cryptoModule.encrypt(data); return typeof encrypted === 'string' ? encrypted : encode(encrypted); } @@ -4950,16 +4993,16 @@ export class PubNubCore< * @param [customCipherKey] - Cipher key which should be used to decrypt data. **Deprecated:** * use {@link Configuration#cryptoModule|cryptoModule} instead. * - * @returns Data decryption result as an object. + * @returns Asynchronous data decryption result as an object. * * @deprecated Use {@link Configuration#cryptoModule|cryptoModule} instead. */ - public decrypt(data: string, customCipherKey?: string): Payload | null { + public async decrypt(data: string, customCipherKey?: string): Promise { this.logger.warn('PubNub', "'decrypt' is deprecated. Use cryptoModule instead."); const cryptoModule = this._configuration.getCryptoModule(); if (!customCipherKey && cryptoModule) { - const decrypted = cryptoModule.decrypt(data); + const decrypted = await cryptoModule.decrypt(data); return decrypted instanceof ArrayBuffer ? JSON.parse(new TextDecoder().decode(decrypted)) : decrypted; } diff --git a/src/core/utils.ts b/src/core/utils.ts index e66bb8102..4760ed8a1 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -4,6 +4,8 @@ * @internal */ +import type { ICryptoModule } from './interfaces/crypto-module'; +import { encode } from './components/base64_codec'; import { Payload, Query } from './types/api'; /** @@ -210,3 +212,21 @@ export const messageFingerprint = (input: Payload) => { while (len-- > 0) mfp[0] = (mfp[0] << 5) - mfp[0] + msg.charCodeAt(walk++); return mfp[0].toString(16).padStart(8, '0'); }; + +/** + * Encrypt message payload which should be sent as part of request URL or body. + * + * @param crypto - Module which should be used to encrypt data. + * @param payload - User-provided data which should be encrypted. + * + * @returns Stringified encrypted payload (base64 encoded if encryption result is binary). + * + * @throws {Error} in case if provided `payload` or results of `encryption` can't be stringified. + * + * @internal + */ +export const encryptMessagePayload = async (crypto: ICryptoModule, payload: Payload): Promise => { + const encrypted = await crypto.encrypt(JSON.stringify(payload)); + + return JSON.stringify(typeof encrypted === 'string' ? encrypted : encode(encrypted)); +}; diff --git a/src/crypto/modules/LegacyCryptoModule.ts b/src/crypto/modules/LegacyCryptoModule.ts index 2f633220b..83f2af32e 100644 --- a/src/crypto/modules/LegacyCryptoModule.ts +++ b/src/crypto/modules/LegacyCryptoModule.ts @@ -45,14 +45,14 @@ export default class LegacyCryptoModule implements ICryptoModule { * @returns The encrypted data as a string * @throws {Error} When data is null/undefined or encryption fails */ - encrypt(data: ArrayBuffer | string): ArrayBuffer | string { + async encrypt(data: ArrayBuffer | string): Promise { if (data === null || data === undefined) { throw new Error('Encryption data cannot be null or undefined'); } try { const plaintext = typeof data === 'string' ? data : Buffer.from(new Uint8Array(data)).toString('utf8'); - const encrypted = this.legacy.encrypt(plaintext); + const encrypted = await this.legacy.encrypt(plaintext); if (typeof encrypted !== 'string') { throw new Error('Legacy encryption failed: expected string result'); @@ -82,7 +82,7 @@ export default class LegacyCryptoModule implements ICryptoModule { * @returns The decrypted payload, or null if decryption fails * @throws {Error} When data is null/undefined/empty or decryption fails */ - decrypt(data: ArrayBuffer | string): ArrayBuffer | Payload | null { + async decrypt(data: ArrayBuffer | string): Promise { if (data === null || data === undefined) { throw new Error('Decryption data cannot be null or undefined'); } @@ -101,7 +101,7 @@ export default class LegacyCryptoModule implements ICryptoModule { ciphertextB64 = Buffer.from(new Uint8Array(data)).toString('base64'); } - const decrypted = this.legacy.decrypt(ciphertextB64); + const decrypted = await this.legacy.decrypt(ciphertextB64); // The legacy decrypt method returns Payload | null, so no unsafe casting needed return decrypted; diff --git a/src/crypto/modules/NodeCryptoModule/ICryptor.ts b/src/crypto/modules/NodeCryptoModule/ICryptor.ts index 81cfde5b6..a26016062 100644 --- a/src/crypto/modules/NodeCryptoModule/ICryptor.ts +++ b/src/crypto/modules/NodeCryptoModule/ICryptor.ts @@ -2,6 +2,8 @@ * Cryptor module. */ +import { MaybePromise } from '../../../core/interfaces/crypto-primitives'; + /** * Data encrypted by {@link NodeCryptoModule}. */ @@ -62,7 +64,7 @@ export interface ICryptor { * * @throws Error if unknown data type has been passed. */ - encrypt(data: BufferSource | string): EncryptedDataType; + encrypt(data: BufferSource | string): MaybePromise; /** * Encrypt provided source {@link Readable} stream. @@ -90,7 +92,7 @@ export interface ICryptor { * * @throws Error if unknown data type has been passed. */ - decrypt(data: EncryptedDataType): ArrayBuffer; + decrypt(data: EncryptedDataType): MaybePromise; /** * Decrypt provided encrypted stream object. diff --git a/src/crypto/modules/NodeCryptoModule/ILegacyCryptor.ts b/src/crypto/modules/NodeCryptoModule/ILegacyCryptor.ts index 8fd6ae953..e70df0b06 100644 --- a/src/crypto/modules/NodeCryptoModule/ILegacyCryptor.ts +++ b/src/crypto/modules/NodeCryptoModule/ILegacyCryptor.ts @@ -2,6 +2,7 @@ * Legacy cryptor module. */ +import { MaybePromise } from '../../../core/interfaces/crypto-primitives'; import PubNubFile, { PubNubFileParameters } from '../../../file/modules/node'; import { PubNubFileConstructor } from '../../../core/types/file'; import { Payload } from '../../../core/types/api'; @@ -30,7 +31,7 @@ export interface ILegacyCryptor { * * @throws Error if unknown data type has been passed. */ - encrypt(data: string): EncryptedDataType; + encrypt(data: string): MaybePromise; /** * Encrypt provided source {@link PubNub} File object. @@ -63,7 +64,7 @@ export interface ILegacyCryptor { * * @throws Error if unknown data type has been passed. */ - decrypt(data: EncryptedDataType): Payload | null; + decrypt(data: EncryptedDataType): MaybePromise; /** * Decrypt provided encrypted {@link PubNub} File object. diff --git a/src/crypto/modules/NodeCryptoModule/aesGcmCryptor.ts b/src/crypto/modules/NodeCryptoModule/aesGcmCryptor.ts new file mode 100644 index 000000000..c85507dd5 --- /dev/null +++ b/src/crypto/modules/NodeCryptoModule/aesGcmCryptor.ts @@ -0,0 +1,213 @@ +/** + * AES-GCM cryptor module. + * + * @internal + */ + +import { PassThrough } from 'stream'; + +import { toArrayBuffer } from '../../../core/components/cryptography/bytes'; +import { ICryptor, EncryptedDataType, EncryptedStream } from './ICryptor'; +import NodeCryptoPrimitives from '../../primitives/node'; + +/** + * AES-GCM cryptor. + * + * Decrypt-only AES-256-GCM cryptor for payloads produced by other PubNub SDKs. Cryptor metadata stored in the + * header is `nonce (12 bytes)` and the authentication tag (16 bytes) is appended to the ciphertext: + * `ciphertext || authentication tag`. + * + * **Note:** Encryption isn't supported and cryptor can't be used as crypto module default cryptor. + * + * @internal + */ +export default class AesGcmCryptor implements ICryptor { + /** + * Cryptor unique identifier. + */ + static IDENTIFIER = 'ACGM'; + + /** + * Nonce length. + */ + static NONCE_LENGTH = 12; + + /** + * Authentication tag length. + */ + static TAG_LENGTH = 16; + + /** + * Cryptor metadata length (nonce only). + */ + static METADATA_LENGTH = AesGcmCryptor.NONCE_LENGTH; + + /** + * Generic decryption error message (doesn't reveal which part of the payload failed authentication). + */ + static DECRYPTION_ERROR = 'Decryption error: unable to decrypt data'; + + /** + * Unsupported encryption error message. + */ + static ENCRYPTION_ERROR = 'Encryption error: AES-GCM cryptor (ACGM) supports decryption only.'; + + /** + * {@link string|String} to {@link ArrayBuffer} response decoder. + */ + static encoder = new TextEncoder(); + + /** + * Native crypto primitives. + */ + private static primitives = new NodeCryptoPrimitives(); + + /** + * Data decryption cipher key. + */ + cipherKey: string; + + /** + * Raw decryption key (SHA-256 digest of the cipher key). + */ + private key?: Promise; + + constructor({ cipherKey }: { cipherKey: string }) { + this.cipherKey = cipherKey; + } + + // -------------------------------------------------------- + // --------------------- Encryption ----------------------- + // -------------------------------------------------------- + // region Encryption + + encrypt(_data: BufferSource | string): EncryptedDataType { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + } + + async encryptStream(_stream: NodeJS.ReadableStream): Promise { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + } + // endregion + + // -------------------------------------------------------- + // --------------------- Decryption ----------------------- + // -------------------------------------------------------- + // region Decryption + + async decrypt(input: EncryptedDataType): Promise { + const data = typeof input.data === 'string' ? AesGcmCryptor.encoder.encode(input.data) : input.data; + + if (!input.metadata || input.metadata.byteLength !== AesGcmCryptor.METADATA_LENGTH) + throw new Error('Decryption error: invalid metadata'); + if (data.byteLength <= AesGcmCryptor.TAG_LENGTH) throw new Error('Decryption error: empty content'); + + return toArrayBuffer(await this.decryptBytes(new Uint8Array(input.metadata), new Uint8Array(data))); + } + + /** + * Decrypt provided encrypted stream object. + * + * **Note:** AES-GCM authenticates the whole message (authentication tag is the last 16 bytes of the stream), so the + * stream is buffered in memory and decrypted data is written to the output stream only after successful + * authentication. + * + * @param stream - Encrypted stream object for decryption. + * + * @returns Decrypted data as {@link Readable} stream. + */ + async decryptStream(stream: EncryptedStream) { + if (stream.metadataLength !== AesGcmCryptor.METADATA_LENGTH) throw new Error('Decryption error: invalid metadata'); + + const decryptedStream = new PassThrough(); + const chunks: Buffer[] = []; + + const onReadable = () => { + let data = stream.stream.read(); + + while (data !== null) { + if (data) chunks.push(typeof data === 'string' ? Buffer.from(data) : data); + data = stream.stream.read(); + } + }; + stream.stream.on('readable', onReadable); + stream.stream.on('end', () => { + const payload = Buffer.concat(chunks); + + // Content validated asynchronously (like authentication) to report errors after the stream has been returned. + const decrypt = async () => { + if (payload.byteLength <= AesGcmCryptor.METADATA_LENGTH + AesGcmCryptor.TAG_LENGTH) + throw new Error('Decryption error: empty content'); + + return this.decryptBytes( + payload.subarray(0, AesGcmCryptor.METADATA_LENGTH), + payload.subarray(AesGcmCryptor.METADATA_LENGTH), + ); + }; + + decrypt().then( + (decrypted) => decryptedStream.end(Buffer.from(decrypted)), + (error) => decryptedStream.destroy(error), + ); + }); + + // Header has been read from the `readable` event handler: drain data which already has been buffered, because + // a fully buffered source may not emit `readable` again. + onReadable(); + + return decryptedStream; + } + // endregion + + // -------------------------------------------------------- + // ----------------------- Helpers ------------------------ + // -------------------------------------------------------- + // region Helpers + + get identifier() { + return AesGcmCryptor.IDENTIFIER; + } + + /** + * Authenticate and decrypt data. + * + * @param nonce - Cryptor metadata (nonce). + * @param data - Encrypted data with appended authentication tag. + * + * @returns Decrypted data. + * + * @throws Error with generic message if data can't be authenticated. + */ + private async decryptBytes(nonce: Uint8Array, data: Uint8Array) { + try { + return await AesGcmCryptor.primitives.aesGcmDecrypt(await this.getKey(), nonce, data); + } catch (_error) { + // Native error intentionally not exposed: wrong key and modified payload should be indistinguishable. + throw new Error(AesGcmCryptor.DECRYPTION_ERROR); + } + } + + /** + * Compute raw decryption key (cached). + * + * @returns SHA-256 digest of the cipher key. + */ + private getKey() { + if (!this.key) { + this.key = AesGcmCryptor.primitives.sha256(AesGcmCryptor.encoder.encode(this.cipherKey)); + this.key.catch(() => (this.key = undefined)); + } + + return this.key; + } + // endregion + + /** + * Serialize cryptor information to string. + * + * @returns Serialized cryptor information. + */ + toString() { + return 'AesGcmCryptor {}'; + } +} diff --git a/src/crypto/modules/NodeCryptoModule/legacyCryptor.ts b/src/crypto/modules/NodeCryptoModule/legacyCryptor.ts index 7fc3720f5..e2a9a9d02 100644 --- a/src/crypto/modules/NodeCryptoModule/legacyCryptor.ts +++ b/src/crypto/modules/NodeCryptoModule/legacyCryptor.ts @@ -14,6 +14,7 @@ import { PubNubError } from '../../../errors/pubnub-error'; import { isSensitiveLogKey } from '../../../core/utils'; import { ILegacyCryptor } from './ILegacyCryptor'; import { EncryptedDataType } from './ICryptor'; +import NodeCryptoPrimitives from '../../primitives/node'; import FileCryptor from '../node'; /** @@ -39,7 +40,7 @@ export default class LegacyCryptor implements ILegacyCryptor { constructor(config: CryptorConfiguration) { this.config = config; - this.cryptor = new Crypto({ ...config }); + this.cryptor = new Crypto({ ...config }, new NodeCryptoPrimitives()); this.fileCryptor = new FileCryptor(); } @@ -65,11 +66,11 @@ export default class LegacyCryptor implements ILegacyCryptor { // -------------------------------------------------------- // region Encryption - encrypt(data: string): EncryptedDataType { + async encrypt(data: string): Promise { if (data.length === 0) throw new Error('Encryption error: empty content'); return { - data: this.cryptor.encrypt(data), + data: await this.cryptor.encrypt(data), metadata: null, }; } @@ -86,7 +87,7 @@ export default class LegacyCryptor implements ILegacyCryptor { // -------------------------------------------------------- // region Decryption - decrypt(encryptedData: EncryptedDataType) { + async decrypt(encryptedData: EncryptedDataType) { const data = typeof encryptedData.data === 'string' ? encryptedData.data : encode(encryptedData.data); return this.cryptor.decrypt(data); diff --git a/src/crypto/modules/NodeCryptoModule/nodeCryptoModule.ts b/src/crypto/modules/NodeCryptoModule/nodeCryptoModule.ts index e44588775..14511b064 100644 --- a/src/crypto/modules/NodeCryptoModule/nodeCryptoModule.ts +++ b/src/crypto/modules/NodeCryptoModule/nodeCryptoModule.ts @@ -14,13 +14,14 @@ import { PubNubError } from '../../../errors/pubnub-error'; import { EncryptedDataType, ICryptor } from './ICryptor'; import { ILegacyCryptor } from './ILegacyCryptor'; import AesCbcCryptor from './aesCbcCryptor'; +import AesGcmCryptor from './aesGcmCryptor'; import LegacyCryptor from './legacyCryptor'; import { Payload } from '../../../core/types/api'; /** * Re-export bundled cryptors. */ -export { LegacyCryptor, AesCbcCryptor }; +export { LegacyCryptor, AesCbcCryptor, AesGcmCryptor }; /** * Crypto module cryptors interface. @@ -89,7 +90,10 @@ export class NodeCryptoModule extends AbstractCryptoModule { ...config, useRandomIVs: config.useRandomIVs ?? true, }), - cryptors: [new AesCbcCryptor({ cipherKey: config.cipherKey })], + cryptors: [ + new AesCbcCryptor({ cipherKey: config.cipherKey }), + new AesGcmCryptor({ cipherKey: config.cipherKey }), + ], }); } @@ -103,10 +107,37 @@ export class NodeCryptoModule extends AbstractCryptoModule { ...config, useRandomIVs: config.useRandomIVs ?? true, }), + new AesGcmCryptor({ cipherKey: config.cipherKey }), ], }); } + /** + * Construct decrypt-only AES-GCM cryptor. + * + * Modules created with {@link aesCbcCryptoModule}, {@link legacyCryptoModule} or `cipherKey` already register it. + * Use it to let a custom crypto module decrypt AES-GCM (`ACGM`) payloads produced by other PubNub SDKs: + * + * @example + * ```typescript + * const cryptoModule = new PubNub.CryptoModule({ + * default: myCryptor, + * cryptors: [PubNub.CryptoModule.aesGcmCryptor({ cipherKey: 'my-key' })], + * }); + * ``` + * + * @param config - Cryptor configuration options. + * + * @returns AES-GCM cryptor which can be used only as an additional (decryption) cryptor. + * + * @throws Error if `config.cipherKey` not set. + */ + static aesGcmCryptor(config: CryptorConfiguration): ICryptor { + if (!config.cipherKey) throw new PubNubError('Crypto module error: cipher key not set.'); + + return new AesGcmCryptor({ cipherKey: config.cipherKey }); + } + /** * Construct crypto module with `cryptor` as default for data encryption and decryption. * @@ -124,12 +155,12 @@ export class NodeCryptoModule extends AbstractCryptoModule { // -------------------------------------------------------- // region Encryption - encrypt(data: ArrayBuffer | string) { + async encrypt(data: ArrayBuffer | string): Promise { // Encrypt data. const encrypted = data instanceof ArrayBuffer && this.defaultCryptor.identifier === NodeCryptoModule.LEGACY_IDENTIFIER - ? (this.defaultCryptor as ILegacyCryptor).encrypt(NodeCryptoModule.decoder.decode(data)) - : (this.defaultCryptor as ICryptor).encrypt(data); + ? await (this.defaultCryptor as ILegacyCryptor).encrypt(NodeCryptoModule.decoder.decode(data)) + : await (this.defaultCryptor as ICryptor).encrypt(data); if (!encrypted.metadata) return encrypted.data; @@ -153,7 +184,7 @@ export class NodeCryptoModule extends AbstractCryptoModule { return (this.defaultCryptor as ILegacyCryptor).encryptFile(file, File); if (file.data instanceof Buffer) { - const encryptedData = this.encrypt(file.data); + const encryptedData = await this.encrypt(file.data); return File.create({ name: file.name, @@ -198,7 +229,7 @@ export class NodeCryptoModule extends AbstractCryptoModule { // -------------------------------------------------------- // region Decryption - decrypt(data: ArrayBuffer | string): ArrayBuffer | Payload | null { + async decrypt(data: ArrayBuffer | string): Promise { const encryptedData = Buffer.from(typeof data === 'string' ? decode(data) : data); const header = CryptorHeader.tryParse( encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length), @@ -235,7 +266,7 @@ export class NodeCryptoModule extends AbstractCryptoModule { return File.create({ name: file.name, - data: Buffer.from(this.decrypt(file.data) as ArrayBuffer), + data: Buffer.from((await this.decrypt(file.data)) as ArrayBuffer), }); } diff --git a/src/crypto/modules/WebCryptoModule/ICryptor.ts b/src/crypto/modules/WebCryptoModule/ICryptor.ts index aa6080ce9..871fe2b67 100644 --- a/src/crypto/modules/WebCryptoModule/ICryptor.ts +++ b/src/crypto/modules/WebCryptoModule/ICryptor.ts @@ -2,6 +2,8 @@ * Cryptor module. */ +import { MaybePromise } from '../../../core/interfaces/crypto-primitives'; + /** * Data encrypted by {@link CryptoModule}. */ @@ -42,7 +44,7 @@ export interface ICryptor { * * @throws Error if unknown data type has been passed. */ - encrypt(data: ArrayBuffer | string): EncryptedDataType; + encrypt(data: ArrayBuffer | string): MaybePromise; /** * Encrypt provided source {@link ArrayBuffer}. @@ -70,7 +72,7 @@ export interface ICryptor { * * @throws Error if unknown data type has been passed. */ - decrypt(data: EncryptedDataType): ArrayBuffer; + decrypt(data: EncryptedDataType): MaybePromise; /** * Decrypt provided encrypted {@link ArrayBuffer} File object. diff --git a/src/crypto/modules/WebCryptoModule/ILegacyCryptor.ts b/src/crypto/modules/WebCryptoModule/ILegacyCryptor.ts index a0afa95e5..fec0b7a50 100644 --- a/src/crypto/modules/WebCryptoModule/ILegacyCryptor.ts +++ b/src/crypto/modules/WebCryptoModule/ILegacyCryptor.ts @@ -2,6 +2,7 @@ * Legacy cryptor module. */ +import { MaybePromise } from '../../../core/interfaces/crypto-primitives'; import { PubNubFile, PubNubFileParameters } from '../../../file/modules/web'; import { PubNubFileConstructor } from '../../../core/types/file'; import { Payload } from '../../../core/types/api'; @@ -32,7 +33,7 @@ export interface ILegacyCryptor { * * @throws Error if unknown data type has been passed. */ - encrypt(data: ArrayBuffer | string): EncryptedDataType; + encrypt(data: ArrayBuffer | string): MaybePromise; /** * Encrypt provided source {@link PubNub} File object. @@ -65,7 +66,7 @@ export interface ILegacyCryptor { * * @throws Error if unknown data type has been passed. */ - decrypt(data: EncryptedDataType): Payload | null; + decrypt(data: EncryptedDataType): MaybePromise; /** * Decrypt provided encrypted {@link PubNub} File object. diff --git a/src/crypto/modules/WebCryptoModule/aesCbcCryptor.ts b/src/crypto/modules/WebCryptoModule/aesCbcCryptor.ts index ebd38a743..04b49c3a3 100644 --- a/src/crypto/modules/WebCryptoModule/aesCbcCryptor.ts +++ b/src/crypto/modules/WebCryptoModule/aesCbcCryptor.ts @@ -2,8 +2,8 @@ * AES-CBC cryptor module. */ -import cryptoJS from '../../../core/components/cryptography/hmac-sha256'; -import { decode } from '../../../core/components/base64_codec'; +import { toArrayBuffer } from '../../../core/components/cryptography/bytes'; +import WebCryptoPrimitives from '../../primitives/web'; import { ICryptor, EncryptedDataType } from './ICryptor'; /** @@ -27,20 +27,27 @@ export default class AesCbcCryptor implements ICryptor { */ static decoder = new TextDecoder(); + /** + * Native crypto primitives. + * + * @internal + */ + private static primitives = new WebCryptoPrimitives(); + /** * Data encryption / decryption cipher key. */ cipherKey: string; - /* eslint-disable @typescript-eslint/no-explicit-any */ - encryptedKey: any; - /* eslint-disable @typescript-eslint/no-explicit-any */ - CryptoJS: any; + /** + * Raw encryption key (SHA-256 digest of the cipher key). + * + * @internal + */ + private key?: Promise; constructor({ cipherKey }: { cipherKey: string }) { this.cipherKey = cipherKey; - this.CryptoJS = cryptoJS; - this.encryptedKey = this.CryptoJS.SHA256(cipherKey); } // -------------------------------------------------------- @@ -48,29 +55,22 @@ export default class AesCbcCryptor implements ICryptor { // -------------------------------------------------------- // region Encryption - encrypt(data: ArrayBuffer | string): EncryptedDataType { - const stringData = typeof data === 'string' ? data : AesCbcCryptor.decoder.decode(data); - if (stringData.length === 0) throw new Error('encryption error. empty content'); - const abIv = this.getIv(); - - return { - metadata: abIv, - data: decode( - this.CryptoJS.AES.encrypt(data, this.encryptedKey, { - iv: this.bufferToWordArray(abIv), - mode: this.CryptoJS.mode.CBC, - }).ciphertext.toString(this.CryptoJS.enc.Base64), - ), - }; + async encrypt(data: ArrayBuffer | string): Promise { + const plaintext = typeof data === 'string' ? AesCbcCryptor.encoder.encode(data) : new Uint8Array(data); + if (plaintext.byteLength === 0) throw new Error('encryption error. empty content'); + + return this.encryptFileData(plaintext); } - async encryptFileData(data: ArrayBuffer): Promise { - const key = await this.getKey(); + async encryptFileData(data: ArrayBuffer | Uint8Array): Promise { const iv = this.getIv(); - return { - data: await crypto.subtle.encrypt({ name: this.algo, iv: iv }, key, data), - metadata: iv, - }; + const encrypted = await AesCbcCryptor.primitives.aesCbcEncrypt( + await this.getKey(), + iv, + data instanceof Uint8Array ? data : new Uint8Array(data), + ); + + return { data: toArrayBuffer(encrypted), metadata: iv.buffer }; } // endregion @@ -79,27 +79,26 @@ export default class AesCbcCryptor implements ICryptor { // -------------------------------------------------------- // region Decryption - decrypt(encryptedData: EncryptedDataType) { - if (typeof encryptedData.data === 'string') - throw new Error('Decryption error: data for decryption should be ArrayBuffed.'); - - const iv = this.bufferToWordArray(new Uint8ClampedArray(encryptedData.metadata!)); - const data = this.bufferToWordArray(new Uint8ClampedArray(encryptedData.data)); - - return AesCbcCryptor.encoder.encode( - this.CryptoJS.AES.decrypt({ ciphertext: data }, this.encryptedKey, { - iv, - mode: this.CryptoJS.mode.CBC, - }).toString(this.CryptoJS.enc.Utf8), - ).buffer; + async decrypt(encryptedData: EncryptedDataType): Promise { + try { + return await this.decryptFileData(encryptedData); + } catch (error) { + if (error instanceof Error && error.message.startsWith('Decryption error')) throw error; + throw new Error(`Decryption error: ${error instanceof Error ? error.message : 'unable to decrypt data'}`); + } } async decryptFileData(encryptedData: EncryptedDataType): Promise { if (typeof encryptedData.data === 'string') throw new Error('Decryption error: data for decryption should be ArrayBuffed.'); - const key = await this.getKey(); - return crypto.subtle.decrypt({ name: this.algo, iv: encryptedData.metadata! }, key, encryptedData.data); + const decrypted = await AesCbcCryptor.primitives.aesCbcDecrypt( + await this.getKey(), + new Uint8Array(encryptedData.metadata!), + new Uint8Array(encryptedData.data), + ); + + return toArrayBuffer(decrypted); } // endregion @@ -112,53 +111,33 @@ export default class AesCbcCryptor implements ICryptor { return 'ACRH'; } - /** - * Cryptor algorithm. - * - * @returns Cryptor module algorithm. - */ - private get algo() { - return 'AES-CBC'; - } - /** * Generate random initialization vector. * * @returns Random initialization vector. - */ - - private getIv() { - return crypto.getRandomValues(new Uint8Array(AesCbcCryptor.BLOCK_SIZE)); - } - - /** - * Convert cipher key to the {@link Buffer}. * - * @returns SHA256 encoded cipher key {@link Buffer}. + * @internal */ - private async getKey() { - const bKey = AesCbcCryptor.encoder.encode(this.cipherKey); - const abHash = await crypto.subtle.digest('SHA-256', bKey.buffer); - return crypto.subtle.importKey('raw', abHash, this.algo, true, ['encrypt', 'decrypt']); + private getIv() { + return AesCbcCryptor.primitives.randomBytes(AesCbcCryptor.BLOCK_SIZE); } /** - * Convert bytes array to words array. + * Compute raw encryption key (cached). * - * @param b - Bytes array (buffer) which should be converted. + * @returns SHA-256 digest of the cipher key. * - * @returns Word sized array. + * @internal */ - private bufferToWordArray(b: string | any[] | Uint8Array | Uint8ClampedArray) { - const wa: number[] = []; - let i; - - for (i = 0; i < b.length; i += 1) { - wa[(i / 4) | 0] |= b[i] << (24 - 8 * i); + private getKey() { + if (!this.key) { + this.key = AesCbcCryptor.primitives.sha256(AesCbcCryptor.encoder.encode(this.cipherKey)); + this.key.catch(() => (this.key = undefined)); } - return this.CryptoJS.lib.WordArray.create(wa, b.length); + return this.key; } + // endregion /** * Serialize cryptor information to string. diff --git a/src/crypto/modules/WebCryptoModule/aesGcmCryptor.ts b/src/crypto/modules/WebCryptoModule/aesGcmCryptor.ts new file mode 100644 index 000000000..54fa22f95 --- /dev/null +++ b/src/crypto/modules/WebCryptoModule/aesGcmCryptor.ts @@ -0,0 +1,156 @@ +/** + * AES-GCM cryptor module. + */ + +import { toArrayBuffer } from '../../../core/components/cryptography/bytes'; +import WebCryptoPrimitives from '../../primitives/web'; +import { ICryptor, EncryptedDataType } from './ICryptor'; + +/** + * AES-GCM cryptor. + * + * Decrypt-only AES-256-GCM cryptor for payloads produced by other PubNub SDKs. Cryptor metadata stored in the + * header is `nonce (12 bytes)` and the authentication tag (16 bytes) is appended to the ciphertext: + * `ciphertext || authentication tag`. + * + * **Note:** Encryption isn't supported and cryptor can't be used as crypto module default cryptor. + */ +export default class AesGcmCryptor implements ICryptor { + /** + * Cryptor unique identifier. + */ + static IDENTIFIER = 'ACGM'; + + /** + * Nonce length. + */ + static NONCE_LENGTH = 12; + + /** + * Authentication tag length. + */ + static TAG_LENGTH = 16; + + /** + * Cryptor metadata length (nonce only). + */ + static METADATA_LENGTH = AesGcmCryptor.NONCE_LENGTH; + + /** + * Generic decryption error message (doesn't reveal which part of the payload failed authentication). + */ + static DECRYPTION_ERROR = 'Decryption error: unable to decrypt data'; + + /** + * Unsupported encryption error message. + */ + static ENCRYPTION_ERROR = 'Encryption error: AES-GCM cryptor (ACGM) supports decryption only.'; + + /** + * {@link string|String} to {@link ArrayBuffer} response decoder. + */ + static encoder = new TextEncoder(); + + /** + * Native crypto primitives. + * + * @internal + */ + private static primitives = new WebCryptoPrimitives(); + + /** + * Data decryption cipher key. + */ + cipherKey: string; + + /** + * Raw decryption key (SHA-256 digest of the cipher key). + * + * @internal + */ + private key?: Promise; + + constructor({ cipherKey }: { cipherKey: string }) { + this.cipherKey = cipherKey; + } + + // -------------------------------------------------------- + // --------------------- Encryption ----------------------- + // -------------------------------------------------------- + // region Encryption + + encrypt(_data: ArrayBuffer | string): EncryptedDataType { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + } + + async encryptFileData(_data: ArrayBuffer): Promise { + throw new Error(AesGcmCryptor.ENCRYPTION_ERROR); + } + // endregion + + // -------------------------------------------------------- + // --------------------- Decryption ----------------------- + // -------------------------------------------------------- + // region Decryption + + async decrypt(encryptedData: EncryptedDataType): Promise { + return this.decryptFileData(encryptedData); + } + + async decryptFileData(encryptedData: EncryptedDataType): Promise { + if (typeof encryptedData.data === 'string') + throw new Error('Decryption error: data for decryption should be ArrayBuffed.'); + if (!encryptedData.metadata || encryptedData.metadata.byteLength !== AesGcmCryptor.METADATA_LENGTH) + throw new Error('Decryption error: invalid metadata'); + if (encryptedData.data.byteLength <= AesGcmCryptor.TAG_LENGTH) throw new Error('Decryption error: empty content'); + + try { + const decrypted = await AesGcmCryptor.primitives.aesGcmDecrypt( + await this.getKey(), + new Uint8Array(encryptedData.metadata), + new Uint8Array(encryptedData.data), + ); + + return toArrayBuffer(decrypted); + } catch (_error) { + // Native error intentionally not exposed: wrong key and modified payload should be indistinguishable. + throw new Error(AesGcmCryptor.DECRYPTION_ERROR); + } + } + // endregion + + // -------------------------------------------------------- + // ----------------------- Helpers ------------------------ + // -------------------------------------------------------- + // region Helpers + + get identifier() { + return AesGcmCryptor.IDENTIFIER; + } + + /** + * Compute raw decryption key (cached). + * + * @returns SHA-256 digest of the cipher key. + * + * @internal + */ + private getKey() { + if (!this.key) { + this.key = AesGcmCryptor.primitives.sha256(AesGcmCryptor.encoder.encode(this.cipherKey)); + this.key.catch(() => (this.key = undefined)); + } + + return this.key; + } + // endregion + + /** + * Serialize cryptor information to string. + * + * @returns Serialized cryptor information. + */ + toString() { + return 'AesGcmCryptor {}'; + } +} diff --git a/src/crypto/modules/WebCryptoModule/legacyCryptor.ts b/src/crypto/modules/WebCryptoModule/legacyCryptor.ts index 28424818f..f2812d003 100644 --- a/src/crypto/modules/WebCryptoModule/legacyCryptor.ts +++ b/src/crypto/modules/WebCryptoModule/legacyCryptor.ts @@ -12,6 +12,7 @@ import { PubNubError } from '../../../errors/pubnub-error'; import { isSensitiveLogKey } from '../../../core/utils'; import { ILegacyCryptor } from './ILegacyCryptor'; import { EncryptedDataType } from './ICryptor'; +import WebCryptoPrimitives from '../../primitives/web'; import FileCryptor from '../web'; /** @@ -45,7 +46,7 @@ export default class LegacyCryptor implements ILegacyCryptor { constructor(config: CryptorConfiguration) { this.config = config; - this.cryptor = new Crypto({ ...config }); + this.cryptor = new Crypto({ ...config }, new WebCryptoPrimitives()); this.fileCryptor = new FileCryptor(); } @@ -71,11 +72,11 @@ export default class LegacyCryptor implements ILegacyCryptor { // -------------------------------------------------------- // region Encryption - encrypt(data: ArrayBuffer | string) { + async encrypt(data: ArrayBuffer | string): Promise { const stringData = typeof data === 'string' ? data : LegacyCryptor.decoder.decode(data); return { - data: this.cryptor.encrypt(stringData), + data: await this.cryptor.encrypt(stringData), metadata: null, }; } @@ -92,7 +93,7 @@ export default class LegacyCryptor implements ILegacyCryptor { // -------------------------------------------------------- // region Decryption - decrypt(encryptedData: EncryptedDataType) { + async decrypt(encryptedData: EncryptedDataType) { const data = typeof encryptedData.data === 'string' ? encryptedData.data : encode(encryptedData.data); return this.cryptor.decrypt(data); diff --git a/src/crypto/modules/WebCryptoModule/webCryptoModule.ts b/src/crypto/modules/WebCryptoModule/webCryptoModule.ts index dc0d8964e..330407a51 100644 --- a/src/crypto/modules/WebCryptoModule/webCryptoModule.ts +++ b/src/crypto/modules/WebCryptoModule/webCryptoModule.ts @@ -10,13 +10,15 @@ import { PubNubError } from '../../../errors/pubnub-error'; import { EncryptedDataType, ICryptor } from './ICryptor'; import { ILegacyCryptor } from './ILegacyCryptor'; import AesCbcCryptor from './aesCbcCryptor'; +import AesGcmCryptor from './aesGcmCryptor'; import LegacyCryptor from './legacyCryptor'; import { LoggerManager } from '../../../core/components/logger-manager'; +import { Payload } from '../../../core/types/api'; /** * Re-export bundled cryptors. */ -export { LegacyCryptor, AesCbcCryptor }; +export { LegacyCryptor, AesCbcCryptor, AesGcmCryptor }; /** * Crypto module cryptors interface. @@ -85,7 +87,10 @@ export class WebCryptoModule extends AbstractCryptoModule { ...config, useRandomIVs: config.useRandomIVs ?? true, }), - cryptors: [new AesCbcCryptor({ cipherKey: config.cipherKey })], + cryptors: [ + new AesCbcCryptor({ cipherKey: config.cipherKey }), + new AesGcmCryptor({ cipherKey: config.cipherKey }), + ], }); } @@ -99,10 +104,37 @@ export class WebCryptoModule extends AbstractCryptoModule { ...config, useRandomIVs: config.useRandomIVs ?? true, }), + new AesGcmCryptor({ cipherKey: config.cipherKey }), ], }); } + /** + * Construct decrypt-only AES-GCM cryptor. + * + * Modules created with {@link aesCbcCryptoModule}, {@link legacyCryptoModule} or `cipherKey` already register it. + * Use it to let a custom crypto module decrypt AES-GCM (`ACGM`) payloads produced by other PubNub SDKs: + * + * @example + * ```typescript + * const cryptoModule = new PubNub.CryptoModule({ + * default: myCryptor, + * cryptors: [PubNub.CryptoModule.aesGcmCryptor({ cipherKey: 'my-key' })], + * }); + * ``` + * + * @param config - Cryptor configuration options. + * + * @returns AES-GCM cryptor which can be used only as an additional (decryption) cryptor. + * + * @throws Error if `config.cipherKey` not set. + */ + static aesGcmCryptor(config: CryptorConfiguration): ICryptor { + if (!config.cipherKey) throw new PubNubError('Crypto module error: cipher key not set.'); + + return new AesGcmCryptor({ cipherKey: config.cipherKey }); + } + /** * Construct crypto module with `cryptor` as default for data encryption and decryption. * @@ -120,12 +152,12 @@ export class WebCryptoModule extends AbstractCryptoModule { // -------------------------------------------------------- // region Encryption - encrypt(data: ArrayBuffer | string) { + async encrypt(data: ArrayBuffer | string): Promise { // Encrypt data. const encrypted = data instanceof ArrayBuffer && this.defaultCryptor.identifier === WebCryptoModule.LEGACY_IDENTIFIER - ? (this.defaultCryptor as ILegacyCryptor).encrypt(WebCryptoModule.decoder.decode(data)) - : (this.defaultCryptor as ICryptor).encrypt(data); + ? await (this.defaultCryptor as ILegacyCryptor).encrypt(WebCryptoModule.decoder.decode(data)) + : await (this.defaultCryptor as ICryptor).encrypt(data); if (!encrypted.metadata) return encrypted.data; else if (typeof encrypted.data === 'string') @@ -161,7 +193,7 @@ export class WebCryptoModule extends AbstractCryptoModule { // -------------------------------------------------------- // region Decryption - decrypt(data: ArrayBuffer | string) { + async decrypt(data: ArrayBuffer | string): Promise { const encryptedData = typeof data === 'string' ? decode(data) : data; const header = CryptorHeader.tryParse(encryptedData); const cryptor = this.getCryptor(header); @@ -194,7 +226,7 @@ export class WebCryptoModule extends AbstractCryptoModule { return File.create({ name: file.name, - data: await (this.defaultCryptor as ICryptor).decryptFileData({ + data: await (cryptor as ICryptor).decryptFileData({ data: data.slice(header.length), metadata: metadata, }), diff --git a/src/crypto/primitives/node.ts b/src/crypto/primitives/node.ts new file mode 100644 index 000000000..7892a82ee --- /dev/null +++ b/src/crypto/primitives/node.ts @@ -0,0 +1,88 @@ +/** + * Node.js cryptographic primitives backed by `node:crypto`. + * + * @internal + */ + +import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } from 'crypto'; + +import { CryptoPrimitives } from '../../core/interfaces/crypto-primitives'; + +/** + * Compute HMAC-SHA256 (used for request signature). + * + * @param data - Data for which HMAC should be computed. + * @param secretKey - HMAC secret key. + * + * @returns Base64 encoded HMAC-SHA256 digest. + * + * @internal + */ +export const hmacSha256 = (data: string, secretKey: string): string => + createHmac('sha256', secretKey).update(data, 'utf8').digest('base64'); + +/** + * Node.js cryptographic primitives. + * + * @internal + */ +export default class NodeCryptoPrimitives implements CryptoPrimitives { + async sha256(data: Uint8Array): Promise { + return new Uint8Array(createHash('sha256').update(data).digest()); + } + + async aesCbcEncrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise { + const cipher = createCipheriv(this.algorithm(key), key, iv); + return new Uint8Array(Buffer.concat([cipher.update(data), cipher.final()])); + } + + async aesCbcDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise { + const decipher = createDecipheriv(this.algorithm(key), key, iv); + return new Uint8Array(Buffer.concat([decipher.update(data), decipher.final()])); + } + + /** + * Decrypt and authenticate data using AES-256-GCM (128-bit tag, no additional authenticated data). + * + * Used only by the decrypt-only `ACGM` cryptor (Web and Node.js). Intentionally not part of + * {@link CryptoPrimitives}: React Native supports only the legacy cryptor. + * + * @param key - 32 bytes long raw key. + * @param nonce - 12 bytes long nonce. + * @param data - Data which should be decrypted, with 16 bytes long authentication tag appended to the ciphertext. + * + * @returns Decrypted data (returned only after the authentication tag has been verified). + * + * @throws Error if data can't be authenticated (for example: wrong key or modified nonce, ciphertext or tag). + */ + async aesGcmDecrypt(key: Uint8Array, nonce: Uint8Array, data: Uint8Array): Promise { + if (data.byteLength < 16) throw new Error('Decryption error: authentication tag is missing'); + + const decipher = createDecipheriv('aes-256-gcm', key, nonce, { authTagLength: 16 }); + decipher.setAuthTag(data.subarray(data.byteLength - 16)); + + // `update` returns not yet authenticated data: release it only after `final` verified the tag. + const decrypted = decipher.update(data.subarray(0, data.byteLength - 16)); + try { + return new Uint8Array(Buffer.concat([decrypted, decipher.final()])); + } catch (error) { + decrypted.fill(0); + throw error; + } + } + + randomBytes(length: number): Uint8Array { + return new Uint8Array(randomBytes(length)); + } + + /** + * AES-CBC algorithm name for provided key size. + * + * @param key - Raw key bytes. + * + * @returns `node:crypto` cipher name. + */ + private algorithm(key: Uint8Array) { + return `aes-${key.byteLength * 8}-cbc`; + } +} diff --git a/src/crypto/primitives/react-native.ts b/src/crypto/primitives/react-native.ts new file mode 100644 index 000000000..659d5e781 --- /dev/null +++ b/src/crypto/primitives/react-native.ts @@ -0,0 +1,90 @@ +/** + * React Native cryptographic primitives backed by `@noble/ciphers` and `@noble/hashes`. + * + * Hermes doesn't provide `crypto.subtle`, so pure JS audited implementations are used. This module is imported + * only by the React Native entry point, so `@noble/*` never becomes part of Web or Node.js builds. Modules are + * loaded lazily on first encryption / decryption, so apps which don't use encryption don't evaluate them. + * + * @internal + */ + +import { CryptoPrimitives } from '../../core/interfaces/crypto-primitives'; +import { PubNubError } from '../../errors/pubnub-error'; + +/** + * Subset of `@noble/ciphers/aes.js` API used by the SDK. + */ +type NobleAes = { + cbc: ( + key: Uint8Array, + iv: Uint8Array, + ) => { + encrypt(data: Uint8Array): Uint8Array; + decrypt(data: Uint8Array): Uint8Array; + }; +}; + +/** + * Subset of `@noble/hashes/sha2.js` API used by the SDK. + */ +type NobleSha2 = { sha256: (data: Uint8Array) => Uint8Array }; + +/** + * Loaded `@noble/*` modules. + */ +let noble: { aes: NobleAes; sha2: NobleSha2 } | undefined; + +/** + * Lazily load `@noble/*` packages. + * + * @returns Loaded modules. + * + * @throws PubNubError if packages can't be loaded (for example, excluded from the app bundle). + */ +const loadNoble = () => { + if (noble) return noble; + + try { + /* eslint-disable @typescript-eslint/no-require-imports */ + noble = { aes: require('@noble/ciphers/aes.js') as NobleAes, sha2: require('@noble/hashes/sha2.js') as NobleSha2 }; + /* eslint-enable @typescript-eslint/no-require-imports */ + } catch (_error) { + throw new PubNubError( + "React Native encryption requires '@noble/ciphers' and '@noble/hashes'. " + + 'Install them: npm install @noble/ciphers @noble/hashes', + ); + } + + return noble; +}; + +/** + * React Native cryptographic primitives. + * + * @internal + */ +export default class NobleCryptoPrimitives implements CryptoPrimitives { + async sha256(data: Uint8Array): Promise { + return loadNoble().sha2.sha256(data); + } + + async aesCbcEncrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise { + return loadNoble().aes.cbc(key, iv).encrypt(data); + } + + async aesCbcDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise { + return loadNoble().aes.cbc(key, iv).decrypt(data); + } + + randomBytes(length: number): Uint8Array { + const cryptoApi = (globalThis as { crypto?: { getRandomValues?: (array: T) => T } }).crypto; + + if (!cryptoApi || typeof cryptoApi.getRandomValues !== 'function') + throw new PubNubError( + "Crypto error: secure random number generator unavailable. Install 'react-native-get-random-values' " + + 'and import it at the top of your app entry file.', + ); + + return cryptoApi.getRandomValues(new Uint8Array(length)); + } +} diff --git a/src/crypto/primitives/web.ts b/src/crypto/primitives/web.ts new file mode 100644 index 000000000..8d425a442 --- /dev/null +++ b/src/crypto/primitives/web.ts @@ -0,0 +1,104 @@ +/** + * Browser cryptographic primitives backed by `crypto.subtle`. + * + * @internal + */ +/* global crypto */ + +import { CryptoPrimitives } from '../../core/interfaces/crypto-primitives'; +import { bytesToHex } from '../../core/components/cryptography/bytes'; +import { PubNubError } from '../../errors/pubnub-error'; + +/** + * Browser cryptographic primitives. + * + * @internal + */ +export default class WebCryptoPrimitives implements CryptoPrimitives { + /** + * Maximum number of imported keys kept in cache. + */ + private static readonly MAX_CACHED_KEYS = 8; + + /** + * Imported keys cache (keyed by the algorithm and hex representation of the raw key). + */ + private readonly keys = new Map>(); + + async sha256(data: Uint8Array): Promise { + return new Uint8Array(await this.subtle.digest('SHA-256', data)); + } + + async aesCbcEncrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise { + const cryptoKey = await this.importKey(key, 'AES-CBC'); + return new Uint8Array(await this.subtle.encrypt({ name: 'AES-CBC', iv }, cryptoKey, data)); + } + + async aesCbcDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise { + const cryptoKey = await this.importKey(key, 'AES-CBC'); + return new Uint8Array(await this.subtle.decrypt({ name: 'AES-CBC', iv }, cryptoKey, data)); + } + + /** + * Decrypt and authenticate data using AES-256-GCM (128-bit tag, no additional authenticated data). + * + * Used only by the decrypt-only `ACGM` cryptor (Web and Node.js). Intentionally not part of + * {@link CryptoPrimitives}: React Native supports only the legacy cryptor. + * + * @param key - 32 bytes long raw key. + * @param nonce - 12 bytes long nonce. + * @param data - Data which should be decrypted, with 16 bytes long authentication tag appended to the ciphertext. + * + * @returns Decrypted data (returned only after the authentication tag has been verified). + * + * @throws Error if data can't be authenticated (for example: wrong key or modified nonce, ciphertext or tag). + */ + async aesGcmDecrypt(key: Uint8Array, nonce: Uint8Array, data: Uint8Array): Promise { + const cryptoKey = await this.importKey(key, 'AES-GCM'); + return new Uint8Array(await this.subtle.decrypt({ name: 'AES-GCM', iv: nonce, tagLength: 128 }, cryptoKey, data)); + } + + randomBytes(length: number): Uint8Array { + return crypto.getRandomValues(new Uint8Array(length)); + } + + /** + * Retrieve `SubtleCrypto` interface. + * + * @returns `SubtleCrypto` interface. + * + * @throws PubNubError if `crypto.subtle` isn't available (non-secure context). + */ + private get subtle(): SubtleCrypto { + if (typeof crypto === 'undefined' || !crypto.subtle) + throw new PubNubError( + 'Crypto error: crypto.subtle is unavailable. Encryption requires a secure context (HTTPS or localhost).', + ); + + return crypto.subtle; + } + + /** + * Import raw AES key (cached, least recently imported keys evicted first). + * + * @param key - Raw key bytes. + * @param algorithm - Algorithm for which key will be used. + * + * @returns Imported `CryptoKey`. + */ + private importKey(key: Uint8Array, algorithm: 'AES-CBC' | 'AES-GCM'): Promise { + const id = `${algorithm}:${bytesToHex(key)}`; + let cryptoKey = this.keys.get(id); + + if (!cryptoKey) { + if (this.keys.size >= WebCryptoPrimitives.MAX_CACHED_KEYS) this.keys.delete(this.keys.keys().next().value!); + + const usages: KeyUsage[] = algorithm === 'AES-CBC' ? ['encrypt', 'decrypt'] : ['decrypt']; + cryptoKey = this.subtle.importKey('raw', key, algorithm, false, usages); + cryptoKey.catch(() => this.keys.delete(id)); + this.keys.set(id, cryptoKey); + } + + return cryptoKey; + } +} diff --git a/src/node/index.ts b/src/node/index.ts index 44c1bfe6f..7040a6e64 100755 --- a/src/node/index.ts +++ b/src/node/index.ts @@ -2,7 +2,12 @@ import CborReader from 'cbor-sync'; import { Readable } from 'stream'; import { Buffer } from 'buffer'; -import { NodeCryptoModule, LegacyCryptor, AesCbcCryptor } from '../crypto/modules/NodeCryptoModule/nodeCryptoModule'; +import { + NodeCryptoModule, + LegacyCryptor, + AesCbcCryptor, + AesGcmCryptor, +} from '../crypto/modules/NodeCryptoModule/nodeCryptoModule'; import type { NodeCryptoModule as CryptoModuleType } from '../crypto/modules/NodeCryptoModule/nodeCryptoModule'; import { ExtendedConfiguration, PlatformConfiguration } from '../core/interfaces/configuration'; @@ -19,6 +24,7 @@ import { PubNubFileConstructor } from '../core/types/file'; import { decode } from '../core/components/base64_codec'; import NodeCryptography from '../crypto/modules/node'; import Crypto from '../core/components/cryptography'; +import NodeCryptoPrimitives, { hmacSha256 } from '../crypto/primitives/node'; import { PubNubError } from '../errors/pubnub-error'; import { PubNubCore } from '../core/pubnub-common'; import Cbor from '../cbor/common'; @@ -74,7 +80,10 @@ class PubNub extends PubNubCore | undefined; @@ -120,7 +132,7 @@ class PubNub extends PubNubCore { if (!cryptoConfiguration.cipherKey) return undefined; if (process.env.CRYPTO_MODULE !== 'disabled') { - const legacy = new Crypto({ - secretKey: platformConfiguration.secretKey, - cipherKey: cryptoConfiguration.cipherKey, - useRandomIVs: platformConfiguration.useRandomIVs, - customEncrypt: platformConfiguration.customEncrypt, - customDecrypt: platformConfiguration.customDecrypt, - logger: cryptoConfiguration.logger, - }); + const legacy = new Crypto( + { + secretKey: platformConfiguration.secretKey, + cipherKey: cryptoConfiguration.cipherKey, + useRandomIVs: platformConfiguration.useRandomIVs, + customEncrypt: platformConfiguration.customEncrypt, + customDecrypt: platformConfiguration.customDecrypt, + logger: cryptoConfiguration.logger, + }, + new NobleCryptoPrimitives(), + ); return new LegacyCryptoModule(legacy); } return undefined; @@ -135,14 +139,17 @@ export default class PubNub extends PubNubCore { let crypto: Crypto | undefined; if (process.env.CRYPTO_MODULE !== 'disabled') { if (clientConfiguration.getCipherKey() || clientConfiguration.secretKey) { - crypto = new Crypto({ - secretKey: clientConfiguration.secretKey, - cipherKey: clientConfiguration.getCipherKey(), - useRandomIVs: clientConfiguration.getUseRandomIVs(), - customEncrypt: clientConfiguration.getCustomEncrypt(), - customDecrypt: clientConfiguration.getCustomDecrypt(), - logger: clientConfiguration.logger(), - }); + crypto = new Crypto( + { + secretKey: clientConfiguration.secretKey, + cipherKey: clientConfiguration.getCipherKey(), + useRandomIVs: clientConfiguration.getUseRandomIVs(), + customEncrypt: clientConfiguration.getCustomEncrypt(), + customDecrypt: clientConfiguration.getCustomDecrypt(), + logger: clientConfiguration.logger(), + }, + new NobleCryptoPrimitives(), + ); } } diff --git a/src/transport/middleware.ts b/src/transport/middleware.ts index 366072446..bc86cacee 100644 --- a/src/transport/middleware.ts +++ b/src/transport/middleware.ts @@ -31,9 +31,9 @@ type PubNubMiddlewareConfiguration = { tokenManager?: TokenManager; /** - * HMAC-SHA256 hash generator from provided `data`. + * HMAC-SHA256 hash generator from provided `data` (base64 encoded digest). */ - shaHMAC?: (data: string) => string; + shaHMAC?: (data: string, secretKey: string) => string; /** * Platform-specific transport for requests processing. diff --git a/src/web/index.ts b/src/web/index.ts index be73b2e9e..e6e0de926 100644 --- a/src/web/index.ts +++ b/src/web/index.ts @@ -3,7 +3,12 @@ import CborReader from 'cbor-js'; -import { WebCryptoModule, AesCbcCryptor, LegacyCryptor } from '../crypto/modules/WebCryptoModule/webCryptoModule'; +import { + WebCryptoModule, + AesCbcCryptor, + AesGcmCryptor, + LegacyCryptor, +} from '../crypto/modules/WebCryptoModule/webCryptoModule'; import type { WebCryptoModule as CryptoModuleType } from '../crypto/modules/WebCryptoModule/webCryptoModule'; import { SubscriptionWorkerMiddleware } from '../transport/subscription-worker/subscription-worker-middleware'; @@ -22,6 +27,7 @@ import { Transport } from '../core/interfaces/transport'; import { LogLevel } from '../core/interfaces/logger'; import Crypto from '../core/components/cryptography'; import WebCryptography from '../crypto/modules/web'; +import WebCryptoPrimitives from '../crypto/primitives/web'; import { PubNubCore } from '../core/pubnub-common'; import Cbor from '../cbor/common'; import { Payload } from '../core/types/api'; @@ -70,7 +76,10 @@ export default class PubNub extends PubNubCore/\\ β€” spans several AES blocks.", + "plaintextHex": "4c696e65206f6e650a094c696e652074776f2077697468202271756f7465732220262073796d626f6c73203c3e2f5c20e28094207370616e73207365766572616c2041455320626c6f636b732e", + "nonce": "000000000000000000000000", + "tag": "8390572d9b05255f3097614e888fcca4", + "ciphertext": "326052d9ff1b757ed8810f5d6f87a7c021920eb09b82e47049efecf5094f6f5837691e9fb66539dfc47d434cc01b783527c4d62ae2d8163043f86dc7ac405b90971ceaa07134add2be50dfd60f", + "ciphertextWithTag": "326052d9ff1b757ed8810f5d6f87a7c021920eb09b82e47049efecf5094f6f5837691e9fb66539dfc47d434cc01b783527c4d62ae2d8163043f86dc7ac405b90971ceaa07134add2be50dfd60f8390572d9b05255f3097614e888fcca4", + "metadata": "000000000000000000000000", + "payload": "504e4544014143474d0c000000000000000000000000326052d9ff1b757ed8810f5d6f87a7c021920eb09b82e47049efecf5094f6f5837691e9fb66539dfc47d434cc01b783527c4d62ae2d8163043f86dc7ac405b90971ceaa07134add2be50dfd60f8390572d9b05255f3097614e888fcca4" + } + ] +} diff --git a/test/fixtures/crypto/aes-gcm-vectors.ts b/test/fixtures/crypto/aes-gcm-vectors.ts new file mode 100644 index 000000000..f4ca43148 --- /dev/null +++ b/test/fixtures/crypto/aes-gcm-vectors.ts @@ -0,0 +1,30 @@ +/** + * Typed access to AES-GCM (`ACGM`) decryption vectors. + * + * Vectors generated with `node:crypto` (independent of the SDK code under test) in the `ACGM` layout: + * `PNED | 0x01 | ACGM | 0x0c | nonce (12) | ciphertext | auth tag (16)` (metadata is the nonce only). + */ + +import { readFileSync } from 'fs'; +import { join } from 'path'; + +export type AesGcmVector = { + name: string; + expect: 'decrypt'; + cipherKey: string; + keySha256: string; + plaintext?: string; + plaintextHex: string; + nonce: string; + tag: string; + ciphertext: string; + ciphertextWithTag: string; + metadata: string; + payload: string; +}; + +const vectors: { source: string; vectors: AesGcmVector[] } = JSON.parse( + readFileSync(join(__dirname, 'aes-gcm-vectors.json'), 'utf8'), +); + +export default vectors; diff --git a/test/fixtures/crypto/generate-legacy-vectors.ts b/test/fixtures/crypto/generate-legacy-vectors.ts new file mode 100644 index 000000000..5aabe8ca2 --- /dev/null +++ b/test/fixtures/crypto/generate-legacy-vectors.ts @@ -0,0 +1,89 @@ +/** + * Golden vectors generator. + * + * Captured once from the CryptoJS-based implementation (SDK v13.x) before it was removed. Output is + * committed as `legacy-vectors.json` and used by parity tests to guarantee that the native crypto + * implementations produce / accept exactly the same ciphertexts. + * + * **Note:** Runs only against the v13 (CryptoJS-based) sources it was written for, e.g. from a worktree of commit + * `788cdb92`: `npx tsx test/fixtures/crypto/generate-legacy-vectors.ts`. Kept to document how the fixture was + * produced; type checking is disabled because the APIs it calls no longer exist. + */ +// @ts-nocheck + +import { writeFileSync } from 'fs'; +import { join } from 'path'; + +import LegacyCrypto from '../../../src/core/components/cryptography/index'; +import { WebCryptoModule } from '../../../src/crypto/modules/WebCryptoModule/webCryptoModule'; +import { NodeCryptoModule } from '../../../src/crypto/modules/NodeCryptoModule/nodeCryptoModule'; +import { encode } from '../../../src/core/components/base64_codec'; + +const keys = ['customKey', 'enigma', 'ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί', 'k'.repeat(37) + '0123456789'.repeat(6) + '!@#']; +const plaintexts: unknown[] = [ + { hi: 'there' }, + { nested: { list: [1, 2, 3, { deep: true }], text: 'value', n: null } }, + 'Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú', + '', + 42, + 3.14159, + 'x'.repeat(16384), + 'with\u0000null\u0000bytes', + ['array', 1, false], +]; + +/* eslint-disable @typescript-eslint/no-explicit-any */ +const toB64 = (data: ArrayBuffer | string) => (typeof data === 'string' ? data : encode(data)); + +const main = async () => { + const legacy: any[] = []; + const cryptoModule: any[] = []; + + for (const cipherKey of keys) { + const staticIV = new LegacyCrypto({ cipherKey, useRandomIVs: false }); + const randomIV = new LegacyCrypto({ cipherKey, useRandomIVs: true }); + + for (const value of plaintexts) { + const plaintext = JSON.stringify(value); + // Keep fixture size reasonable: large payload only for the first key. + if (plaintext.length > 1024 && cipherKey !== keys[0]) continue; + + legacy.push({ + cipherKey, + plaintext, + staticIV: staticIV.encrypt(plaintext), + randomIV: [randomIV.encrypt(plaintext), randomIV.encrypt(plaintext)], + }); + + const webAes = WebCryptoModule.aesCbcCryptoModule({ cipherKey }); + const webLegacy = WebCryptoModule.legacyCryptoModule({ cipherKey }); + const nodeAes = NodeCryptoModule.aesCbcCryptoModule({ cipherKey }); + const nodeLegacy = NodeCryptoModule.legacyCryptoModule({ cipherKey }); + cryptoModule.push({ + cipherKey, + plaintext, + webAesCbc: toB64(webAes.encrypt(plaintext)), + webLegacy: toB64(webLegacy.encrypt(plaintext)), + nodeAesCbc: toB64(nodeAes.encrypt(plaintext)), + nodeLegacy: toB64(nodeLegacy.encrypt(plaintext)), + }); + } + } + + const signatureInputs = [ + 'GET\npub-c-key\n/publish/pub-c-key/sub-c-key/0/channel/0/%22hello%22\nauth=token&pnsdk=PubNub-JS-Nodejs%2F13.1.3×tamp=1700000000&uuid=user\n', + 'POST\npub-c-key\n/v3/pam/sub-c-key/grant\npnsdk=PubNub-JS-Nodejs%2F13.1.3×tamp=1700000000&uuid=user\n{"ttl":15,"permissions":{"resources":{"channels":{"ch-ΓΌnΓ―code":1}}}}', + ]; + const secretKeys = ['sec-c-test-only-not-a-real-key', 'sΓ«cret-πŸ”']; + const hmac: any[] = []; + for (const secretKey of secretKeys) { + const crypto = new LegacyCrypto({ secretKey }); + for (const input of signatureInputs) hmac.push({ secretKey, input, signature: crypto.HMACSHA256(input) }); + } + + const output = { generatedWith: 'CryptoJS v3.1.2 (pubnub v13.1.3)', legacy, cryptoModule, hmac }; + writeFileSync(join(__dirname, 'legacy-vectors.json'), JSON.stringify(output, null, 2) + '\n'); + console.log(`legacy: ${legacy.length}, cryptoModule: ${cryptoModule.length}, hmac: ${hmac.length}`); +}; + +main(); diff --git a/test/fixtures/crypto/legacy-vectors.json b/test/fixtures/crypto/legacy-vectors.json new file mode 100644 index 000000000..0db18a787 --- /dev/null +++ b/test/fixtures/crypto/legacy-vectors.json @@ -0,0 +1,590 @@ +{ + "generatedWith": "CryptoJS v3.1.2 (pubnub v13.1.3)", + "legacy": [ + { + "cipherKey": "customKey", + "plaintext": "{\"hi\":\"there\"}", + "staticIV": "TejX6F2JNqH/gIiGHWN4Cw==", + "randomIV": [ + "jUu9b1vVOonQrygM+wM09VrphY/fgEutuFIY9z7Yuu4=", + "VciTWWgGlgWjMdpdRs40aYn0REZNEKewHbJyYo+jGs8=" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "staticIV": "Mu1GPH1c5Ke3erqlYXuMRpr+1/pEYvXXlvQV9LcPuUtoo3lxtshfUMKG5bq/3ogmsh6eARR4+EFJJcY8PrHhjdSC82x9iVPHV7P9w18Lt/c=", + "randomIV": [ + "6oxKGOK8VsM4qL2A9BatxMm48lDsG3fyX5VsbQF+tuphFiby+ZiMCr5V4lHz/4w8Zfc+8Pe+k6zsduYhMZ5OWOtqQYhiN2VSx0WE/FugI3YwslfqPvOZfjs6SECy6L8y", + "3Z3/bOFK0A9VE5ehBui3NRY5hrxxMXd445uSaOtHLpagvRODAs5P5sTQ8y8wpQWpRBvlyi1mD6j8eevhzWD+SO51PvvjpkG4tCrgaIBUv66mWi98s61vtWKtt3Vr3LTJ" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "staticIV": "TmGTph0LU8ixNsTZCrVpQmJvTbafuMeTyrpMDakLkP9V1H34S/fs08gcVV8gj9WE", + "randomIV": [ + "TjyJXSDRXWs2FHzHhR9NYGlRtf5kRqObED9er4NxQhZmujsRkhhTRagvEr+6PUtz5+lmwOauZtKnd03/9cWXYw==", + "Cdc8lppGY6crAKAz+p+yVKLiNRjILl3qFPnK71myRLv+UDM17K8d8t2Yf0izJphilp4mFq+Yt9duEouHyKfjNw==" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "\"\"", + "staticIV": "8/BGZWF57NcrBcIZo0WikQ==", + "randomIV": [ + "dDt7VqKnyPm+SF0fd3onRyUclVzSWOQkbSRAlTyLs4Y=", + "b9nCcuWMxCXJM0bJlkt/majuf9Z4bsgsCd9q8YioC/w=" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "42", + "staticIV": "Kg35CKfv/JoeW8TfTc84JQ==", + "randomIV": [ + "DAdW9LXEeJwchqzjtMC9o4eiErAI1hqi/ewZi4o7fhI=", + "SyBdamuBCaq1xdUn7dKZSbSud+WVE2gwi1GqmaXVRZU=" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "3.14159", + "staticIV": "SsfXFnVWwH3MRd8G9v0wOA==", + "randomIV": [ + "ubgoNY6ex5FOl08LudhyC1s0bAg3OE+FLd00oAmbG9A=", + "cRgmC/nS9XWBDgfvz1ENb9TOjylX4nmvz/wtEWOvf/k=" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"", + "staticIV": "WuXS+yjVqmZtERzys32751fdbQac4WqS2j8+5kc/zZfpL2aq5NsfnDi1kxEP+Iq4CgTbDxJeMtB149hjExKmIo/hUmQSUKEG6ubofIPGoIhyZydnG86QiVyZYTxqcgayT2zL5kFSYLP1lc6P9YHnL2Rd+cqkMym25TE6ys7jqZ/mKM2tuWRfekACSoehAt+mWMYJ0nZNBssVmQmR0nnst9/cCvVNEG6F1hgAAugc89k1lEjQ9zy72wntMnqoQHbAF8SxW3LJdV78H87VJTXFIMWSgWi9bMemLqfsJsTItKsQXXt25Bz1QF6LBcMIReleXchkQSBaUiFYTDBkLhKO1qJwLH9dUt8UF2FvKOQwzabj39mg14k1nxTtAtgwnf82Bf41mcpOZyAJIHwRyWaBZuTlM2P4eEYJZsgtQWUUuqTSodd9OP1ZQ0EM642bWuwGpEZu6BXhs5dE0RZ7sEnuf+YprDJkI7Rb9tn/fsna+swO9BPKRH0eTJYkPLvdO1wdXn8xfwq1Vgxjf69PF8V7U4nRSy9LWAi9BOOdHGQMTKWBKMq/wpm+eyr21vBe25+PdXQU9L+2RykwJlGvQMbRpwofb9rKEfpdPWOnaM+pXIUHGNLEWmalotdDEpac15H7gW2A+3G+3zgEgQ5/qPbdl+ws05zvnND5z0TjMIAa+6klBLOO/+TpLgLDwSsCo/Ax+CvsvcuD6ciZgjIK4oIn/t0VLA0MzUJ74zFrYw3VQ2qt16RAO3aB0Ol3qJHKlNhXJeZslSOGgZB1mG/xm9aEJkSKAglmuK+ZTStIyKTohApbjeNETgDtdaIlw6ITTwKlm7eUZeNhpmsTgTOH05Cy2o3hL7fYgbHBDyE4k5FcQeKjxy7X9E+gDFbYNNGe0iICwSHHauzNKOPJradyqyNSR6/z6+BbVDe2zcKPxGGFTMxMgT/5+Ddnu2AzXub8ISP6bw2jWMSgwU65Te6qFxqwtu1F5jcvcuSihPcLeIiirtJYgHtaxMcKyZD1TbwrGCHvACbv8BuEC6hrI1oB0wiThKEz3clZMxCZ85B2TQwd8armPCELWTMSK8rls9b7W7rxIRzS+xJ48lI2FhqX546sLwPI6yzH3m5XonV5jCZ+Qb4TJQtchEqGrebryfphxBETpleFQzMMiW7EVQ+TQVpQclBEqbVnw0f6Er7W2olO4CC9vN/Bcfk7YSNaRdSqxCnsLM9oGcZSurdt0za4X0zEsLmREv1vV0wZj0VtmU4YVOrxVLdUyyk2lb73O2n5YyKUoPdaWZCDE+GFZt2Q+MTosu7pgNwmW1yDyPsFtVxr3QFWK3BqNA0rYWNYgoW20eTsrMkdXGmLOUtWy/OFCDdcknBxvCAXaeyyioBM95SSK+n3LKS1Fr/2vNaqQcJDowLlpzrjqxGcJOl7ij2CyGCJe9fA970VTqAFitgVrGumLeZIlPHSrYZ7217aLpCbXAeapS5GHRcDMLPGCb5r+zIy8Ahvpf23QlnmP9QuyvWv0Vzl9pE4cRQqgu1Has2vJs/bU2fL8EM96fine6EZuCi25j3QteGZs4ZiPdlKSaZibDC2JLqCtSWSEkvVBfyW9yRyyWmT1//ioxYQgbGofF1udi5uwVr7xFxZtFgekJUWQ08IDM3bCJdPhNSVriNa3AUZCEFZCLsU+CJVJaBDU43qcUzH2WEQyJU/u6qomSz4/0hdPogIuTjbfHa8SC0nWUAMJpbIBS1DcShfENJhdjjCLwx3BhOaT1GaftFeB8PnpFqo8d32GU+QsYNxbei3hV/Vkghnl5h69ZFLszNgZgVp3UFcMh2pf5t4EiFTGAEGNupXTBhVklf9eiw5pGmNm6YCwC38XDZyZTeBitTqDlg5K2kexQgn5gH6C8ACC99hJuFEzId4fmamacLp5MzjvkCl8RQFD9zqooWf5asyQWOiGYtQWOf7OuVAs/Nr03kmgq5I6i50M/g9TdkLd5SL8a0vqeHjHHzcy5M+PhBTS/n2yLN1Fnua0RHjNHVajudIK7GbVEB0TBj66qmGv6o6TOnqPeiHMJUF9bnYgmdMEZigYZGel6flg5kT3GT90EOKLpB8SA7j0oK/1QMEOnt+B5deUWsiVVVRX8t1RXeItlobrJfZgzC6XMgWxKHf73fdwNK7B/lNCQ4gBzKj9J9sIb5+I9UCUm4hAm4I5tpx+Bj9QeltzodJKoErYOW0k3EVfOS60374tTeJvXsKZ0QeM9MBs2CgfaKfhpoUWOOofi6WI6newS5zx9EqYl7s9re1GlCXRDC4xu9lCb1jq3iUU1PHQjNE9y8P5E+mdLZ1/Bh4ukKGruiCVAawt6zpEhQ7JCxgC35S87fm5iPNqJbb4ozH7FXU+E6SkO4xcpgZJxKm094qhAChd1oSQ2tOui99PhKpHlZVGxN9nMTjsHnIIo0303f+O+kKYKVwk6tl5amfDuliOf2pB+ML/e7KCHKfw/xJMVVtLvx6aJcxO5fe7sseE4GVwBSGnnXXpbny1+6V1DXIfZRiu0Icnbl1ZdbaiGQzdR/VLHkyg7dyXv9mDoCiJbM3Fr6R7+zKt6JMB1eQGbqU8VU2j+0iBUUSj2puNfqPCym6S5G35HIuv4RHyle24vJIL2yLkvVs91sCKzHBDQmAj0qGnGbpuz40Rf7Uzpyu7jURcPDIZLKIkDaoeJujk0oDw+fcRb6CzyPrgCL4cbjU6lmWFUNVkPBP6/hazUbBxJgImOCTrokEN6fZPf1fSc6ilqp+TZ/6UQpBhawNC2ImPwUAQTO+uG+teoYt8eeFu9jIPyvHW69o2xT5gYzFl/HthRAOaNdnwPfVVQL9tNhLTo21FFZGOtO5+jVN75KMXfIVm9y23/IivNoQFb4peiSrvdHq9foM3E8lS/tlAlbHToiTVrMowpFJDSF541cp3Fz2gkvA+OIWs9w3ZYm+knvy255qs8X9x34Kh723hNTFyNSy05A40xlJE0g0P8jiU58GGfsTvYfWwwJ9igON+jrnB9cAHJCVrjSjTCZ7PEGqQ2OIuQI2g46hy4QYWrJgyJwAtDXRVqD/ZOxljUjSeKaYMun1NFeU337cbBakP50OW12R+wsmm+0+Tut8PnBxTzj+2J5KhtMPbAVrAjIZibQwcRf/xCT3Gtmy82JqrzzLKY2i7dYBSPbdEyPgfG/GkvmJRJN+YrdiBNNUN4eR3WIVNS9fABsPlEJh+x7vYoU+XDBlK8mO2rwr2Tgmh/cwPRQwnWJVupLxBw8g0olLho3sm/Bv/g8dtvRYunfqc2gassb/PeyPz6WjF1QVl9O3gt6TcRuGDI/rWwXieOgsWOdYu4ihUJ2HzPb/1D3ct7JBQ4yN4OcNbWlrxMZtHVznGZmwzwSdbfTSlyGZHYRxTGC7TL8ddphm3DW5Sl3wUxw9ifySdA9PRA6WC5wxcXXTetKZV07EDEDBRFpsJUFPjYXS6fY/L1wSE7uFA5olJAt5xiBa2DrInFlYGTAaZWstD3BvoDZMoq/9qWQBBVE5S34eaqD6coxZ9nR4NXla7C6tmiqhso5RIG/QZCPd/jOhgGvH1O/PQvdoaA50vxJkvatotxjouumrb3a+8kuRbvTPxuY6ESi9OmaMKWoA86lwq0LN4WY12hjtOatEvPL0RQSX6HH5loBM/Sr0vFy6i2A53857d166uSvda6OIWAPEhMjz11jl7KQoRLQFP6poBxEQ69zuFP05cr1VvbpiiICfw5U2AeO84RTxcsS0t65V4ab/5dAxfp4ItaQY74osFFdMtpzmliSHGUJD/+a17gbKalJe9dn1LEZR7SAck0LhFHmZJtK7zN6Ke+Oz8jnPiisqFC6PdSsqDN1WVK0phLe/rNUAnBEy5hMXDKJ9U98zfdbUzlWDWksySDpRH2AIMGBNV3iEn263XId73O8G+d2a7hTUMGoPKaANpE53s81tRxceBufSRWAFWOfQTjn7HZ1seqn9cSs1QOz5LlhCBKNWEHwFZeYdNnn+YwHSjDqulXojHkJZFQ9a7cYlw1Qv07CfeloPZZsPE9h2cu7ab5c8AQnWx77WpP74ZTMLdrzwOMhrC9WZHuy6KQ7v9gjXDJdje58gPfq07x/OOs27ehDTNN+FCFdnD2zCK2lT1ijFcJRPAsGPvwAoB47jbXW1aMz1TYw9ZS/QatUPeLn+scLvWOY6R6pgC7DRF3I3Wk5vUYKGt4vrZY4WYxEsv7FaGKX5zIy+7MSLHDudd1ePg6T7Aop79CSHzO0/HoWCw/2tvzlP45Prcm1Bm3cVWjSmi9yVVb0d+eEqr8FX8u+8RyVrQoylWcNvJ+fYtmPQEYRCJL/JfLCcRkFwIJl4B+a4zBx/kcML1R+FV/1u54q3Er4Dnd0SIKRUKFYaoEjOHRH2FraRs3MpqQAbAAGYpzy5kIqnn/cUTMpJfcnKdYDirsj4i3tXnZ7NJI5aLaE+DsAnyxCZVmh4uPFsICBcnLEJY0RbRC7Cji82yIiCZYCB63HWokpOO/do6HM0KWpTI3ujZtSsuGm/80kx6dOKBSP190edUIWZUucws0cib/ABLRw76/rYQtSu7KXDrCC5/B+QhNK6apcdaSjX4C5CYVM47pggUr/f+zdWE1ff1bCxvpaI2DQ4Z6E3ciyEQn/8YkJ4KLnNW8UxfDBFVCViySvc5ILY1eTa/DT4rauxStPI6CuGaL0r6ur8kOlgPKLPhHJbLaIRuiORQxF1+wGL35xlhQAUx7cf5ONLkl5fI4sj+kYX+fHzXdznR5V/7aeYAHUUDfSK9RvVT2iBllEwgyi7EdPEKEeSVlRtzn4ahnOyHTYPjhIub1mEYjRA4rZCtBhSVrWCxAv9MpevE5bdip3LMW3lp5Sav/wGA1sSYXdJR9EFjEZ467W1s78vMc+mX0YzpQ4wGLH//+TjpVFsFw3uosvNhM0oA28SoNA0kpHEx6+Zuiod0kOSsQ4biCu3NU5FLzLppqjw80xuPzYDGMZFT9qz6+0xbKPhtvAxOh5iMlfgvKkis9D5Y4Efzz7OMhUvGRD304Io7bm/edj5xts/Hvsfg5bo/Qs4jgnUlbrOkCE18wjf3agmMkcouE6GzL/MTb3pEnVPG6QXvF02gA3QRfVQTPOPPRaZb+9ak5u8WsLKSmJ1JUiBk+sZ5olC3UV/rfIA92jdENKvEkGqVikOsWhPUbQ8WOBDZjAWO3qr7gnMChk8ApgcolPwBfpLmcANoOZPviBWT8dHnMT+ZwQFL1gayb1cRUWnR8oSDeVyr41GMF1Wyu179dKA091u61zGeSQtROT/QowHZ+BhFcTdHFeH5yXhKmblvP/sKbDWyO5L6hXoBQVwDl3/Rl9XKJoldMBoVn+M+M1JOWCRM25cj1lk9vwov8iD3kWa81vT3GEZMGTVU77joJUUnyQWAioe6he9w4ozOWPIYrzw80jbo2HD74sUt0pWM7FlR2r39uHB54/cu1NvWI4uB7tYY6u9rmanbSw70QS+s8DFxzHsyDghkNEVepFoPgOavGSJt7rfAGT8tHsr2ZReMgI0J4K8ul2FV/Kyas/1QIs2BBVFBfkbgYWWBjjpmmSrGWhS0umr4AyB8ckWPiP+S1P8XSeT5HxFcMAy+X+VtmUJe/MPjkMv33wVqAXGR2un3zohp4iCnS8zeIJf9/mJEflT+qVDaxza73EU5MKcFNo4xf4asBsPqn/gfb5tEvMcysGhii9bzFnY6Nr7EwS6d10InOFTVa0JGhCZyUN6Rx7rk6+t60d6vhSsAfjS9FYqHrcIky8YD2tXMaJNnraXJcLA5NdhPaU/Y7rUVb73pPPAv0yiW13nwitXQrsnTgcMjdYRHhqbjQLYRbDtywpnRO++IrEtDc276Nz92UhidiuqOko7b+sMnzWFYNC+1oSjCA0dLHCiSLG8WIfG9DnAAbjKW/K5nhbBFBEcY4C49T1cmTgDHjeoRNc0KlWdeaZTrI/B0gAQdwE4Zn/I6clSmWHKsxV8imn6pxfCiInofuDIwA9rbgiBC0yS/Ak6m0uDgCtihGtvQiUlVfsLjvC7AMaHQcH6T5Lqqg7iIucV2IrGhHyRPgpULB070kZqALaC4MiYfwa2ISvQBXbwat8x5HX1I1D0Kt0f+QBv/O13T2F4GpPUWkoVmXNSWwuMXB9DYX6X/KFL9pK3tY6dlfWO+xmI1/LS//bHTfDu8HG3bCk12kS3M9fIz0qyFdI4kJ+liuKY+wz7ei27C8ITNWMt4hFHqCplAyAQLDBFiaDoRwl2R7esy+aNQIxxY1p0Ei8oPsM7VPj0h/iFl4HwdWKOHTutWkaEc0FE2DSMU+E1dOSsuv1d+zGf69YEiJ+5L6QB1j3ndCxNxBFBCqx67CMCsBTtddp3y6a2RukqcLCmJXb/2LI7raMmkUY+LA/08ToGbF+ziRu2hW87WmdCiiUUZAVVtZ1B7xuHt3qfa0JMWXEwwHJFppXj+8LQqF8SPIDpxrNEPfTjMa72BQNlm1aH47Lwiviq8iiTMNoEzZR4OFiqT+mhPoyoiXjQ+s2TnnjwZDfEA9SxyhBBh969b3ZoA5+zO/Rwef4wQi1R6tV8MgG0rzBqmFuFVCoLA6mWV1uAUW4v6cmcZfi3V8VaOZlkdtBoMemjhKluqZMtcmoe2y/ZUL+taaz8Uc8cC1jlg3MMaM688ihqk+4Ch81JnPvLcXedL3N/7j9WuGHfqOX3R4zYh1dFnPggO8RepKJOtsH4y2XcA/+docpj1aHcjo9BC5LSPxMPsvCADnzPp8QuPK1HfmxzI1oI0Ypod5FClggsNEQ4voFGzu6vkWpZ6Ab21hhv97gqnRC2dScc0Zl694AAqLhgGB2N2qXb6NS4DAIiNc+EA3fl5Bx4MnFwIIZ7VV0kiWgrYzytAHNvmgBs14hprciMydb/hM9BpEOlFyxF35K2q1EGpCsA8AVUyQRT7adNrFTE6cPIsXzkLw+Z8vfUc0QsMNvRae9CaIqqQhTnrjHz59GfzRONG4zvpXPnWJgnUktrvsfH9+TvaQU5lf25vkJuU5I+xWVBQk9gJ0NlPwDZESRKeLtn/iull1XI1QbDaTkpIVjv6WeOVg/zVbFYGrjwdUMvMqnM4QnEfPq7l/NlyZKRp26Fobsm+YZbN06qJJRrdSbC070DKu4zSSoTlV3WXXaruG7hk85NVDH8bne+STozH4xrn0IYK/2ZSHu3eDF/u6aecjtgN+ozGA943TvXi//z+uRZKr0RVtOF7NUmAPCwV7UgBIbKGRE75sgsHY6yM0VUkiuIvdBjtY2+PEUHdUjRLz80dau6Jo68x+ep4pAA4UvT+Hh/vWqtrZgX2lJrYzI728W/QilnP4N5mlage5MWxAnl4R1bW6/0UaoEtisT00/DTyD50Pj7UeM/C7/KmcEO5AY7CP8zelmiGDHiitThemH0fGt+anGczzKuKnwxjwMt4uJNM5KeC6xcPQ4e3uNMsX7w9JgKAT7KUw3z0IZjoL3aT6OkVig0C3Ku5+HzlS3DHRTm9hxUnrTnDIE/TwQPkVoBZ/+zGHzZ8An/rnUzocxRnVHFHgEGQwKDUnPsdYtFACNQCMvfuTWYHQILjBE08LQ2KQr5tkN+knKVW/rspdxxz1K77rl1M28VSlmTk4GcGHKSWrh4Df7+b/CYVj8fKB6m1valGQL2XQ5Yze/DNQJBcuLlFrZ/2eFgeCbbKCbH9GfEGQ0wVdOkRTlFQt+8EaheQARyh6YqK/f2YBlnR/k9IbC8V+4jnmsNrqE82TiBcTmoKk0DA0u36G/Mw+8opLvAmf+wW2iICzW2MLLHdI4qL5e6jegsCLEGuOhkk2EaxkWuFBenhu9P5YL32Epq51RB+yO1qvF7TdAEHrD2RFFR2HgXobyZ8JzDzMVBbs+KTDBzbdxu8JN20pNG75ieULegbRYUWekrcyoqTMw7atREsTER5tZyPcMFoYYZamXMIqRA+BevMdZHV9vmgz3iFrHXMDre3vUkEGq9LrO0wmC48YkMHiDipxWnMe/1oR5slwv1rs9foMtZyoJ3GUK2Bp8ZD3GVJDCd3VHXCWxeyX4qlbqh4eQj9U75pHQ8ouCIYd9w8qgfAFs2kpW3fyyRJelNvaLt2IwlzMD3/X5imUPDG7m0gZ1jslBTTrKxrPOccYZITCpk0ic3V0cjX0p5n4jsy1j+wNKBGwRGVWIk0a99uy1L7SBVHXgQudNIi9wfN2BmbFoBw4v+EcL7RiPZJbf7oJ+e1FqcraZKvxMnMmBQsPWmeR3y7vvKHAvBVN+4enq+6Tlg3uNKmXsy1BKTjR/UICwquAiCNAsIlSlxD5sEnaj4ovIMVtreiWLKItLo0pXYZqrfEY1dYUo50mINAXtGwuHpZSfO/UBs71Zoui7+yAin7ifZV4d5p+QqHurpCEz74ekXd+z/N/PkemkwvukokNvvE/LS3yIyCWyIYymkLS0OI0pVIQwT43+tF9eEAyVxB2y+4k7Z5jKu3nNdPbxU5Vi4gFwTKvczwtRlFi0URNoZIcDQiYlmeWPdTDR7E3PA3nQcLQJQ8ODvzVeojyej2jBe/0ppdDDKqBmvYzI2KmGWPS8olvxTUPUYKw5H9fdsi5qmwS15sV3V4VPAet/Y4pn1YZzZ64HUFdWbbO2jhHOM+hW0MOaBrNED6A05dkmrn8mivzmNZ1s9r9V7ub0OfJBnxCqTqRbFcsUK4QBPDUm7/N5H6pCn+ABaPlERS1TtYXvHkoovgeRQNWjJR/H0nABqs+Vq3KSzwQt2lid5xnKGsauGHmAoFLoxJK4jixwBeMTisIH4MuP3Z3uYTOZllU5arryght1/aYORhlNg3Cn/VgTfY/Czh+/vGSkJASdYrJzNQ+hCALI4bQRPQoYweWfkVKzdyp4pOlEXozksQtBOMIyW0WGR8MYH3rCuAKP8QIBUtFrnxaOAH7aoxW3CDykl+Kppapc63kLSsKgtFUw5TQA2jgXUIJrBouik0fwX+30POYFzN8AHeE6Mr74Sv27pYaFZAW+PV29PjyW0JcMcHykk6r4Ie5166UQoTO1NVZifPhZ7ilGiMUkrWDsh3VUnMrO5qZMlyGv60gxoGMiiDtQQ2MoIZUnWCEVjuDENUTLLBqjHTE2f+CfXjci+OuNriK4uBQ/c4ZmYDC/KCx5IIbJgafgmGFto/kBPlU2uyoFTB7LonkqnN3bFNRx80TK5AbJJ+GcWK73fLtATK5CCOjLOH4Aaf+4CQOXvHvo3Uagce2namQimYg8zDuDKzpubvK+AY98ppjgMjrFtf49LWeY45UbAXiL7lqnpgw6sPT01pykpeIYqxRD4xhgv0PQe0G+d01iy7fi8O9L5+NNF1vl3Id9WY+QhOIAlsEpuMEWvS2KaH5XpOr/767T0eeEChtGMD5Xg3jwEqjPxXr5EXP/6Y/DqXpTXWGOyQe8QAoahd+zwqiKGqpyUHKgdAAd0BQn71g3/Hzn06yRFvJ7E2IJtuYAHM/zbkcgtq7+3hsde7OyTPGRChARW5ClRNV5ip/fUYEuf1IEWcLy9QS+ly/08pN1JqmjwI+lawxdjgFLeDSHurXy/faUJkSpkZ05Vefz5lX2DpMh21ruktwPvqvInDtWWtD1YOrTEqHs/6TlCENUQqFnsnP9u3U64IOSteKwsE8Eyvf/o5Eidtx7+lx9sXAxCpO/8MVhjmjOiOf95MzEPC9znzAOFeledvoJwrVrZLVYK0HToqkrtohvUK/lb+NxLMGWWP1Nv4eih+O9zJG7UuXSbVeVjmse6P7756XfMv9WVlnC4Mna/DX29/2mO9w1BouCLvuXJPb4Z0mHdiHfgnifQpjhKr6ykaLCmOxGgw0vQONlJWS+7Y/l0OW+25IfgLj2ZtwbsC1JEVxeBRcYdUsoS0IxULUTqsn/finKPDJF3UIqlU2YlEHQYEXKpVHO7MBXRqFvmDn7Kntk361LVUIDkmAMwWV0bgGlCgybZ6YeW5pUyWHI0qQATt54j+JPQkSTEok5TxobMz3NuI+ct9vbytnLub+K1dUprKccgWnXDN9ql8ZA7w/oWwWUIDJEGtJq4GAyVS+LpZ4xO3oH/tRgXdPLjGJzXhkmf4rW/aFrtk3VWyxuorR1VhlDuXueDtw8OssBMZZLBydB7B/EucG07t1/I/hUIa6gGuAcw4yFxN2ada/PLLfEh1jP6QkCT6Ga7Uh/mfBjkIVMsjT7R+/BhTU1lRW+tuY9i5hkTf+p84Y9bh4+3bhCQ2HvIU/xjrbiGVK/AC4U2sEQ0Sz2Wqi2UCROkslmosrJVgkNUG7mDZ6TnmcPKE38rXuVad73ZQLbRFq432g0yk31+HzUTq5iln44fd1kCoDQWa9wCno/E8QTictKogM57/FpHqs/UHT1WNyFBWpx4YSW/uiUbr4wrG5uwfQ55IuKR8d0NaTPlWwp+nLVKgNI1Spc7U+ee89RxeCwaWCOMsqzF8Uf6fd156cjtCpJak8X/+ASRe1v8Tv07eUFXmMnC8D4ai7a5gV/ZEKG2taSpnZ/I0lDSQWZcoDh31nHliilLoVwUmdTPXuQt0lHyh769HWLvEs/HnsYa8Qvbqj+nuvj8a25RaIhBIanN+HkbHbQTf7SMZI6FikuLo4TqkUU1DWV0A34cy9ZGILAlynPdiVyjy26q0Bh/Tp87ojwedIkfWoNY9FSpIrdttfhTWjLeKQXrTlszor3dNfGrxSjrIaV0KrLEPo4klPAL4qdDHctd7l4D6S8X/2wvzvloo2cAFjMY9El950zbmzqtOoufWe+LtOzd12FkS5fsJJxMhdWf602vCEF5NSLrRIs2FS9vhfMoZJ4mBog0gUo/3JewQxHe80qiC9Rwz2XDU928IhCAnaCuqjrFK8FrW6E8Z2yQ9HxYJ1NWJ7jLzv7ZCJA6g3WhZnlOd+eBcL/n4V3GkiXLa3eWjwxmb5y+Wf9kBvtwKHg+9DpFu+YBskkg/5E/3vKGmu4RV9Qt818TfnFFrE80TOIAm8Xalkv04P/VqYe7a2cYPfxcEUeoT/Vp2PKL4ZD0J4OQ0P+QysAgQ6BT8R/INVXMdOj99BcPzicDC/17Q/iNZNWnEQhz86bOsZ8+3wSlIhPAXDU7itzdgisRk34gFix/I9Hyl+t9BTIVTWS8ctUfzT0hmgbeULuP40JXQGG4v2yvUEbUe0SezKb288G/QDmhMtX/dxhOIc59x77Rt8zslp1oPGRI0FJabPv7OOgooiMvzVyqOpurUPkfwGs84X9iWbWEvS2mDIW5CTAWB6sXOWBrYoym0RR5qaydiCs56zC3fqgwysVNAmvaPiIYVhsYI2TGcWGHBgmIJoYnnvsMLCVwA4BppSkqh82Av9Li9FvGq5hUUIhfKbSpVuC6dGFOvo5clp/0PsNxyC31uT8DZ4XgHkJUTpcbHEgVQrl1s6U18T+A7UAKNhs1DJi1gCDSrqd5/AZIlvv6qKHLB46zGkXZMTPEoU4AV2huHZl83O2QiZ1+Z5ppmkfFD37l96EO/iLdVg3UTrrdIO5joFoWtQE4+YB9zC4CU6UH5bfCgSgNdyXp3AU51D/0fSOXmiY52uipbsmtNLH5sSYeGfK/qbICV33VIhZECKK5amhCVF98xjKG0EKZY0qgNVliBsFcZYWlNCHKnlI8C3ZOaknAqa1tvij5k+bWHveB4m0GhydSVxZWKfMh1aDt1QG9U8F/v9UkPii4HsJMLdkr6J6ZTjyUPpsnBIE9I8otpbKdg94LR5yntZdqp6Q+SgCs/TjKXLS0gQe9JWHOhQPYy3HmUY71Fl4AKrRjhFQ9vNBgBp3jp8uW+h3m99m8tT2iP4Z7B9RvuR6RQIKn5MTExCiR5/e6HOAbxot2Zwvvnw7ISyXH26MGlmK1Mn8Xseiga7Fd4//ng7NKrJKR1mzeey/EBgDmKbfXR2B5ScUZcDqt2ikfflmCIeoZp50lcFTsstAV7Cwmr5zBzVxgd/4NFGgLsvuM3PUtkQ0KM6kOTmYGTLkKo8iZWijHFmM1RKLOj1BrPuf0KqtwrY1DT/d/xMBFQwXkA+qU7vzTsIa/h1Q2zmssISr+DKP5qlL0ln6tyhqlH2OtGmpQ0fuybKQWn+2bV6sDHnKFZGnZAvkKK5t0zHjuuF0wTYCfdLYpj4itWbZb5HB6smLD/kXX/UFsKOfXskX0Tq8H3jVKtR3sHAAdsevtMJ3Adifjnxh2Ns1OM7vEDI+37/QKhlEun350BKfpB0cQhgNLfEiRZq9xlfFMUCcfIR5uMJMF4anjOiLeA5Lgi2iu/lNEPOQbVX9uDdar3c3IlKfssTC24o6j72kyqCASWfsSoh6HObmN6SX4TyuNve1B+SgxqSKanb0ajKDaCuj8W+6ToAZZG8DJjPfmZ9p8TDqkmrntUscegOcYBgjCsa2r6u6MQZJD3ZjxbEuvwvMV/YJYUud5Rv20L7cldJiD6rSmvhjbflSrgz+9Db07dz+WF4h08PG51dpt3y4icw1zJRL98bhONAycMg5hfXfxupt79r6AO10rumjJCF5q0hVaa5F10fSONsY50vppdjBgEpSt7kmsx3f4JMLb/AvHnVMM/agZtrlrRHJ6xv4z4e7o+OIrAUT6YcFkXgrA/sfVrSYFlYE3sIPfnjqeMabeJbdrSHILwOf6vLFrvVU+2CyQ8ObQh2EWO77I1BWDig2ck7xFtfmywKHMaLDnp9mp8XOnG8UIb4UOcRn3D96OKJSYQ6orNyirFtwGF6Od6RsRRW+iq8jhgPqepw8PlhYMWkfUcZ8blafHI1QNtRM7asjifbnx2ZscTKsyjoC/9LaI+X31UaB0v3NWpBSA8Xh78esBI203o1S7Ea29x2ltt82ZWSnULZzAsdz7ySSnUbiDmAykbIBYCR8ca00G06ImvmL0m6iuXdDqi3lQFsU7ZwTpIP8runfia2kJXsd6/BE1p+H99HUPasN7UEJdHIjyeE1NkwEcoKA8O6QBacJ5GbOcLbpaoW/9vqunyGd7bbBesZF9MmiLo+mX1SfEnmFbnpDeQxgeOQvHtuYxiztz82wKWTlv4cBKKXYx/XO/bvSdecOmFzVZ3+m0/aDLVbyynfNGfjUnW+fE2oz6tJWKtXD44b/KDoujJuW3AxJXtQxf0Cc3RA6YiNl2ctm5B9vN4tsPD+luQX1eN1SJFvD1erULOSO6lp/7w2htEn9N8hKWD+kagtdNWAjuUXQNrSUaPisbNGwq3l4DJkK1rdskMq3eZJ+EAoAdICHPx8OeboQr4ETgmOtBMtoLrFgcXJPG61Yc1WWSNSbNBoKRnaV024ZRP/2Y7hLKeEPkpRXlwR1Gy0cHcaHoMwmX26nD7630GxzRYQJca9pXxFDOroXHnhqw7oQwtAMUm+ZO4NZtc36hDH8IWoiUEgakCul2ex0vTuTFne/c6ftNLhWWwSt2WK8cNLAesmEzm0DmTxv0Gg/NVP8ohL6WNw8rAsSTYAq3g25PIq1mfDnnomXqZ3gyr8qznxh9+p7A6iTLtjTbPAQD0Xi0uinWY4oODIEDNVuBboJlX8cOY3pg0RjD9+7OrRJT+XWg1JGnf7Wya/3drLFR0M1gfYoZ/6ty5ZQwYLLcv5m1bnbGUfUWwG2fM/GmzR3vMhDABgs5RM012OaS2/wVuOYeUStY4DI6pWqg9Zf48AwIW9qwzEkOcSFFOYTedTUP0b8i4UlFnVoj3ILEhFTNroy4I7QBkFH+CuwNJhnmDO4i2AKfnZID703I2lGCDdyR6zD9BXzyJ51MvTkXMKyzbZZqlHFkE67ZLD0HZuUI28Z2Jb53jyYstbKtgtOiKITiyNYt8zfDTbGewzL8dVuFTt0BmLe/bJyQFSi+kUuvL9EiBj8hPPi0vgVTLgWsItu78hmysKDBIFAkr5G4JCyZl7t3T2Sqh/5B7hOCTZXpYgJSQaOU2aRlZA1r6B+IZ8eZPtezz2MMBhRNeffrKwbBU0FRAxzBArgZa28AcSnZYryvI0TYGEwQPzR/LkbTNPR9CoH77XkxHs43mnfscflvC2JmylpVG8mbsKFhuS4h3A2biCOGTQ0TfU5VcPzgVBaKbCJvpVXD5tBDNNag3Sgw+Fm3B4DuMsQeIFTluD3duUA+3iIyih4R2QoTkW81em72HBUypVGATKyqU64kK+p2MP9sHTWCsHusSsC4lU+Wp0v3FABHCbDM1rz3mWB3FXo+EXUWCM/Bi2PMGPI9s4FIWp3tcFRC5e6B2CruJbfsisZ8i4mVblEbW+vVNr6z6PSoqzxL2gO3zqPA+oGdRh627x9k86DntnMvLouUGu9lBEg1XOUdUnax9lydc2puk5LFJIOO1MEohpJ8jjszmIOZvMVpMAa6fMpsvz382QllI/TaDvLprH3MZ7Q/EEVzMD2rWumJM3VfPMU13sjM00Aq4/CftW8hSqriVJ3f5odIorTDl21lSXThtaOdUE5j8pudzHfomRqNzTJGjbuwovZzEPi+F1RBPJMcNvRS1Ky/9EL8BCJFVm75OKN1ER/euyskDipPGAlcj6QlttwAzGRICw8McTiLY+eiVCQQw2S2jqfr6+4NyDydaSHYzPJ48xFChzyQNfNSxrGHb/dGmLJDgQf/uBJC82Sx/x7RMhHMQP3+I8CJAvzDVR6mjJR+qUzMveeAZaAY2QRZqRTGI3ICSGbjsErBiuSVGFclzTj4keNpBPIxWFiziZLzYRTJDj96oPCvAFMHTEess0P8pIsf8jej2XyjSjyjHrpIz3CFB+Gc21n6GAo6UZnLJkRCXZ262CYMksXkpt6PHzIbe3oduFV1fle/uI4tuHbrGLKbyqvOssG/YrqtWV5JcOIGsH7py1hlMNiPO2up+tv8hDalBsElVM86w9i39SETh8fC0uPTR5Gwc+/RI/QHySOH5jzMP/P0w8BhR5Ec0VRy++YQZGn8glfPnTkt4vbQN8QWlvnCnL99iqDbrlsWsqANdf4LEv79rL96dOTlRqQo18i9J1IM7FLVO1PeIWZ6uuzRgOM/aydH6z6485iSdnL6+fllXqtU2+UbdFKs4wKAf8V9yH4/ry2hbN1B546cYCFeAoQwVzeqgjyi6vTevKlFU10Q4pbR6v0KM8pEynRbn7URpE89PBMLNIB9GuqJ5B3qteh7S2sjGdIUyFC254l3w3Q0r3RrhVBIKNiEPsK1+5R7v5HcuDz8w1rWW6TnLc7lLkf0LrUxeoA9/RkPLiN0/Pefrc99E9yqDKRdzXggJtGIPgZld+5qRQyceuZFcfx4xZd0EKgde5PFVbJYOefRagq71o5G2smGqkrhcO1D7j3RB0wlRYvqTNgYHZnoThNa6V3nPgkP4OuuTtT9leaPLdkrTdpxWk9nXetf5vePHksOHfWoZI0eTRnfHrMRKHNwvQOtY4SGhA2cvmEmK09q17yFujNP/JwT99G7pHh6Sk0aLP0gQYtsAgkoTs+4JzG2EsPtzHsSmO82KGX49YYYuzlURoaa/X0MfZ+jEvPRUbZ1DIp2/OZ8C6+qajXXSBzh4AywQfpuYqxMaAWJF7RB1X8rmG6ZaQJAgMEFXVY6I2jyRZCgBxXgW3Y81HYhb94WWknn+yKzqbZ/dsJPr0gszkF7okPTF+XlNozgLyTqfLhc+mIja6skSTxzs/maoG+XYhy3Vwjl7LWINxvrAJeLSjU4PehdlMvhrhAdxd8KEGe9F8yyWlrP0KOQNyr7lNRepzZWxGYwUgvSdU09a99c33AOYr94c7V+yrwYEqD6YJ2L24TvbbYCENT7ZB8Io67xBf9Tcv488JdR9C7sPt/Q83/FEVeWODlnq4RyPD35x7NIEOuT0wYbdXt52lDpeJi6oriJrosSVUCXQVCf2/HdaC+DvH+S4nryzCfwiQQZmgVZRFhufWMSz8DI9XCBNSNBLJ84uEqX7oGMXai/tIHrfMFwLqGwT+ySRTR87whdy4AMkR17cWHaem8TISdF2t4U7jz91Ln/vfbA4CIs22uuYIjNe2EATGxDM0zf5py++jJ7ue5TqHNEsCtTxK2MBHnkrXVfsgNF1pL5ulUN1GMjKJywlJVuxwidQ1vI6FJ5mMxVQs4KKbg3rztF9YX82QsUcqbyoMX6iF+LeoIl85TF2APlJ3LX8Q+0BRCaU28FcLgZz5SQdQezR4VMP0u2o9eq+yhLmRx8uwGU5L/XlIBqaS575eYf+PZiV1QzZlw1fIzbL2hsXsliDYsMJ3ON1kNhSml5cVG0abMDKjfF0Pi4km4hTzudEirPe5ru27dm6K32oksQyb6EvkqljAYzLIxz31A9SMKCqZCDfGeqQPhjbz5ttBNbyEIPLw/wIsYFMYM7vcaxyC2B/aD4JQEEq0UIJik/OF8nWy6y+wKCCgW4pQUrP/+dkUtfwJhqjnfIeGD/jTuio4Zy3Kycs5xMsSJ15mYDRkJbLxw7o1yyX4WQHHteND8DVY7UK1rTV3GRAnyl/rIzo3h6dUdrWF2RCBwlPKzh6+kYBA68MapI5C4820a5xSKcygz6vhu6U9CnjtLI+XEGunfnOqHPKL1aCGeqsrKxc5Cb69yxw9lXxp7mQzotfpAWrB4RcZzuEHqSqXfxzbxGUGJpP7fwekYDCCzSnuW3ao/XWhEBpk8XaGtj8zcJYRKIxkjZ+Tf0bJp7fjSG/KqF4ThgQy96sEQ54db9dnwFVKdWtMkZugG2LyD4eB8kLPMY+TjyTEmPka5AAQgqrq2UCVNJFG1Sg5TUF99kJkWLQ8nAGBYt3qRLdg43CIIDLjXlgJ472jXpExqOn15lcRXkEI+ajOS6snVCTIumPjNYgYIhz6gmLYSNs2J8tsTWJbkXqco1+h2n9GcZqZeKi/NPlFb6aZfF8x1sf1i9/R3Ci7lIpeMoh/JX/QqvSZSsgtnaS/3KT60s7rOUrTb5cXCARL5o0i+8h07MBmSf8HtaUQONed96Jdb/M4Mv1aeclZtLxYD+Xt4+DeDGfM5PmXyYRxAP13EeyC1r7IR+3I1ru/BvSL4XXWxpGoVXbwtfbUekX5Ce4RGGm6Ij2zkM8/nMg9h/lP3VRhyW4AqW0DZhI4PTfPnBn+HfpdntFWDDOJhUXztlprSo8m71cEwkbGB70/NDrD+VYBHS9FovkW9HlT684uezPNNRGb6CN/ZZe6Ut2GBEq0Bbomej+7YIjXIq1/6DKpNONv6gLGzyxlwGn1X1ABxxm7K6DbdnwRkaiX2EYL/lw2xpDghXZL3v/uVmDXBX+LZNcipzIdypHj+NBqmd/P+TgTh3HNZ/EwKATGKGQN3VJXBmLsqZmPHTucJ512XVu9BCyKK0D8mHSgZXORryzMkx3yBiXo5e5psfvAsw9ev4yuBWxC6gwD+ZENEuwTofR6dc6tXvMrARjE8p/WRXfUe1NXCbYqidlhEYzx82BOyv+T6KBYLmInpWruH16Z4KhIVAZy+7/24hPrwjP1DXvXBtWRwpsnWpSv1nmEGhNNo33Ht5/UxJGpexs6Xou4ZQ17w+PfT04znlM92BoRwEUH3RUR0oV4K0JwXAx2Lj3LFn/tGf0mZCixrGE9j3D890uHMz/Ob1+M+20RZYGEjqCEDNx+ClAGVCfyNnoL26zmEf669Hp9SrkGSabqTAz1LZ9EG3ioq5RulK1TvOCTlKLtpx4GscGO9iUaR+EbwbjRqvxbvOtlHk4Ewbh5XLdG/+mbhcXlTvvw1VvMfSIcRqlUTl0WqBfOhrKVn6LfKeVxNBpUcGDuzpb9ODycEOMlCVrX7uHgI8ldhWWRD1hwAmnicsvNNnPow+NTaiOKlHOPfKAlX2RnUf/QtMjpH7rCD96EMoiIv3xrsVr76N4IUtj91bBqejGfUgllrS1fdH321XWCHOY9csQN/afkISziOP490T5tVWqDjij+fwdegtGkK8mL95oSGgqpfkE516/5WJLuhaVxD24VxPv+A7/3hfgCmpnttcJmEp4LmSz88bnto9laWSKGuBZflanLJnSeP8PbhotjGJJLvZz5Sb5PK9ISH22FXPq2PkrOWv/1JEb0KgTzDpuy6SX8x30su3F48fDU6u+G9HgyLbpED+N3vQw5lFXjGgskU5157M40l+Zpqq0WZamnNprHR4PfrUToDpYnQRN+vMdpRlLb7D3gBSe4IgkNjlqXv3nkMw36q1gpIYTRQw4USp5HX3qX7aLLv14Z3L18B9SX1soDJALjqOKPxr67qv4JactNpF4fAz3LaK+ZLYENFVrktO25Ny6maKU0vr8pcEAIfnr4lFUP4wlFTadzaXKClzOLIM7Ko57woghWcHoAinG3w8Byrz6myWgvBl0GfDHRojG5OpXVlEvU4XoDgy1kDzbetxMKpxLMDc11LK9NnEbGBp4sOz2us06LeLH6eqJ1ROQOLwE/hMXoRdg+PTzjPcotJBD3aj6oAP9JN07iymBOotP+dBQpFUJa/wBqKtmRPJVZnXVjkmB13la3vm9VSI7RpkeCDiPsGXad0ADY8w08Ajh3chJMTonmeACJ7ME4rbBwJytAr/cOEbvDjZE3cdnUtITb5xnyrhehPdbJNlKN5GIInWI4naxZuv/ApmzurJyt8KEWDvG4xRJFzeB4FjAsVlmFY9ZFCByVkiKWcQIm22MxzDhfKQs7jwalJmnyUY8olSVAyhp1wc13JyJkClv8UxOoSCrTIH0zKOaC9qpvl2zd0w2PozMPwk3vfuuoqXwPyfS5H0iYVrVIQ7QgtWlElS8O/kecaKimxzeZOpsf9BlGYwNEUSOLnc7SeBWqmANa1a3jBgHiI4Mkl7WJrXvWTFtIuUHReL7Ag/cJHWqjBfm4JZ9s3+HhfBE4nVLU23w5G5jUEX68LZhSwLjuUhNqxxNMYBndWsBqlWehfcTM0MLgn7/8Y3/EowMbLkGkUzvnjAqXhNygLfyb3vrzGgbip9KT/5o8zo1Z8ehwuOJICCOk2DaFXzgpc5OxA3K1RhGKCeOoaPhBMU8kXhJbHN2RCVGxNiu4Vi0/oN+0VMovBL9UlQUxeoyu6XBrT3/22V21Ve82SB9WlenpHWdect9BbpbAu+ZardPfPQMJtsvfcoFm2jtEEHb7yU9mTpATiR5inp7reewficpxFlBVbHa6UD4C8Fizd6w6mbtNLS/5plZvY6m4SK5OScQ087X3HvokfxAUBLs3E6Jcc8B2eqa66ZHI4jERVzLda51SS2X8LMUPDXeHGbMiA+gYaKjXUm+7PI7ZE6/15XiIia4zzG/P7SLca5mNm8wsI/urMXuLGAOsIVvL7r+kte6L+zlpBEWJhnDfXefvSJHDha+IBkXIDWbIF7nb+3TSnLj5G86FLHsDO6lyZzeJXu5GbNWT83uFb/h0O1BIpRdBN185A8pxpFhFKbboZUo77SZh0s3KGp8X69UFsNZ3uvWlbddprWfRWsryvBxalxcEmnrtfcYW9GXGAS/SWOSn/lOah95agtuzxAX7G4pzxKo2ZJXol91B4ad0h1fDWcWNLY0uFBxoXDFBDcCnGjSSFBzq+GuTs0rquMiD6EFIs+OsqTyhnRDdyPNYxqAWVTZFT0g5VPlprI6JZT4z03BwxDADBV8Oe0OlJjntFzg6/kAz1J9sQ6UiHqkEcD+pXkO3OzMA8U28wKa/vd3vyzAP2d4fFxs7K50m2O2BSSKv+xCVLrvdCV0EN6sEYOKYp7jXt+cDQWwQfTJxpIPmWASivco3q/We1NMBX55qB0Fa5OXWb+CH+VE1pOynAhDKn6eoYJ+7uFHaXq763jilQm/9lywZ6vOZXCTIGtEfO3IFosyz0d46770xZWCLvAMZizIjvduqDQGFitT8krJfyUFuLlWRspazislgigGwNLcHOWoGHMPVXcb+N9kckdWshWKeFdpp+jqNSmr4ypofMdW1cJiw85yZ9C9oLDNOqIbnYjCfYEFrq0XvxiddsmOGCjjUAlYInKkEF22zp6LZoj9O92mWR9BlQU613Ywdi+Cn1vjBhKJK/kV9yzt3+JoTBYdYKp5uuNXCDL+LTAZ1AldqhMuiHvBwPe0FYzuwpLs7xeUUh4MfNkdaFQnvrQxbm2/zeTi6LlOPdxAt3GRYiER9yCim56LXb7y82eLCvMJ1ZfLaU/t3U0/jqDhrVA2ciqWqFYol4j/M8RV2r6YD+cfuN6tGcF3pETxMXHzAn+9WT2XqwPs8zDosoK62Ns9pfV77lFJ4z9T26TiXxpvOVbj6NvHMKy6zBFvmsdP4L1ha4Ok5yl7anmQgZEILR0dEfjVtRMO0+AQBCziPyHaDUHSKT/F41BsX3oZQ0Zc1Wsxeba9O0v53A30YW1zOA38o1T257tfRDbeuEi4q8hU69+BgerRbW4ZOaKwCRCvfCdpES3GrFUjjOF1el9ykCoL//HP6cUfqBU5RWFJHt63UBljGmie92rnW7nxhT9fgQz45ivY/0EeUZvfNiQJf24+9OKBbkOVkNStiI4jZw4Vxh3bqER1CDzSOsBGXliDU/B6WN4fX9c0Y7ViG9QZaDLbzcFuK5nFKNu2Bz8nSTq5S+9LrNsMxNEuhOavZ90IT6hoAZfpjXCZGrd8YJcHpEicnwpAt7g7C/46au+6KlwMZugb7aUYmN36IajCAkFvwRr3mlXWByK4/IU9RSqiAWwE03xXQOim3tA/Ne0rJyDEvD5vhgv7xhR3NuEplncQqNe9/jce/OoFuP8VrmTUd7mKBNwjuAtCUdiElKrLdFrOaLOCsnK1nGguuZx/+Au79S1lsG3+7s7WsD/p4tQhvVgzUvaULl0Dpi5uXGY+0eSQQg46hRj7nGebyQLP1K/yjlzrOHiWWicGb5n/XOeylNYYHft26UDjvhZn4tESQmtSHBjIt7YPziW2Yl6N78jmbssAQXGkFVEIKLAYLzvApmB6PgJWRC/XBEX89zsRntqjoMqpdbm7fZTGdpAEaubR+FOZyKu/DufVnCo/lksSn2Zsnu8QXva4JaCADj7IxZveXhnKL7t79vF7MfYEGzOifnLolvpLYcGunqDq+uS0cjVGq8nI41BtvqMTVGv7i9luYceeFwy4Ku5x6SA3x9WekCFLdBspgS8KIwjaa2TpaBBVH3rKBCyShYD+uOXOQ8qpBQygta4q4x2l3blltMk7I5hV7aW4XkLL0HHc9XB8ThLwmeCkQUSbaC9WFcZOi2uRqYDDIJ56QBE6NWZEMCiCBaHRWcADu7JfGqpklVnf/SpeylWke191twdlyy5xdzbFXG2B9S5ht/kqg0M0V0g1IxRu1fqg7jLsvh5HYfy5kj62gdaJZA4OTx3GJwqWIGOXoZCbgD1ldJVhrIy4BCJtqAAIf6b8Nfq7M7hdPrectXpHKNfCNscHvouT/KYxtgpJVKeMM6z2+/oQSmqn9gUjcMRwECzXFiYjxLLFhYnLiHMD9Palegtu5LxZQv/04v1Jxz8GdEuaBrR3FZwzo707irWiQsjIR+h3TwrDR81hEmYTGXWJgjsC7XR8aLya7ycADrVd1+/Y8YqPTU/NxHl8zSt1wCkFWXEizPln1/jodI8YuWuyt37AG42dD/3azTAc4f4PAE3NkuMJ32m3TkmcomF4NW71j5wyazXqFTrBRgVMEM7yc4cE4BmFDijyfoAgK3E8Lwl9pdK/TjEpJavU8mB7qESG04138qYIJUzQZ/5NhmWYz+4sqqOkq3OCt51cRspJc4BkApmiBvBFx7s+guS0s+8c+uIxzbxCDJ+ERfzk2WzNWdX2BTn+LZJlyvgOWKcJ8MU3yh/rp0A8MObptVomCYxT3orX0erV18XFOHYiu9n/B8RsdohdWDpu9rrIxiDK68P6Vx+J8S96KNyuWzkbmt0Yaw1QglsQRc1TNjYcqRpgnO0XI4Nyvw=", + "randomIV": [ + "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" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "staticIV": "OjRLkPvGpotGCMr0zGLxEUx/0MtbmUZNJY/FuX/TCvI=", + "randomIV": [ + "0meGYlRspr2v+zYVRXzx09S0rpgXbna9mpKckWXqJL7A9SRAhWZnwRXp5VYMm0ZT", + "itil6Qn3C2YlTVkrWe8qfb4gnd6ROvMRwa5SZEkWYawo4CXl/Bx6x364O+o91lf0" + ] + }, + { + "cipherKey": "customKey", + "plaintext": "[\"array\",1,false]", + "staticIV": "UQopbBYnG7eEr5gTLQQaTiloZha1QHvObi8m9SetLik=", + "randomIV": [ + "7SiUJ11LnUKec6Qct2u1DALny1s31lXYb2+qdvw/OwCLTobdyN+4YV6mfFO9E+S+", + "O5lLtWPQ5T9EezpYfup4w1LVKw7H7g/cGgr/wlhZYMUQtVASqEeu39Si1npIiPwA" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "{\"hi\":\"there\"}", + "staticIV": "8E8VNVX79rb6ECdvZcoIFA==", + "randomIV": [ + "LUaBbNlrB9I6bQ9plXmaeINOOGYsbqFllH/58boM1kM=", + "mrIFfobzbvsIkwHHPJDWicUNayOg6l2QTwmv9/cs/yY=" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "staticIV": "07no0D3nEqgV80rOy51sktouxuCPCNLmBQoTh4XXnmE9AVubMfT2Twh9gcSaxOg+tGB9JJ+5fcVjosqTzvYG4G2T1DIVJjxm7RbkeFxS9vc=", + "randomIV": [ + "omlJw/8KHRdhjG3q88ULHf48TMLpw7X5XatmWP/tsIASaj9jKcVFOU45qBgO/RV69mnJN7OWPqqD4n4fVCjMEWnm/F0bBM2BEf/vkbXiABB1fdfaIza3ZF4xc4gfoH3H", + "4wufXvrORmj2ozhNqX3Q2UdvsZnSXT80Jo9mFD2KiXFDmvhP6rQ0YFRuAlVOSWqNI6qnIR9I5rIqY+79l3bKcV3ZIIZDab3BSYUsyUKTi1eUhr1u/WlnLYbEdT/U6szJ" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "staticIV": "zXmOtQbZZxxRM2ZtdB6AqAOWfES1ZOJ/v8CjEVIr/rZzsCMUw/37QbGPvuT5Xbwi", + "randomIV": [ + "6OYbvJkvmujxJSuBByNXYeMYbhkuuVSPy6xgTVF84AeHKg+/JjZ3wFxB3oc8tBdx3Ig+i8qrSd6BdxEl22ghhA==", + "4jvgW6a/1nZ9yTt0aV6W4xGjTL2RuvvpVCS3wDiOpt7ukOLE4dpW4zONRctjKFlXgFwxLUpzaI+iNHW5Txq4vw==" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "\"\"", + "staticIV": "CcwsgtxZkxFBTdwDtkvOlA==", + "randomIV": [ + "uE3YTyfMa1ADaEr3UPehJuXUcwVaMt5z/AJIUeF61ys=", + "Kw/ARVRzkPeYsZ9mJ/aSOJQQMfW159dEubw5khI7FLA=" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "42", + "staticIV": "7KAeQkk+HJTXt7fD+0pmvg==", + "randomIV": [ + "t4VyLAd1lTzcUJjp2q7r9/lHztJPYxg5ZRCHJ5GE0dE=", + "nnABQLnelgxt0Q5kvCEiMGR8rBzsiOHhI7wn54CkYqY=" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "3.14159", + "staticIV": "NUavVtd/DDOaLYmHU/7MPA==", + "randomIV": [ + "jSZ/gPqO8pJh3wgF4OOO7PEuXxFzu5OYvYD4/INT7XM=", + "1odMzFOoBrttTdXhnUOs9kCn3Om40DcZIiXyA32+2L8=" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "staticIV": "1IBzsjUrR0XIwYS6VP4Akqxkj4118IiGG3WbODPi4b0=", + "randomIV": [ + "HQHd9bgmZUXIu+jjRT4WuYpBLUUs0aZwkRaRRKlSqQH+OfyEI4ujazdi3DxkIkat", + "updYgwqNSMcvibI6/mdjwRQbNCxKBRydbahV5KhiuOIDA7hir1kFAeTSVzhzm/26" + ] + }, + { + "cipherKey": "enigma", + "plaintext": "[\"array\",1,false]", + "staticIV": "0DRt4LJ+A0Ufe91p0pcA0M5hE5IkCiP/mlWlYdWmpuY=", + "randomIV": [ + "ayAJkIpH16427VwPQXdPMmRb1QNF0p42njZim8LltaLTtkc6pwLE82ZyGel64jkK", + "3aNtR2/PDVn4GN7zw/JbhwTQrQ7QYPck52eOc9hAd819IzmGDf2bG715nkYIfwFn" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "{\"hi\":\"there\"}", + "staticIV": "gARCsQtKDTGPz8u1Out5AA==", + "randomIV": [ + "vUE9z8D/Bp2H/QEHo6q/xjRhWfHe3WVCnI/vyc/+pZQ=", + "gdFbNMgzCDSpWkC6VIAU1GHjO3HaXd3sbpxoCz3eltA=" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "staticIV": "xosjmkRfWIqsxviE/qTKzt2z6VeZc7C7Slo4KuJnUx0iKFKF7OeJlxXW6HX2jSLPeh8d+pTZHuaY1AhaDmyOKS4KGvrIGowu/TeX8FVTgXU=", + "randomIV": [ + "fidpEgA1X3tTajJbR6z6Be6utizZBbH011pQINxB7rUi5Mz5FRPJ2uYSoT1xv1H8BqY3Vqp7fP3p2h4BFrkeeZpC82oSnh7cCXmmdY+X401r4bNykhO1xAEUGuNO3d2J", + "2WbDMqQStdNc6MLb6NYWX7U8nZ+6ihsEHmCDPGL8yQrhY0FhpNcmQDWOll6DbAGFfJCATe1CeCViJhkcKFve9OpBgUGH8/PQwEewe5fqX6cA/jQWtQL+OeQAJ+9HRJkq" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "staticIV": "igLMj1p1/6bi6zOt9vd4NYehEAMsLMIzsVCTDpQ9/vIGWLRhihONzRp13Fs0J5Im", + "randomIV": [ + "d3b1+i52+yLAFr31sqFkvgUd1DZ4+Rg+i4Mk5uc+NDOKAvWQ3dBYUFVN/4LTMf6DVEizN+Bv3KyTtSTmU6lt5g==", + "pgRJj0affpnXgOJv7VuYyV1MWhWHV8YrCKj1LmiTAESOYGHEQnQNq6c6tlmJYisPxd9PVkjX4p6+PY4RMSs/5A==" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "\"\"", + "staticIV": "mWFb4tXrXz70wuJ/5ks2zQ==", + "randomIV": [ + "RxOfK/uRXpdbpoSt8cx23Kt51bZpp2N2vk8PL/vbXb0=", + "QmfJaLPscDcyoUV9i/GC2te4lF7cMMZNouAWJYgvlvk=" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "42", + "staticIV": "ljE6KOQjZ6jzj1yXWERCvQ==", + "randomIV": [ + "TJelYANlLrqUIY5EGfSknXsKmaMC9LaizMjyO3RdZKg=", + "n1yz1XL0raOCUqJiY5vXZra5ikRZMaJ+c7w0bJVguHc=" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "3.14159", + "staticIV": "Ib+JxIdHG/gu7B0Wa6+s2w==", + "randomIV": [ + "oJAyARFfQnePTeJ97Gk05bFbu+Axk/U34yCBWPCr1EI=", + "26tVBCUctmst+yvGjx47C0vt/X+bVT7yIn//yDCXrHQ=" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "staticIV": "RiGhL8Z8cSZ0ux1sKZRHijko633SkpNz/kRJK0sPmII=", + "randomIV": [ + "PSVAXpQX/ObugLzQSGn760yQ8JiWI5ptyZoKfZ1O1nQFx777wnpvoN9KrxAhIW1F", + "zGX+XDpiP3RGinwwk/fPD1KX5shKbNFaSogDaqEb80V8BAhHlEjEofuHL7EhD5Xr" + ] + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "[\"array\",1,false]", + "staticIV": "blSsveqCRw/EZw1tk0ENPzFWeLAaBEWZ6PQZXpBSZbs=", + "randomIV": [ + "MkKsGNl6jXfPJsJQkRCjRtMlUqL8pTVWaVNPCSRvx8ftQjiMivksNCfxB5OfEat6", + "tLSiJatSHM82Wsh+IaS3zFNqV7y+ZD9h1Tbv8rEf36XSO8O4rph1sv9yjG3FjErz" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "{\"hi\":\"there\"}", + "staticIV": "FwBoQCdsUAh+EUZAwH5bBw==", + "randomIV": [ + "P+CaXxvzn/BWBrz8wxdzFapPmPdLSgaURx/5FZsVVro=", + "NYMfkp3yj1qJ8ZxFom2Fejs4gdMg8KdKHKZCTa28Mng=" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "staticIV": "Dekrx/TJeoj3eSxzwyq4LkjfSavocv5I/8CXLug3340v6BeHWILKnGiI5HvTn06ViAJuHDoeeATtiakMQBzgsxgI7fnxSi/gXuwwfkTUQk0=", + "randomIV": [ + "JAgnMAObiNvk6xvpYtxY/zuq8Xxc3u7wvAmnsE3+VZ6lTZ6UYXs3CCyYBts1LBJ/BQLVKrhRzRTDsZ7uiEmbZqYPNmP2B8P0dpWiIX3bfLwO2EaiTjhxLpSFWaoCVcBq", + "QpJyib04VD7KBDhefMPIIPWBO4cKK6cjO2Yk5+nfJdxe/tHumsVdjg/lKn8nd2KtonDdmPquHdt9b+jy2Y99DHINk21aNiNzpURDQFjxsc2koeQpvnDL9SDNYD10CymJ" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "staticIV": "K9lwtOyzKU2HL11M3pax7DmUrci0cJDLtQLBFrO7jqyKvFU1aQFkHZpCMfNkvecj", + "randomIV": [ + "rx/a5/Aynb44fLPSuiP43MtaiVTgnzIlVzEERthcWtgS+cX2J+CJu+GG+mdA5tbMe59/7Bey3A4yP9rzRXTBoA==", + "BmsTDfct0szXhZHNzu3TXGEuaSt8qgwe7eZlGTlH8I1GjqfR0gnBP61RR+2sxdo6gqLsW1u3LhZ+FvPUPVhw4Q==" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "\"\"", + "staticIV": "dz8Y+F0SHlqKEyuGLhTMgw==", + "randomIV": [ + "KIr88jTKPSDagnMccbKViA2xSntiNyrQz1zQqrOOc5c=", + "crZ6B91gjDjYkCEb+DKP+WA9NqQ5Fqnf0g4f/8Gzj2g=" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "42", + "staticIV": "qf6HQZV/WYRQ/NA5Uf/yvw==", + "randomIV": [ + "Ub7awiJJhm63HIG7Hy/29LMcJoWl1yo6a0jjOKbroQw=", + "vTpfqgWGNsRnL4fUQ7DSw2J0T2yyzDz5BjvmlmR2EqQ=" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "3.14159", + "staticIV": "/Fe+ZRSNTlnOAyA1+XowJw==", + "randomIV": [ + "lkJEHt4rCA3aTvNb7df/orm+7HOQhv6Ernl4MoS7/tM=", + "hxMOyGbN7FtfG3D8p8GEnyRhjpff490VPaHetvM3OCw=" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "staticIV": "9CqILVhoAV0R/Lo10dGA+dUlpV5iDhzezw3B5Ik9o+s=", + "randomIV": [ + "Mq4I9A583h+cMhF9ER8a+UIrhhGARSZ9oaX+QMaHQKo0YnEJ3Qb8TiWeVdcqAHrk", + "n4bxawlt+vbKYAvBMycU5FZX6JB5XVx8CqJyqsOyD0ESzMHr54TTeiQHgLUk5wKk" + ] + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "[\"array\",1,false]", + "staticIV": "DTclFumUb5ljq3BfFoW3+DVpVaknuYzISjnmOYDEvvo=", + "randomIV": [ + "lrHvjMGT3LVlPD5zT5MuZ9X9YXnp3ehnV4ANNdZSgukBQOtEesbUJgWFAZK4reTO", + "JQCfjEB1KxW8idO7nCAuwBethdmWhqx+/i5CjtVGgTWKbuXktRvc2Q6XipRmwMBm" + ] + } + ], + "cryptoModule": [ + { + "cipherKey": "customKey", + "plaintext": "{\"hi\":\"there\"}", + "webAesCbc": "UE5FRAFBQ1JIEBmUCSqwzsB/wRUJpmkEeEsBU1uYyuoWkil6c/AR0HpV", + "webLegacy": "AzhXv1kPwMtL0U30PUb+ahxcOuEEzWoxxLNfB4i3pnA=", + "nodeAesCbc": "UE5FRAFBQ1JIEGWnGxLxE1irMRTz1qp/EtAZuEhmPtJt1MCRHqRjLXRr", + "nodeLegacy": "93ad61c7VMKbOiqFmffj1JQBXpHzkqh2tIRblJE2qCM=" + }, + { + "cipherKey": "customKey", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "webAesCbc": "UE5FRAFBQ1JIEJn0rNNj7FlKt8HLAygCLmsjrEg3z6m8ay3Rms4PC7zrTaVEsFn9gMD30Sy3MT2q6ysOwQoc2RHIT0+0fuKh7e7z2qsTGbn63SA4tEBe7ULBtikJ4mXp2zjsYr+3K4gTYA==", + "webLegacy": "4OiJXQIBFDAHZuY7VTqvHylMi9ywU1Q7bQSqxi+J1KnV2xtdXP2qKZ5KQrYh+JJbFhNeldEpZvgdiCKRPNhSWiHO89sXUrPQFsedVmYmLQe7cUQrtrJcVWHLuN2NZoX/", + "nodeAesCbc": "UE5FRAFBQ1JIEGfC2aCv5cZ6H5KhkP92XxaJR5jGQmbivV9QB29irktv0W5btjxWwmi85zzcvtSaKXi+gOhFRgL0j0GrkWRY1ttkyxFOQZcFW1gTA+63MJgUtVzbIl25KkrinGX4DE5dww==", + "nodeLegacy": "bGToNyKfrC5j0GSa4CxpMnOnbKGcdkCLXgX7YBEdVDBdhYJPyS01JqCJJrlWp1mRpm0M/1FK+5qytkSaCyfc1bUf5IH74hw40PLC+RIF7oChvEgutq/8ffxy+mCgumi3" + }, + { + "cipherKey": "customKey", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "webAesCbc": "UE5FRAFBQ1JIEN8IoCoRNSMWooYla6QgUJFlW3i34yanEOrzAALfuHjHPj67mnc7/7GHnByrcOkHe77xDSJeDtpNWrNpFscyytA=", + "webLegacy": "aibetekXWvWy4zxo7uD+cgErhmnJRvnGOUt1irzkOqQYsPE9lnyCZjQZoNry0iJ/hwmLcKVTBsBYRIyYbJYElQ==", + "nodeAesCbc": "UE5FRAFBQ1JIEPhGRgkWWCLoTrvKusC3ytJBLt1ustrUFfKkKYEbIBjN+uEFOgXUZ5X0yEkXAkb7f/gZFo6Yoh2YfmcnMz3dMVw=", + "nodeLegacy": "6Ue/YhNJdBOE9RU2TDsUM//VLoW9T+3kagPAZFoH4pm65DaZ9LXcqKLwinWE4YrBasDAeBiumhRjJkRX/idkiA==" + }, + { + "cipherKey": "customKey", + "plaintext": "\"\"", + "webAesCbc": "UE5FRAFBQ1JIEFU7NChW/CE3W/FRgiQjV84cwFHl4kTBQwlGP7ePRN57", + "webLegacy": "o2r/lRs2BlTb59q4bXgGCU19GZyXrqGWv9KF46VLRYM=", + "nodeAesCbc": "UE5FRAFBQ1JIEHEewo0fVbJPDyF63bt7VOLGukJaC2hq874q0DTcIC6r", + "nodeLegacy": "e9qxzNDh6nzNlhkTK53gYtUMA0BFKvGl0rUDT5//PSI=" + }, + { + "cipherKey": "customKey", + "plaintext": "42", + "webAesCbc": "UE5FRAFBQ1JIEHZJcvW7kOC/d4l2m+bSsffOf1EKEvy35sl7NiYv0WsR", + "webLegacy": "bvEOctYp6ZrhJ8k0sU3qsz3QtOrf1e6HYKAyuOWFeVI=", + "nodeAesCbc": "UE5FRAFBQ1JIECeSWWj9g050hlwCmHe2FP4FZqZkvmv3Pdbar58vBrLM", + "nodeLegacy": "mXMlslSCYykAxt7U4cQnaV5vi4I0O9tOi4xJXbPK6Vc=" + }, + { + "cipherKey": "customKey", + "plaintext": "3.14159", + "webAesCbc": "UE5FRAFBQ1JIEP612GK/MqwOaZpT7GJp3RcDFuD1RiyIUdd2uSj6joZc", + "webLegacy": "wB8E3LcHCXRZ3LMlUiSQYIMBAKvN6+qP4gCYUYqhfU8=", + "nodeAesCbc": "UE5FRAFBQ1JIENHvOe90sMF+pf3oLTL3bSuCymCW7hSWymyB0VE6SJ4p", + "nodeLegacy": "V+bstjm+L3c6BtVIh9BhCHsPtNvUu4aGxU3jM9LlmzI=" + }, + { + "cipherKey": "customKey", + "plaintext": "\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"", + "webAesCbc": "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", + "webLegacy": "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", + "nodeAesCbc": "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", + "nodeLegacy": "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" + }, + { + "cipherKey": "customKey", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "webAesCbc": "UE5FRAFBQ1JIEJohgGA6ClsMvkx73YidfbSrqQu3loD7GsE2FAFizjTM8nTmMjXwIiU2dqje0E5S2w==", + "webLegacy": "gCLnGQ7v4C1Nhw2GciRTmhjDtlXvaF3Ku4FuRkg1Aphmz//DFZo4x2f1LZLe6y0K", + "nodeAesCbc": "UE5FRAFBQ1JIEIkeDHiXxJcxo2h6VE3WhLOtO93r6nHMywtrVA9MNSan6OIXvBMq56DpfzEoi4xrIw==", + "nodeLegacy": "3zz2uj4FxeGpNGSS7mcg9NuJbA8tbnHLw0D2y9UlHmjGb9h/TuM0F+4m9nx0lAbW" + }, + { + "cipherKey": "customKey", + "plaintext": "[\"array\",1,false]", + "webAesCbc": "UE5FRAFBQ1JIEPUGEdoGaCsh+bkNRYhOs8W80YUnihgQIfH+mwBu+je9NrFd2BxqESJ8tuaepb6TMA==", + "webLegacy": "sCxbGBMkJDujO6KAhnAmG4gpo2j0ZBTlzNHPAbf7X9QWq3prJIpd4pO8nFoA9IRl", + "nodeAesCbc": "UE5FRAFBQ1JIECk+ffjn3CJow5dhcXEbg7lRJ2rKi8Xczx49FCBCgyZTaF7iP0wXXi6ol0OOKrvm1A==", + "nodeLegacy": "HVCNk5ETt/PkdOjh7WTqtKqYZ+XHl7Ew1s5Sz+I7DHLm8czzFF60cq3xWEoKhWma" + }, + { + "cipherKey": "enigma", + "plaintext": "{\"hi\":\"there\"}", + "webAesCbc": "UE5FRAFBQ1JIEJdrOF88GIyuXpaajjPjcNbCYVUPCC1A7wOzAByn1BCM", + "webLegacy": "N1DmCz4zCp4V62fhaIIMlgkNcdIIwiMFel86p85u1mU=", + "nodeAesCbc": "UE5FRAFBQ1JIEI8yTt3PaltPHtS1EaXw2gt1bCF2lleZWfBAfcKe+SGG", + "nodeLegacy": "oCKQWhRF/MbLBRnxDyrMzx14uVC7EV+dgOsrWiCw82w=" + }, + { + "cipherKey": "enigma", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "webAesCbc": "UE5FRAFBQ1JIEFemBWLrnvBEfbLjrD5V5c9md4+HWQAo5reLHw5X7EMNOtpfCMvM1te+9W5he1GHUzldFCuPjleObhV3liM0NkaH1NPRczD/MLame7FLe5WLPUE+4SWL7Q4raaV3ssw16g==", + "webLegacy": "kp/rwFJHvS9zlBHoZp3JtDyBxUfz//bGWOJXbP8cNr+rp8GZW/gEM6BNmgW+9tDM9ijSPAX2pNRbnQOArlgE97zDTgbY25qG8Wo/AKmQCAoJ3ZYNXibVOeKKCMREOEPl", + "nodeAesCbc": "UE5FRAFBQ1JIEDVjI5PVMqC7ta35JuMoay4O0Xlw1/ijLbNu1WlqOFJbj4C5/CEdmavX93SASSMRWwB/r9FtSnKYziovYFyFcc3PLWsPOWbKyAeMl2LRiIEmfsSZgDX+A3DBdAdSbTv9HA==", + "nodeLegacy": "cledBJLQrs4jZOnG8zQMzYHcA1wWYhZLcZmAwVeiDblLmObdVYI0Zvih0ZFPrRvttKv743mrLBOjAiFR2hNVayLxZt0yBJqlqMqgiS4Az+qnlK8vAvHBFTtLci6fEMVJ" + }, + { + "cipherKey": "enigma", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "webAesCbc": "UE5FRAFBQ1JIEFhFW1OOASsWJd9EYUqvPsCttQbg14FnoA53+7SuaGuZoBnrd2G6v7w+HrsNNbIqudZGgMobgFvnrMOqEhD/3EI=", + "webLegacy": "glaXOlaOfYquZ/izQQ7YNJRN0+AkSLSjD1tItPku1NhxzuR1D+qVu6almDbwc9IhU4Dxf667N95FFdpOpUnxiA==", + "nodeAesCbc": "UE5FRAFBQ1JIELP9ZYo7UwI2+C0Ue5Es9vkzCIcnvvJybWACu9Zn/JTXhNnfmp4/NM4zCOBfUNUSR+iYWPaCccVyutZkMIy4feQ=", + "nodeLegacy": "CXzPHm3R+p2WaF/kJ9E9+tHKsBoIotnWDZe1Zr132tg2OZemhwgxmswYKfv1ZfbMcBTuEkzIxn5N0L9gjtWJWQ==" + }, + { + "cipherKey": "enigma", + "plaintext": "\"\"", + "webAesCbc": "UE5FRAFBQ1JIEIASabCk//Pxn2f93RYrQmqhFKDZWEB6GOf/L1I768Wg", + "webLegacy": "e5DpxyvDdrD+Om2SylwZ8HTTrgpDCX5vQCdIMawpxfY=", + "nodeAesCbc": "UE5FRAFBQ1JIEDx7/QBNVo1e89A9cmvcBv4WV6j75S//CXDBGplBzub/", + "nodeLegacy": "mFlziaaEza6A4dEjkoPaj2sH824OHX97Fw360q8LHhA=" + }, + { + "cipherKey": "enigma", + "plaintext": "42", + "webAesCbc": "UE5FRAFBQ1JIEH+6+zv+RloKbPtzdK2syqWJrvs/5vu6kUYD3xZEAvCQ", + "webLegacy": "WMj5+/+5etWhSuAKNI9U1v1wFa/+dEnlvpC0/0CSWRY=", + "nodeAesCbc": "UE5FRAFBQ1JIEBDlUeRY1LylAGmNBTysiM6BaTuLpCtYF85uHVnwsm0k", + "nodeLegacy": "FqJ663AEjQx6gE49x2imepe/eBgnIpB90DJwnTggelM=" + }, + { + "cipherKey": "enigma", + "plaintext": "3.14159", + "webAesCbc": "UE5FRAFBQ1JIENQNhwak5EO6xc40GivrvsBxeyOB6h60U/j5GG6OWzlh", + "webLegacy": "jfFauxUM73ZI+VKMnRRS0aslL5owTrlzZDcMV08+TIg=", + "nodeAesCbc": "UE5FRAFBQ1JIEKzbR6fUc23JcRf61nRAW6UA+kKtJaA3h+TkmdrkwkO4", + "nodeLegacy": "31/XTZD3lt3hKmBkCainiiaEA1HwrTQvs8rGp0CvdqE=" + }, + { + "cipherKey": "enigma", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "webAesCbc": "UE5FRAFBQ1JIEE6O2VL+NVYJmO7M6QNJIzcLAfa3L1CFq6j+W5/D9QZYfoQC/6Hm0PRaJl2/sc21eg==", + "webLegacy": "Or9JJ01bZeHyvaxAvg34Uwoat1b+GM80Cow6BNEYCVC/F+EjpYb17Khjc/XKTqg8", + "nodeAesCbc": "UE5FRAFBQ1JIECsoIT6eD3wEr8eJjRJQ4pFwVFIpSoYPSUJwRfzXdFrAV2OKE6HG5liX/sVFwjGQOg==", + "nodeLegacy": "DKQQhcjqZvWvidnWSXfsNsbeeBXgYxhQtnrhDDBEJ11WPASB7ogRJGlQGXrYlaNh" + }, + { + "cipherKey": "enigma", + "plaintext": "[\"array\",1,false]", + "webAesCbc": "UE5FRAFBQ1JIEEyV7+n9mIpmsPPxLLT9MitqTjOQTGYXURSw4+b6NwuRcG6tuk8YXZwe5ZUXXLcGdA==", + "webLegacy": "qU0CtIyFLU4qQLKRQF0kwhad7x1uVDxPI0eIlPG9o0wmY3eZ3gcjtSM1gFryhg3s", + "nodeAesCbc": "UE5FRAFBQ1JIEF7zgX/FewHUoxEWEc4w2SSFGNTOCgmypt+tk4nUPRhITCC+JoS6JlrxmqK8zXAUcw==", + "nodeLegacy": "Mj5lyP4xIyg2Bo9UKrV1ans7pJrFU6EKhC7XOgeGKVa3JcISvOepqkJqzNKUF45Q" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "{\"hi\":\"there\"}", + "webAesCbc": "UE5FRAFBQ1JIEF76MebYUeHde0lXReU9hHMH6IcpAvTVUIoOsZx+2wpE", + "webLegacy": "lOlH6mft1GyQ6caDoCcjLYeZnulRAvLkYW2IteuoA6A=", + "nodeAesCbc": "UE5FRAFBQ1JIEPEFT1M66T+djWpXj8BnJHRfCwWe6HztK8q4An5fLJqz", + "nodeLegacy": "qNPtS+7VWJO8inLbw7v7MaUNYxH1GR6yazqYjXDAqko=" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "webAesCbc": "UE5FRAFBQ1JIEFF3EN0gII9fj+3UKg7Kx4FRZyY+ACySL+xk2XNx74UzsIB9Z6kEBE7Y5idc8K9hsBTm7DAH+62jOUfC9Zx2W2kNb1y0G20FW6U8HH0c/JHbdHj2J22pWi5Op0XpugTGew==", + "webLegacy": "VXgYj5qfLVRyLdZpa4dkl8duzDg0VcKxe5e8U/WmSr/npRTWOJSJMUIL54PcjLFAoODH4hnFB4mgGpIjD+Gd2s51QYddzGcB8tdouC/5Sw2CxVY6/3qHkLXsZRXIScVU", + "nodeAesCbc": "UE5FRAFBQ1JIEPwkqu0BbvGyu/RVpsg6az2y3IJrdtOEGnFWmDY1AwjT0NuSXcyXyqpcs5AoytDhS+NZnct9gFcqNV7VGAPbPMMhDPv1BdXur33vSKVpEg/tHLv8EA/VP/0FzuhAIp9D/w==", + "nodeLegacy": "CmPnnfjnoNMmyOuwmrsRGXmuaLJ8tin+OFP1tZrQSnPnZ+vJguOqZbE1T9tUmJp/22uVMcykMr+2EE2fAzCDoAVKb+wRcTceY0owNosZBwBEekGGfD/FuTC+ON0SiIgI" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "webAesCbc": "UE5FRAFBQ1JIEIiQe3ghPsV5kDJtwLHJFQakyZgsLIrKiRrDBEp06/LwCbsVDzDv0XpPE9REoDrmCV0399K6KqVUNpfvrLi1C0M=", + "webLegacy": "bH89TY1bVZPqvGRrac/2aAobhTWxSowmbaHRua7xVkeTcgJD8ytyiz093TG/XtY6pbiUdtrawom4QCmzx7gHaQ==", + "nodeAesCbc": "UE5FRAFBQ1JIEKYG9VJ0qcN54afeeCVtUNdY99ldbIKFMF8JEnCb6dc7ryQ/LIjlUACKkQLYB7I2dOfZwMIO2+CJS1eEWip2ebk=", + "nodeLegacy": "kh7IZa/VdvCmshztNvPkgL1ovZB8+19GVQncK2+uk/1E+YEVe5mdQTHK2RKkjsYBiFYU0nu642vdPzFDxMQwgQ==" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "\"\"", + "webAesCbc": "UE5FRAFBQ1JIENPH1Xhb1XiMwQ8qRBRGpAxyQyCvlW3YndBud8Wd7L6M", + "webLegacy": "AuNfXeg/ZSKVa++fCAUJtDwrl5NZ+gATJMJ6pwoFkPU=", + "nodeAesCbc": "UE5FRAFBQ1JIEFJoBUVLzF6Z7VvKac3PaNaGgRLICLg9iMA13rxgC6mG", + "nodeLegacy": "fsaMB4SHYeuSiR7YLxczNmzcF5uz6h2e+g56kcxYi0o=" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "42", + "webAesCbc": "UE5FRAFBQ1JIEKhX5tqkQSucR/c+/oGSEya2WNnc2PTFpaLK9QDHtLTl", + "webLegacy": "wc1jwKSnDGrIO1m4Xm2GqS7wCs8ARV8wilYRGVxkpqs=", + "nodeAesCbc": "UE5FRAFBQ1JIEIsvHblMaTgXfUbvLLScBxXP92BjH2PipN374u6opyD8", + "nodeLegacy": "JtGJJaZT5uPy14MXlGyvb7zsyQSlajuuruioehVWezI=" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "3.14159", + "webAesCbc": "UE5FRAFBQ1JIENz5aLdiZmtMetnTMFbfhuj6dLpF2rMUoXfpV/56xtYv", + "webLegacy": "KhhFspPkUhYIT8hD0lrzO3+Y3uEpec0apEveQ/Bh7rc=", + "nodeAesCbc": "UE5FRAFBQ1JIECgcNQLu5bildnxxp1yxZ6JTyJYSeaJp9orsNzHpycFh", + "nodeLegacy": "hkIFcIQO9l9QMulrPCsE4MwH2CdhWFda32vhVjV1mJI=" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "webAesCbc": "UE5FRAFBQ1JIEHa6ksP+pGjfnCA55Z6uKevjRiNV1+gaf6jtqjAKEJr7QQTvB26pFwe42H+fJuHrCw==", + "webLegacy": "Kq/xxR/leTHPblfIHnwA4KNJ/Uthd3zleXkA9cOjsp+oxm98z0+PEJ9fRhSj1/UP", + "nodeAesCbc": "UE5FRAFBQ1JIEE7N7nCdgg/H3c7yHEO90JaLSKIC/Nh5EoEUKk59txGeipWG+aj5YzYLGcbYwXrdHg==", + "nodeLegacy": "oJ8U2gGuoQCEQFe5x6RfZd+UK+I163y7qVl9QpQU8fSQccww5Je2JTneml0VIjgX" + }, + { + "cipherKey": "ΠΊΠ»ΡŽΡ‡-πŸ”‘-κλΡιδί", + "plaintext": "[\"array\",1,false]", + "webAesCbc": "UE5FRAFBQ1JIED2j+Hy/SFmT2vKiz5p0s5abB+a//IekRxoskRnsu1Kijv4lfswDIV+9RFpTfmWHCg==", + "webLegacy": "lwJVp4v/oFYCJl4VnYYfsAWPdkLyszT4F+939YrN7B4vCZ30ZSr9TnmUf2oi08dB", + "nodeAesCbc": "UE5FRAFBQ1JIECB9tO8sprwADt4lD5lQJSc1/nuGEpzma9lzU5rP++U0ldBmjBERaTA+1OVEINyC3A==", + "nodeLegacy": "8Rl06u+HrMebwPzHK9sbAeG149ukvCKsaMvr5Qy5fVy4S5pgCrsz6N51QGiDMsZ2" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "{\"hi\":\"there\"}", + "webAesCbc": "UE5FRAFBQ1JIEHF4F41ca0wZxWOLmSz9GhD4rMceJBjBdOzpNtPRrDXV", + "webLegacy": "X9Nf7/AR2pr+wV1gOJQztHEoHJJdVMPyxKHNcQTS0XU=", + "nodeAesCbc": "UE5FRAFBQ1JIEDRcEcn9jj0I3s6cHm2DrdS1arauDkYYpMPCvq6lRV1R", + "nodeLegacy": "Bs/YabnlC+Y2ma8GspSvcoWiC/7lGHAs4agjugULe64=" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "{\"nested\":{\"list\":[1,2,3,{\"deep\":true}],\"text\":\"value\",\"n\":null}}", + "webAesCbc": "UE5FRAFBQ1JIEGzgeO/Yk3LA0XbT15mUGeSzXjW1ydNpgxXTHsMXxi9r0RWgpPtlAJjGVKZyxE3GSfl/lzsHo0kCNFqNfxvymZU4ZFK9X8E+5x1y/PuMAn4smsQtsYjqPQ17BWeZgKdx7w==", + "webLegacy": "r23OAaJsV96oGMlp6RWS2ETSUDKPZHjtclipThrCx935U0Yj89Vd1Qzd9kzsGCYm6VWQxiMNGpWLT17IGoR4G89AYEcZpjHnytYaO8/+v+LAsZN3DIm4HjnCX1z+xGs4", + "nodeAesCbc": "UE5FRAFBQ1JIECWULal0f8TyQgjgTZdJRUZCMKaXalVQl2KX0Rv38aJOhM5VH++RY37qvTPf1hOB7N80CemIiDWVV6ZfAmxeLk+cahCzJvaBwgMq6m5miwhSbdy+M9evBrcE+2XzAyJpYQ==", + "nodeLegacy": "/l6/hPjawEaEON0MXdwoWuLTaUdDhmKXk3XrlB0BZREEBJzODRlKuWHiYRGL2VuSZkYaBoQHXGY2H9CC52HK6Q+GRmgi9VkaizTwJ4OlPa1gGGIWjThVb32hHrUNTmAq" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "\"Hello, δΈ–η•Œ! πŸŒπŸš€ ñÑéíóú\"", + "webAesCbc": "UE5FRAFBQ1JIEH0wCGhLgClQUZl3JkT3FdKYHkx+4O9GjN4J1BjteZEn6NJYQqhQuKFE0Hqyt5yzmhHTqqMVInUIemEb/mR4Lqs=", + "webLegacy": "dPNvAJycFCvUinUWCEgGIX19WiTZ+iwY6jn2ckGFp4F/rnS39yAxHPLp9LcjGuYrLyinLvlen6kObyNtFKNkdQ==", + "nodeAesCbc": "UE5FRAFBQ1JIEO5bDRUbLqkQkcmnsuiFpXnHw/ETXJIPv6U1XPDambX32FtK2vYdv2gHS80DCUpjaVbqSQocZDG4q4R2PdtXd7w=", + "nodeLegacy": "el57lCPvlOPSbjEuj5aLMW6lLLJ7MO83qmRO4JH5JrY3s7L53ktpF3sUC2K/PXL54E7u6GPDL0H1A29vWkriUQ==" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "\"\"", + "webAesCbc": "UE5FRAFBQ1JIEFX+Mc33HzY3G1RuFA6tOGq8UrnvwISCYGVPzDSrvCvl", + "webLegacy": "cIY/QYKD09Uom6csnE4f6zNIHKJ2+9XdO1j1M8XMFzg=", + "nodeAesCbc": "UE5FRAFBQ1JIEN2A0gJCTF2bAS+fpvyrnW1K7cvEaVsTEnGnGr588mqm", + "nodeLegacy": "me5ckhgZRDPkKuCU1v4LGZrX1xxqKn55KRHNAKBbzGs=" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "42", + "webAesCbc": "UE5FRAFBQ1JIELe6tbWQLlplPkjpD0OQKegmIs2BsyCeHW2h8P7TKrCf", + "webLegacy": "C9Xas/lXwV1H4r0kNMfpMrX8YAXWcZmLuT4XLP13d48=", + "nodeAesCbc": "UE5FRAFBQ1JIEMOurmwaX6ECvPK4fasALC5vIQg4itJEALS0bl0FrL22", + "nodeLegacy": "Fi7RcBK5r+KYcG0HvaBZcOA2kjzxqcxORI5j0WzKSc0=" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "3.14159", + "webAesCbc": "UE5FRAFBQ1JIEAIbQGmnlAGphJAP7g2eE+3fUjaGd5xCSmsgVrIl9V9m", + "webLegacy": "ZZIExUdW6I5T2yzUwTHyiWPWf7GAc9U42h2H7vjzspc=", + "nodeAesCbc": "UE5FRAFBQ1JIEGzDiAgcs7VypNGhmFuSpJ0XtgfY+1pZhO1bBbbtMxD0", + "nodeLegacy": "o8LOlKL1pIBAedOC9RQCUeaKMSY90rL0fP1/32c4ng8=" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "\"with\\u0000null\\u0000bytes\"", + "webAesCbc": "UE5FRAFBQ1JIEPzQBSgLJoZRLEGXedgiHssHeKIXbkxvGBde07ekf5/S8SVgZnOtax86tUfAXmh3UQ==", + "webLegacy": "Ro2FQ12J0GzJau2lYT5v9Fk1vRNfTf8nu8b52mKafSC021QFUgAfvzUKs7nNeYpZ", + "nodeAesCbc": "UE5FRAFBQ1JIEL2kpynBld2dhJJK9ecpsUBykegs+tnMDk96xPYuuOeLRzn62qv5PnNms2uAJkaXWw==", + "nodeLegacy": "gfACK8np1o3XF0cZHRbJX3CFk4jU0oKRQgSvgPKi3IJFW2jjY5EWex1PtZ1Ny8mH" + }, + { + "cipherKey": "kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk012345678901234567890123456789012345678901234567890123456789!@#", + "plaintext": "[\"array\",1,false]", + "webAesCbc": "UE5FRAFBQ1JIEPuuwonpNsPCMeA497mX8RgV3c5MzD5Mdpm7jflF4zEMSEjvmL7uOOOV8SmI5ZP8cQ==", + "webLegacy": "Y3SFBzM7rrdnWqVQRXb4FMbeql22eHxINNj3d2lRlnLBHBzmNmDQoXpgTvSB87tb", + "nodeAesCbc": "UE5FRAFBQ1JIEPAqeOK52IsH24IMzoXIAoEyeRWfpMgEL24lSM6JgJdRtDfDEjujGRbx+TrOeYdcPw==", + "nodeLegacy": "bLk/HMllWXPaYbj7byPFvI/2ZnK92LBznE85PmCmbRNuHDwC2SfIRjlgjEeOG++W" + } + ], + "hmac": [ + { + "secretKey": "sec-c-test-only-not-a-real-key", + "input": "GET\npub-c-key\n/publish/pub-c-key/sub-c-key/0/channel/0/%22hello%22\nauth=token&pnsdk=PubNub-JS-Nodejs%2F13.1.3×tamp=1700000000&uuid=user\n", + "signature": "cxzq32o2yAJzeMaA2MrcvFGPWlP4iG/ZcssBajXHUCw=" + }, + { + "secretKey": "sec-c-test-only-not-a-real-key", + "input": "POST\npub-c-key\n/v3/pam/sub-c-key/grant\npnsdk=PubNub-JS-Nodejs%2F13.1.3×tamp=1700000000&uuid=user\n{\"ttl\":15,\"permissions\":{\"resources\":{\"channels\":{\"ch-ΓΌnΓ―code\":1}}}}", + "signature": "7m6PHUunm/tDSRWfn76eFjOxp0L61zSBRFmOKlvKxUE=" + }, + { + "secretKey": "sΓ«cret-πŸ”", + "input": "GET\npub-c-key\n/publish/pub-c-key/sub-c-key/0/channel/0/%22hello%22\nauth=token&pnsdk=PubNub-JS-Nodejs%2F13.1.3×tamp=1700000000&uuid=user\n", + "signature": "1HwVhygl/KfT8Ig3ilZ6Qa+XwhFjpub3Wo8/o9lDiNw=" + }, + { + "secretKey": "sΓ«cret-πŸ”", + "input": "POST\npub-c-key\n/v3/pam/sub-c-key/grant\npnsdk=PubNub-JS-Nodejs%2F13.1.3×tamp=1700000000&uuid=user\n{\"ttl\":15,\"permissions\":{\"resources\":{\"channels\":{\"ch-ΓΌnΓ―code\":1}}}}", + "signature": "K8nI806bFIMJNGIrAin6V5xugAb3wDVeFJ4vGJUDWnk=" + } + ] +} diff --git a/test/fixtures/crypto/legacy-vectors.ts b/test/fixtures/crypto/legacy-vectors.ts new file mode 100644 index 000000000..ea05506ee --- /dev/null +++ b/test/fixtures/crypto/legacy-vectors.ts @@ -0,0 +1,28 @@ +/** + * Typed access to CryptoJS golden vectors (see `generate-legacy-vectors.ts`). + */ + +import { readFileSync } from 'fs'; +import { join } from 'path'; + +export type LegacyVector = { cipherKey: string; plaintext: string; staticIV: string; randomIV: string[] }; + +export type CryptoModuleVector = { + cipherKey: string; + plaintext: string; + webAesCbc: string; + webLegacy: string; + nodeAesCbc: string; + nodeLegacy: string; +}; + +export type HmacVector = { secretKey: string; input: string; signature: string }; + +const vectors: { + generatedWith: string; + legacy: LegacyVector[]; + cryptoModule: CryptoModuleVector[]; + hmac: HmacVector[]; +} = JSON.parse(readFileSync(join(__dirname, 'legacy-vectors.json'), 'utf8')); + +export default vectors; diff --git a/test/integration/components/crypto.test.ts b/test/integration/components/crypto.test.ts index e13f77973..6c8c8c9a3 100644 --- a/test/integration/components/crypto.test.ts +++ b/test/integration/components/crypto.test.ts @@ -13,7 +13,7 @@ describe('components/crypto useRandomIVs', () => { uuid: 'myUUID', }); - it('should be able to encrypt and decrypt a message', () => { + it('should be able to encrypt and decrypt a message', async () => { const data = { message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et ' + @@ -23,14 +23,14 @@ describe('components/crypto useRandomIVs', () => { 'mollit anim id est laborum.', }; const plaintext = JSON.stringify(data); - const ciphertext = pubnub.encrypt(plaintext); + const ciphertext = await pubnub.encrypt(plaintext); - const decrypted = pubnub.decrypt(ciphertext); + const decrypted = await pubnub.decrypt(ciphertext); expect(decrypted).to.deep.equal(data, JSON.stringify(decrypted)); }); - it('should be able to encrypt and decrypt a message with ICryptoModule', () => { + it('should be able to encrypt and decrypt a message with ICryptoModule', async () => { const pubnub = new PubNub({ subscribeKey: 'demo-36', publishKey: 'demo-36', @@ -49,9 +49,9 @@ describe('components/crypto useRandomIVs', () => { 'mollit anim id est laborum.', }; const plaintext = JSON.stringify(data); - const ciphertext = pubnub.encrypt(plaintext); + const ciphertext = await pubnub.encrypt(plaintext); - const decrypted = pubnub.decrypt(ciphertext); + const decrypted = await pubnub.decrypt(ciphertext); expect(decrypted).to.deep.equal(data); }); diff --git a/test/integration/components/legacyCryptoModule.test.ts b/test/integration/components/legacyCryptoModule.test.ts index 6aa3312dd..725a48d78 100644 --- a/test/integration/components/legacyCryptoModule.test.ts +++ b/test/integration/components/legacyCryptoModule.test.ts @@ -6,7 +6,25 @@ import { expect } from 'chai'; import { Buffer } from 'buffer'; import LegacyCryptoModule from '../../../src/crypto/modules/LegacyCryptoModule'; +import NobleCryptoPrimitives from '../../../src/crypto/primitives/react-native'; import LegacyCrypto from '../../../src/core/components/cryptography/index'; +import { LoggerManager } from '../../../src/core/components/logger-manager'; + +const primitives = new NobleCryptoPrimitives(); + +/** + * Assert that promise rejects with error which matches provided message. + */ +const expectRejection = async (promise: Promise, message: string | RegExp) => { + try { + await promise; + } catch (error) { + if (typeof message === 'string') expect((error as Error).message).to.include(message); + else expect((error as Error).message).to.match(message); + return; + } + expect.fail('Expected promise to be rejected'); +}; // Mock logger manager for testing class MockLoggerManager { @@ -20,15 +38,18 @@ describe('LegacyCryptoModule Integration Tests', () => { let legacyCrypto: LegacyCrypto; let cryptoModule: LegacyCryptoModule; const testCipherKey = 'test-cipher-key-123'; - const mockLogger = new MockLoggerManager(); + const mockLogger = new MockLoggerManager() as unknown as LoggerManager; beforeEach(() => { // Create legacy crypto instance with test configuration - legacyCrypto = new LegacyCrypto({ - cipherKey: testCipherKey, - useRandomIVs: false, // Use predictable IVs for testing - logger: mockLogger, - }); + legacyCrypto = new LegacyCrypto( + { + cipherKey: testCipherKey, + useRandomIVs: false, // Use predictable IVs for testing + logger: mockLogger, + }, + primitives, + ); // Create crypto module with legacy instance cryptoModule = new LegacyCryptoModule(legacyCrypto); @@ -51,163 +72,162 @@ describe('LegacyCryptoModule Integration Tests', () => { describe('Logger Management', () => { it('should set logger on legacy crypto instance', () => { - const newLogger = new MockLoggerManager(); + const newLogger = new MockLoggerManager() as unknown as LoggerManager; cryptoModule.logger = newLogger; expect(legacyCrypto.logger).to.equal(newLogger); }); }); describe('String Encryption/Decryption', () => { - it('should encrypt and decrypt string data', () => { + it('should encrypt and decrypt string data', async () => { const testData = '"Hello, World!"'; // JSON string - const encrypted = cryptoModule.encrypt(testData); + const encrypted = await cryptoModule.encrypt(testData); expect(encrypted).to.be.a('string'); expect(encrypted).to.not.equal(testData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal('Hello, World!'); // Legacy crypto parses JSON, so we get the unwrapped string }); - it('should handle complex string data', () => { + it('should handle complex string data', async () => { const originalData = { message: 'Complex data with special characters: Γ©Γ±δΈ­ζ–‡πŸš€', numbers: [1, 2.5, -3], boolean: true, - nested: { key: 'value' } + nested: { key: 'value' }, }; const testData = JSON.stringify(originalData); - const encrypted = cryptoModule.encrypt(testData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(testData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.deep.equal(originalData); // Legacy crypto parses JSON }); - it('should handle empty string', () => { + it('should handle empty string', async () => { const testData = '""'; // JSON empty string - const encrypted = cryptoModule.encrypt(testData); + const encrypted = await cryptoModule.encrypt(testData); expect(encrypted).to.be.a('string'); - const decrypted = cryptoModule.decrypt(encrypted as string); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(''); // Parsed JSON result }); - it('should handle long strings', () => { + it('should handle long strings', async () => { const longString = 'x'.repeat(10000); const testData = JSON.stringify(longString); - const encrypted = cryptoModule.encrypt(testData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(testData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(longString); // Parsed JSON result }); }); describe('ArrayBuffer Encryption/Decryption', () => { - it('should encrypt and decrypt ArrayBuffer data', () => { + it('should encrypt and decrypt ArrayBuffer data', async () => { const testString = 'ArrayBuffer test data'; const testData = new TextEncoder().encode(JSON.stringify(testString)).buffer; - const encrypted = cryptoModule.encrypt(testData); + const encrypted = await cryptoModule.encrypt(testData); expect(encrypted).to.be.a('string'); - const decrypted = cryptoModule.decrypt(encrypted as string); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testString); // Legacy crypto parses JSON }); - it('should handle empty ArrayBuffer', () => { + it('should handle empty ArrayBuffer', async () => { const testData = new TextEncoder().encode('""').buffer; // JSON empty string as ArrayBuffer - const encrypted = cryptoModule.encrypt(testData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(testData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(''); // Parsed JSON result }); - it('should handle binary ArrayBuffer data', () => { + it('should handle binary ArrayBuffer data', async () => { const testString = 'binary test'; const testData = new TextEncoder().encode(JSON.stringify(testString)).buffer; - const encrypted = cryptoModule.encrypt(testData); + const encrypted = await cryptoModule.encrypt(testData); expect(encrypted).to.be.a('string'); // When decrypting ArrayBuffer data containing JSON, we get parsed JSON - const decrypted = cryptoModule.decrypt(encrypted as string); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testString); }); }); describe('Decryption with ArrayBuffer input', () => { - it('should decrypt ArrayBuffer input', () => { + it('should decrypt ArrayBuffer input', async () => { const testData = 'Test for ArrayBuffer decryption'; const jsonData = JSON.stringify(testData); // First encrypt as string to get valid encrypted data - const encrypted = cryptoModule.encrypt(jsonData); + const encrypted = await cryptoModule.encrypt(jsonData); // The LegacyCryptoModule expects ArrayBuffer to contain bytes that when converted // to base64 will match the encrypted string. So we need to create an ArrayBuffer // from the base64-decoded bytes of the encrypted string. const encryptedBytes = Buffer.from(encrypted as string, 'base64'); - const encryptedBuffer = encryptedBytes.buffer.slice(encryptedBytes.byteOffset, encryptedBytes.byteOffset + encryptedBytes.byteLength); + const encryptedBuffer = encryptedBytes.buffer.slice( + encryptedBytes.byteOffset, + encryptedBytes.byteOffset + encryptedBytes.byteLength, + ); - const decrypted = cryptoModule.decrypt(encryptedBuffer); + const decrypted = await cryptoModule.decrypt(encryptedBuffer); expect(decrypted).to.equal(testData); // Parsed JSON result }); - it('should handle empty ArrayBuffer for decryption', () => { + it('should handle empty ArrayBuffer for decryption', async () => { const emptyBuffer = new ArrayBuffer(0); - expect(() => cryptoModule.decrypt(emptyBuffer)) - .to.throw('Decryption data cannot be empty ArrayBuffer'); + await expectRejection(cryptoModule.decrypt(emptyBuffer), 'Decryption data cannot be empty ArrayBuffer'); }); }); describe('Error Handling', () => { - it('should throw error for null encryption data', () => { - expect(() => cryptoModule.encrypt(null as any)) - .to.throw('Encryption data cannot be null or undefined'); + it('should throw error for null encryption data', async () => { + await expectRejection(cryptoModule.encrypt(null as any), 'Encryption data cannot be null or undefined'); }); - it('should throw error for undefined encryption data', () => { - expect(() => cryptoModule.encrypt(undefined as any)) - .to.throw('Encryption data cannot be null or undefined'); + it('should throw error for undefined encryption data', async () => { + await expectRejection(cryptoModule.encrypt(undefined as any), 'Encryption data cannot be null or undefined'); }); - it('should throw error for null decryption data', () => { - expect(() => cryptoModule.decrypt(null as any)) - .to.throw('Decryption data cannot be null or undefined'); + it('should throw error for null decryption data', async () => { + await expectRejection(cryptoModule.decrypt(null as any), 'Decryption data cannot be null or undefined'); }); - it('should throw error for undefined decryption data', () => { - expect(() => cryptoModule.decrypt(undefined as any)) - .to.throw('Decryption data cannot be null or undefined'); + it('should throw error for undefined decryption data', async () => { + await expectRejection(cryptoModule.decrypt(undefined as any), 'Decryption data cannot be null or undefined'); }); - it('should throw error for empty string decryption', () => { - expect(() => cryptoModule.decrypt('')) - .to.throw('Decryption data cannot be empty string'); + it('should throw error for empty string decryption', async () => { + await expectRejection(cryptoModule.decrypt(''), 'Decryption data cannot be empty string'); }); - it('should throw error for whitespace-only string decryption', () => { - expect(() => cryptoModule.decrypt(' ')) - .to.throw('Decryption data cannot be empty string'); + it('should throw error for whitespace-only string decryption', async () => { + await expectRejection(cryptoModule.decrypt(' '), 'Decryption data cannot be empty string'); }); - it('should handle malformed encrypted data gracefully', () => { + it('should handle malformed encrypted data gracefully', async () => { const malformedData = 'not-valid-encrypted-data'; // Legacy crypto returns null for malformed data, doesn't throw - const result = cryptoModule.decrypt(malformedData); + const result = await cryptoModule.decrypt(malformedData); expect(result).to.be.null; }); - it('should handle encryption failure gracefully', () => { + it('should handle encryption failure gracefully', async () => { // Mock the legacy crypto to throw an error - const failingCrypto = new LegacyCrypto({ - cipherKey: testCipherKey, - useRandomIVs: false, - logger: mockLogger, - }); + const failingCrypto = new LegacyCrypto( + { + cipherKey: testCipherKey, + useRandomIVs: false, + logger: mockLogger, + }, + primitives, + ); // Override encrypt method to throw failingCrypto.encrypt = () => { @@ -216,8 +236,7 @@ describe('LegacyCryptoModule Integration Tests', () => { const failingModule = new LegacyCryptoModule(failingCrypto); - expect(() => failingModule.encrypt(JSON.stringify('test'))) - .to.throw(/Encryption failed: Encryption failed/); + await expectRejection(failingModule.encrypt(JSON.stringify('test')), /Encryption failed: Encryption failed/); }); }); @@ -225,7 +244,7 @@ describe('LegacyCryptoModule Integration Tests', () => { // Mock file interfaces for testing const mockFile = { name: 'test.txt', - content: 'test content' + content: 'test content', } as any; const mockFileConstructor = class {} as any; @@ -242,55 +261,61 @@ describe('LegacyCryptoModule Integration Tests', () => { }); describe('Cross-format Compatibility', () => { - it('should maintain compatibility with different cipher keys', () => { + it('should maintain compatibility with different cipher keys', async () => { const data = 'Compatibility test'; const jsonData = JSON.stringify(data); // Test with different cipher key - const crypto2 = new LegacyCrypto({ - cipherKey: 'different-key', - useRandomIVs: false, - logger: mockLogger, - }); + const crypto2 = new LegacyCrypto( + { + cipherKey: 'different-key', + useRandomIVs: false, + logger: mockLogger, + }, + primitives, + ); const module2 = new LegacyCryptoModule(crypto2); - const encrypted1 = cryptoModule.encrypt(jsonData); - const encrypted2 = module2.encrypt(jsonData); + const encrypted1 = await cryptoModule.encrypt(jsonData); + const encrypted2 = await module2.encrypt(jsonData); // Different keys should produce different encrypted data expect(encrypted1).to.not.equal(encrypted2); // But each should decrypt correctly with its own key - expect(cryptoModule.decrypt(encrypted1 as string)).to.equal(data); - expect(module2.decrypt(encrypted2 as string)).to.equal(data); + expect(await cryptoModule.decrypt(encrypted1 as string)).to.equal(data); + expect(await module2.decrypt(encrypted2 as string)).to.equal(data); }); - it('should handle useRandomIVs setting correctly', () => { + it('should handle useRandomIVs setting correctly', async () => { const data = 'Random IV test'; const jsonData = JSON.stringify(data); // Test with random IVs enabled - const cryptoRandom = new LegacyCrypto({ - cipherKey: testCipherKey, - useRandomIVs: true, - logger: mockLogger, - }); + const cryptoRandom = new LegacyCrypto( + { + cipherKey: testCipherKey, + useRandomIVs: true, + logger: mockLogger, + }, + primitives, + ); const moduleRandom = new LegacyCryptoModule(cryptoRandom); - const encrypted1 = moduleRandom.encrypt(jsonData); - const encrypted2 = moduleRandom.encrypt(jsonData); + const encrypted1 = await moduleRandom.encrypt(jsonData); + const encrypted2 = await moduleRandom.encrypt(jsonData); // With random IVs, same data should produce different encrypted output expect(encrypted1).to.not.equal(encrypted2); // But both should decrypt to original data - expect(moduleRandom.decrypt(encrypted1 as string)).to.equal(data); - expect(moduleRandom.decrypt(encrypted2 as string)).to.equal(data); + expect(await moduleRandom.decrypt(encrypted1 as string)).to.equal(data); + expect(await moduleRandom.decrypt(encrypted2 as string)).to.equal(data); }); }); describe('React Native Environment Simulation', () => { - it('should work with polyfilled Buffer', () => { + it('should work with polyfilled Buffer', async () => { // Simulate React Native environment where Buffer might be polyfilled const originalBuffer = global.Buffer; @@ -300,8 +325,8 @@ describe('LegacyCryptoModule Integration Tests', () => { const testData = 'Buffer polyfill test'; const jsonData = JSON.stringify(testData); - const encrypted = cryptoModule.encrypt(jsonData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(jsonData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testData); } finally { @@ -310,51 +335,51 @@ describe('LegacyCryptoModule Integration Tests', () => { } }); - it('should handle UTF-8 encoding correctly in RN environment', () => { + it('should handle UTF-8 encoding correctly in RN environment', async () => { const testData = 'Unicode test: Γ©Γ±δΈ­ζ–‡πŸš€ emoji and special chars'; const jsonData = JSON.stringify(testData); - const encrypted = cryptoModule.encrypt(jsonData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(jsonData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testData); }); - it('should work with TextEncoder/TextDecoder polyfills', () => { + it('should work with TextEncoder/TextDecoder polyfills', async () => { // Test with polyfilled TextEncoder/TextDecoder (common in RN) const testString = 'TextEncoder/Decoder test'; const jsonString = JSON.stringify(testString); const encoder = new TextEncoder(); const testData = encoder.encode(jsonString).buffer; - const encrypted = cryptoModule.encrypt(testData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(testData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testString); }); }); describe('Performance and Memory', () => { - it('should handle multiple rapid encrypt/decrypt operations', () => { + it('should handle multiple rapid encrypt/decrypt operations', async () => { const testData = 'Performance test data'; const iterations = 100; for (let i = 0; i < iterations; i++) { const jsonData = JSON.stringify(`${testData} ${i}`); - const encrypted = cryptoModule.encrypt(jsonData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(jsonData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(`${testData} ${i}`); } }); - it('should handle large data efficiently', () => { + it('should handle large data efficiently', async () => { // Test with large data (smaller for JSON overhead) const largeData = 'x'.repeat(100000); const jsonData = JSON.stringify(largeData); const startTime = Date.now(); - const encrypted = cryptoModule.encrypt(jsonData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(jsonData); + const decrypted = await cryptoModule.decrypt(encrypted as string); const endTime = Date.now(); expect(decrypted).to.equal(largeData); @@ -363,34 +388,34 @@ describe('LegacyCryptoModule Integration Tests', () => { }); describe('Edge Cases', () => { - it('should handle data with null bytes', () => { + it('should handle data with null bytes', async () => { const testData = 'Data\0with\0null\0bytes'; const jsonData = JSON.stringify(testData); - const encrypted = cryptoModule.encrypt(jsonData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(jsonData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testData); }); - it('should handle JSON-like strings', () => { + it('should handle JSON-like strings', async () => { const testData = '{"key": "value", "nested": {"array": [1, 2, 3]}}'; const jsonData = JSON.stringify(testData); // Double-encode as JSON - const encrypted = cryptoModule.encrypt(jsonData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(jsonData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testData); // Should get back the JSON string }); - it('should handle base64-like strings', () => { + it('should handle base64-like strings', async () => { const testData = 'SGVsbG8gV29ybGQh'; // "Hello World!" in base64 const jsonData = JSON.stringify(testData); - const encrypted = cryptoModule.encrypt(jsonData); - const decrypted = cryptoModule.decrypt(encrypted as string); + const encrypted = await cryptoModule.encrypt(jsonData); + const decrypted = await cryptoModule.decrypt(encrypted as string); expect(decrypted).to.equal(testData); }); }); -}); \ No newline at end of file +}); diff --git a/test/integration/endpoints/crypto_async.test.ts b/test/integration/endpoints/crypto_async.test.ts new file mode 100644 index 000000000..b4480e4e4 --- /dev/null +++ b/test/integration/endpoints/crypto_async.test.ts @@ -0,0 +1,437 @@ +/* global describe, beforeEach, afterEach, it, before, after */ + +import * as zlib from 'zlib'; +import assert from 'assert'; +import nock from 'nock'; + +import { ICryptoModule } from '../../../src/core/interfaces/crypto-module'; +import LegacyCrypto from '../../../src/core/components/cryptography/index'; +import NodeCryptoPrimitives from '../../../src/crypto/primitives/node'; +import PubNub from '../../../src/node/index'; +import vectors from '../../fixtures/crypto/legacy-vectors'; +import utils from '../../utils'; + +/** + * AES-CBC payload encrypted with `customKey` (deterministically fails to decrypt with `enigma` key). + */ +const wrongKeyPayload = vectors.cryptoModule[0].webAesCbc; + +/** + * Wrap crypto module so decryption of earlier payloads completes later (verifies that asynchronous decryption + * preserves original message order). + */ +const withReversedDecryptionTiming = (module: ICryptoModule): ICryptoModule => { + let calls = 0; + + return { + set logger(_logger: never) {}, + encrypt: (data) => module.encrypt(data), + decrypt: async (data) => { + const delay = Math.max(0, 40 - calls++ * 10); + await new Promise((resolve) => setTimeout(resolve, delay)); + return module.decrypt(data); + }, + encryptFile: (file, File) => module.encryptFile(file, File), + decryptFile: (file, File) => module.decryptFile(file, File), + }; +}; + +/** + * Legacy (static IV) encryption used to compute expected request paths. + */ +const legacyEncrypt = (cipherKey: string, data: string) => + new LegacyCrypto({ cipherKey, useRandomIVs: false }, new NodeCryptoPrimitives()).encrypt(data); + +describe('asynchronous crypto in customer-facing APIs (non-breaking)', () => { + let pubnub: PubNub; + + before(() => { + nock.disableNetConnect(); + }); + + after(() => { + nock.enableNetConnect(); + }); + + beforeEach(() => { + nock.cleanAll(); + pubnub = new PubNub({ + subscribeKey: 'mySubKey', + publishKey: 'myPublishKey', + uuid: 'myUUID', + cipherKey: 'customKey', + useRandomIVs: false, + enableEventEngine: false, + // @ts-expect-error Force override default value. + useRequestId: false, + }); + }); + + afterEach(() => { + pubnub.destroy(true); + }); + + describe('publish', () => { + const encryptedPath = '/publish/myPublishKey/mySubKey/0/ch1/0/%22TejX6F2JNqH%2FgIiGHWN4Cw%3D%3D%22'; + + it('sends byte-identical encrypted payload (Promise API)', async () => { + const scope = utils + .createNock() + .get(encryptedPath) + .query(true) + .reply(200, '[1,"Sent","14647523059145592"]', { 'content-type': 'text/javascript' }); + + const response = await pubnub.publish({ channel: 'ch1', message: { hi: 'there' } }); + + assert.equal(response.timetoken, '14647523059145592'); + assert.equal(scope.isDone(), true); + }); + + it('sends byte-identical encrypted payload (callback API)', (done) => { + const scope = utils + .createNock() + .get(encryptedPath) + .query(true) + .reply(200, '[1,"Sent","14647523059145592"]', { 'content-type': 'text/javascript' }); + + pubnub.publish({ channel: 'ch1', message: { hi: 'there' } }, (status, response) => { + try { + assert.equal(status.error, false); + assert.equal(response?.timetoken, '14647523059145592'); + assert.equal(scope.isDone(), true); + done(); + } catch (error) { + done(error); + } + }); + }); + + it('sends encrypted payload in POST body', async () => { + let body: string | undefined; + const scope = utils + .createNock() + .post('/publish/myPublishKey/mySubKey/0/ch1/0', (requestBody) => { + // Body is deflate-compressed by the SDK (compressible request). + body = zlib.inflateSync(Buffer.from(requestBody as string, 'hex')).toString('utf8'); + return true; + }) + .query(true) + .reply(200, '[1,"Sent","14647523059145592"]', { 'content-type': 'text/javascript' }); + + await pubnub.publish({ channel: 'ch1', message: { hi: 'there' }, sendByPost: true }); + + assert.equal(scope.isDone(), true); + assert.equal(body, '"TejX6F2JNqH/gIiGHWN4Cw=="'); + }); + + it('fire() uses the same encryption path', async () => { + const scope = utils + .createNock() + .get(encryptedPath) + .query((query) => query.store === '0' && query.norep === 'true') + .reply(200, '[1,"Sent","14647523059145592"]', { 'content-type': 'text/javascript' }); + + await pubnub.fire({ channel: 'ch1', message: { hi: 'there' } }); + + assert.equal(scope.isDone(), true); + }); + + const failingClient = () => + new PubNub({ + subscribeKey: 'mySubKey', + publishKey: 'myPublishKey', + uuid: 'myUUID', + logLevel: PubNub.LogLevel.None, + cryptoModule: { + set logger(_logger: never) {}, + encrypt: async () => { + throw new Error('Encryption error: boom'); + }, + } as unknown as ICryptoModule, + }); + + it('rejects with PubNubError status when encryption fails and does not send request', async () => { + const failing = failingClient(); + const scope = utils.createNock().get(/\/publish\/.*/).query(true).reply(200, '[1,"Sent","1"]'); + + try { + await failing.publish({ channel: 'ch1', message: { hi: 'there' } }); + assert.fail('Expected publish to be rejected'); + } catch (error) { + const { status } = error as { status?: { error: boolean; operation: string; errorData?: Error } }; + assert.equal(status?.error, true); + assert.equal(status?.operation, 'PNPublishOperation'); + assert.match(String(status?.errorData?.message ?? error), /Encryption error: boom/); + } finally { + assert.equal(scope.isDone(), false); + failing.destroy(true); + } + }); + + it('notifies callback with error status when encryption fails', (done) => { + const failing = failingClient(); + + failing.publish({ channel: 'ch1', message: { hi: 'there' } }, (status, response) => { + try { + assert.equal(status.error, true); + assert.equal(status.operation, 'PNPublishOperation'); + assert.equal(response, null); + done(); + } catch (error) { + done(error); + } finally { + failing.destroy(true); + } + }); + }); + + it('schedules encrypted publishes in call order even if encryption completes out of order', async () => { + const cryptoModule = PubNub.CryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + let call = 0; + const client = new PubNub({ + subscribeKey: 'mySubKey', + publishKey: 'myPublishKey', + uuid: 'myUUID', + cryptoModule: { + set logger(_logger: never) {}, + // First publish finishes encryption last. + encrypt: async (data: ArrayBuffer | string) => { + const delay = call++ === 0 ? 40 : 0; + await new Promise((resolve) => setTimeout(resolve, delay)); + return cryptoModule.encrypt(data); + }, + } as unknown as ICryptoModule, + }); + const received: string[] = []; + const scope = utils + .createNock() + .get((uri) => { + if (!uri.startsWith('/publish/myPublishKey/mySubKey/0/ch1/0/')) return false; + received.push(decodeURIComponent(uri.split('?')[0].split('/').pop()!)); + return true; + }) + .times(3) + .query(true) + .reply(200, '[1,"Sent","1"]', { 'content-type': 'text/javascript' }); + + await Promise.all([ + client.publish({ channel: 'ch1', message: 'A' }), + client.publish({ channel: 'ch1', message: 'B' }), + client.publish({ channel: 'ch1', message: 'C' }), + ]); + + const messages = await Promise.all(received.map((payload) => cryptoModule.decrypt(JSON.parse(payload)))); + assert.equal(scope.isDone(), true); + assert.deepEqual( + messages.map((message) => JSON.parse(new TextDecoder().decode(message as ArrayBuffer))), + ['A', 'B', 'C'], + ); + client.destroy(true); + }); + }); + + describe('publishFile', () => { + it('sends byte-identical encrypted file message', async () => { + const plaintext = JSON.stringify({ file: { name: 'cat.jpg', id: 'file-id' }, message: { hi: 'there' } }); + const encrypted = encodeURIComponent(JSON.stringify(await legacyEncrypt('customKey', plaintext))); + const scope = utils + .createNock() + .get(`/v1/files/publish-file/myPublishKey/mySubKey/0/ch1/0/${encrypted}`) + .query(true) + .reply(200, '[1,"Sent","14647523059145592"]', { 'content-type': 'text/javascript' }); + + const response = await pubnub.publishFile({ + channel: 'ch1', + fileId: 'file-id', + fileName: 'cat.jpg', + message: { hi: 'there' }, + }); + + assert.equal(response.timetoken, '14647523059145592'); + assert.equal(scope.isDone(), true); + }); + }); + + describe('subscribe', () => { + it('delivers decrypted messages in original order with decryption errors', (done) => { + const cryptoModule = PubNub.CryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + + Promise.all([ + cryptoModule.encrypt(JSON.stringify({ order: 1 })), + cryptoModule.encrypt(JSON.stringify({ order: 4 })), + cryptoModule.encrypt(JSON.stringify({ message: 'file', file: { id: 'file-id', name: 'cat.jpg' } })), + ]).then(([first, fourth, file]) => { + const b64 = (data: ArrayBuffer | string) => + typeof data === 'string' ? data : Buffer.from(data).toString('base64'); + const envelope = (data: unknown, timetoken: string, type?: number) => ({ + a: '4', + f: 0, + i: 'client1', + p: { t: timetoken, r: 1 }, + k: 'mySubKey', + c: 'ch1', + d: data, + ...(type !== undefined ? { e: type } : {}), + }); + const client = new PubNub({ + subscribeKey: 'mySubKey', + publishKey: 'myPublishKey', + uuid: 'myUUID', + enableEventEngine: false, + // @ts-expect-error Force override default value. + useRequestId: false, + cryptoModule: withReversedDecryptionTiming(cryptoModule), + }); + const scope = utils + .createNock() + .get('/v2/subscribe/mySubKey/ch1/0') + .query(true) + .reply( + 200, + JSON.stringify({ + t: { t: '3', r: 1 }, + m: [ + envelope(b64(first), '1'), + envelope({ order: 2 }, '2'), + envelope(wrongKeyPayload, '3'), + envelope(b64(file), '35', 4), + envelope(b64(fourth), '4'), + ], + }), + { 'content-type': 'text/javascript' }, + ); + utils.createNock().get('/v2/subscribe/mySubKey/ch1/0').query(true).delay(5000).reply(200, '{}'); + + const received: { timetoken: string; message: unknown; error?: string }[] = []; + const files: { timetoken: string; file?: { id: string } }[] = []; + const finish = () => { + if (received.length !== 4 || files.length !== 1) return; + try { + assert.equal(scope.isDone(), true); + assert.deepEqual( + received.map(({ timetoken }) => timetoken), + ['1', '2', '3', '4'], + ); + assert.deepEqual(received[0].message, { order: 1 }); + assert.deepEqual(received[1].message, { order: 2 }); + assert.equal(received[2].message, wrongKeyPayload); + assert.match(received[2].error!, /^Error while decrypting message content: /); + assert.deepEqual(received[3].message, { order: 4 }); + assert.equal(files[0].file?.id, 'file-id'); + client.destroy(true); + done(); + } catch (error) { + client.destroy(true); + done(error); + } + }; + + client.addListener({ + message: (event) => { + received.push(event); + finish(); + }, + file: (event) => { + files.push(event); + finish(); + }, + }); + client.subscribe({ channels: ['ch1'] }); + }, done); + }); + }); + + describe('history', () => { + it('fetchMessages keeps channel and message order with asynchronous decryption', async () => { + const cryptoModule = PubNub.CryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + const b64 = (data: ArrayBuffer | string) => (typeof data === 'string' ? data : Buffer.from(data).toString('base64')); + const [a1, a3, b1] = await Promise.all([ + cryptoModule.encrypt('{"n":"a1"}'), + cryptoModule.encrypt('{"n":"a3"}'), + cryptoModule.encrypt('{"n":"b1"}'), + ]); + const client = new PubNub({ + subscribeKey: 'mySubKey', + uuid: 'myUUID', + cryptoModule: withReversedDecryptionTiming(cryptoModule), + }); + const scope = utils + .createNock() + .get('/v3/history/sub-key/mySubKey/channel/chA,chB') + .query(true) + .reply( + 200, + JSON.stringify({ + channels: { + chA: [ + { message: b64(a1), timetoken: '1' }, + { message: { n: 'a2' }, timetoken: '2' }, + { message: b64(a3), timetoken: '3' }, + ], + chB: [{ message: b64(b1), timetoken: '4' }], + }, + }), + { 'content-type': 'text/javascript' }, + ); + + const response = await client.fetchMessages({ channels: ['chA', 'chB'] }); + + assert.equal(scope.isDone(), true); + assert.deepEqual(Object.keys(response.channels), ['chA', 'chB']); + assert.deepEqual( + response.channels.chA.map(({ message }) => message), + [{ n: 'a1' }, { n: 'a2' }, { n: 'a3' }], + ); + assert.deepEqual(response.channels.chB[0].message, { n: 'b1' }); + client.destroy(true); + }); + + it('history keeps message order with asynchronous decryption', async () => { + const cryptoModule = PubNub.CryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + const b64 = (data: ArrayBuffer | string) => (typeof data === 'string' ? data : Buffer.from(data).toString('base64')); + const [m1, m3] = await Promise.all([cryptoModule.encrypt('{"n":1}'), cryptoModule.encrypt('{"n":3}')]); + const client = new PubNub({ + subscribeKey: 'mySubKey', + uuid: 'myUUID', + cryptoModule: withReversedDecryptionTiming(cryptoModule), + }); + const scope = utils + .createNock() + .get('/v2/history/sub-key/mySubKey/channel/ch1') + .query(true) + .reply( + 200, + JSON.stringify([ + [ + { message: b64(m1), timetoken: '1' }, + { message: wrongKeyPayload, timetoken: '2' }, + { message: b64(m3), timetoken: '3' }, + ], + '1', + '3', + ]), + { 'content-type': 'text/javascript' }, + ); + + const response = await client.history({ channel: 'ch1' }); + + assert.equal(scope.isDone(), true); + assert.deepEqual( + response.messages.map(({ entry }) => entry), + [{ n: 1 }, wrongKeyPayload, { n: 3 }], + ); + assert.match(response.messages[1].error!, /^Error while decrypting message content: /); + client.destroy(true); + }); + }); + + describe('public encrypt / decrypt (breaking: now asynchronous)', () => { + it('returns Promises with v13-compatible results', async () => { + const encrypted = pubnub.encrypt(JSON.stringify({ hi: 'there' })); + + assert.ok(encrypted instanceof Promise); + assert.equal(await encrypted, 'TejX6F2JNqH/gIiGHWN4Cw=='); + assert.deepEqual(await pubnub.decrypt('TejX6F2JNqH/gIiGHWN4Cw=='), { hi: 'there' }); + }); + }); +}); diff --git a/test/unit/common.test.ts b/test/unit/common.test.ts index 63bd5f567..0218812f3 100644 --- a/test/unit/common.test.ts +++ b/test/unit/common.test.ts @@ -3,7 +3,6 @@ import lilUUID from 'lil-uuid'; import assert from 'assert'; -import CryptoJS from '../../src/core/components/cryptography/hmac-sha256'; import { Payload } from '../../src/core/types/api'; import PubNub from '../../src/node/index'; @@ -61,103 +60,96 @@ describe('#core / mounting point', () => { assert.equal(lilUUID.isUUID(PubNub.generateUUID()), true); }); - it('supports encryption with static IV', () => { + it('supports encryption with static IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', cipherKey: 'customKey', useRandomIVs: false, uuid: 'myUUID', }); - assert.equal(pn.encrypt(JSON.stringify({ hi: 'there' })), 'TejX6F2JNqH/gIiGHWN4Cw=='); + assert.equal(await pn.encrypt(JSON.stringify({ hi: 'there' })), 'TejX6F2JNqH/gIiGHWN4Cw=='); }); - it('supports encryption with random IV', () => { + it('supports encryption with random IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', cipherKey: 'customKey', uuid: 'myUUID', }); - const data1 = pn.encrypt(JSON.stringify({ hi: 'there' })); - const data2 = pn.encrypt(JSON.stringify({ hi: 'there' })); + const data1 = await pn.encrypt(JSON.stringify({ hi: 'there' })); + const data2 = await pn.encrypt(JSON.stringify({ hi: 'there' })); - assert.notEqual(pn.encrypt(JSON.stringify({ hi: 'there' })), 'TejX6F2JNqH/gIiGHWN4Cw=='); + assert.notEqual(await pn.encrypt(JSON.stringify({ hi: 'there' })), 'TejX6F2JNqH/gIiGHWN4Cw=='); assert.notEqual(data1, data2); }); - it('supports encryption with custom key and static IV', () => { + it('supports encryption with custom key and static IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', useRandomIVs: false, uuid: 'myUUID', }); - assert.equal(pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'), 'TejX6F2JNqH/gIiGHWN4Cw=='); + assert.equal(await pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'), 'TejX6F2JNqH/gIiGHWN4Cw=='); }); - it('supports encryption with custom key and random IV', () => { + it('supports encryption with custom key and random IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', uuid: 'myUUID' }); - const data1 = pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'); - const data2 = pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'); + const data1 = await pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'); + const data2 = await pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'); - assert.notEqual(pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'), 'TejX6F2JNqH/gIiGHWN4Cw=='); + assert.notEqual(await pn.encrypt(JSON.stringify({ hi: 'there' }), 'customKey'), 'TejX6F2JNqH/gIiGHWN4Cw=='); assert.notEqual(data1, data2); }); - it('supports decryption with static IV', () => { + it('supports decryption with static IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', cipherKey: 'customKey', useRandomIVs: false, uuid: 'myUUID', }); - assert.deepEqual(pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw=='), { hi: 'there' }); + assert.deepEqual(await pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw=='), { hi: 'there' }); }); - it('supports decryption with random IV', () => { + it('supports decryption with random IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', cipherKey: 'customKey', uuid: 'myUUID', }); - const data = pn.encrypt(JSON.stringify({ hi: 'there2' })); + const data = await pn.encrypt(JSON.stringify({ hi: 'there2' })); - assert.notDeepEqual(pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw=='), { hi: 'there' }); - assert.deepEqual(pn.decrypt(data), { hi: 'there2' }); + assert.notDeepEqual(await pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw=='), { hi: 'there' }); + assert.deepEqual(await pn.decrypt(data), { hi: 'there2' }); }); - it('supports decryption with custom key and static IV', () => { + it('supports decryption with custom key and static IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', useRandomIVs: false, uuid: 'myUUID', }); - assert.deepEqual(pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw==', 'customKey'), { + assert.deepEqual(await pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw==', 'customKey'), { hi: 'there', }); }); - it('supports decryption with custom key and random IV', () => { + it('supports decryption with custom key and random IV', async () => { let pn = new PubNub({ subscribeKey: 'demo', uuid: 'myUUID' }); - const data = pn.encrypt(JSON.stringify({ hi: 'there2' }), 'customKey'); + const data = await pn.encrypt(JSON.stringify({ hi: 'there2' }), 'customKey'); - assert.notDeepEqual(pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw==', 'customKey'), { + assert.notDeepEqual(await pn.decrypt('TejX6F2JNqH/gIiGHWN4Cw==', 'customKey'), { hi: 'there', }); - assert.deepEqual(pn.decrypt(data, 'customKey'), { hi: 'there2' }); + assert.deepEqual(await pn.decrypt(data, 'customKey'), { hi: 'there2' }); }); - it('supports custom encryption/decryption', () => { - let customEncrypt = (data: Payload) => { - // @ts-expect-error Bundled library without types. - let cipher = CryptoJS.AES.encrypt(JSON.stringify(data), 'customKey'); - return cipher.toString(); - }; + it('supports custom encryption/decryption', async () => { + // Simple reversible transformation (custom encryption is user-defined and opaque to the SDK). + let customEncrypt = (data: Payload) => Buffer.from(JSON.stringify(data)).reverse().toString('base64'); - let customDecrypt = (data: Payload) => { - // @ts-expect-error Bundled library without types. - let bytes = CryptoJS.AES.decrypt(data, 'customKey'); - // @ts-expect-error Bundled library without types. - return JSON.parse(bytes.toString(CryptoJS.enc.Utf8)); - }; + let customDecrypt = (data: Payload) => + JSON.parse(Buffer.from(data as string, 'base64').reverse().toString('utf8')); let pn = new PubNub({ subscribeKey: 'demo', @@ -166,8 +158,9 @@ describe('#core / mounting point', () => { uuid: 'myUUID', }); - let ciphertext = pn.encrypt({ hi: 'there' }); + let ciphertext = await pn.encrypt({ hi: 'there' }); - assert.deepEqual(pn.decrypt(ciphertext), { hi: 'there' }); + assert.notDeepEqual(ciphertext, JSON.stringify({ hi: 'there' })); + assert.deepEqual(await pn.decrypt(ciphertext), { hi: 'there' }); }); }); diff --git a/test/unit/crypto/aes-gcm-cryptor.test.ts b/test/unit/crypto/aes-gcm-cryptor.test.ts new file mode 100644 index 000000000..ba19aa17b --- /dev/null +++ b/test/unit/crypto/aes-gcm-cryptor.test.ts @@ -0,0 +1,469 @@ +/* global describe, it */ + +import { createCipheriv, createHash, randomBytes } from 'crypto'; +import { PassThrough, Readable } from 'stream'; +import { expect } from 'chai'; + +import { WebCryptoModule } from '../../../src/crypto/modules/WebCryptoModule/webCryptoModule'; +import { NodeCryptoModule } from '../../../src/crypto/modules/NodeCryptoModule/nodeCryptoModule'; +import WebAesGcmCryptor from '../../../src/crypto/modules/WebCryptoModule/aesGcmCryptor'; +import NodeAesGcmCryptor from '../../../src/crypto/modules/NodeCryptoModule/aesGcmCryptor'; +import NodeCryptoPrimitives from '../../../src/crypto/primitives/node'; +import WebCryptoPrimitives from '../../../src/crypto/primitives/web'; +import { PubNubFile as WebPubNubFile } from '../../../src/file/modules/web'; +import NodePubNubFile from '../../../src/file/modules/node'; +import PubNub from '../../../src/node'; +import fixtures from '../../fixtures/crypto/aes-gcm-vectors'; + +/** + * Vectors were generated with independent `node:crypto` (`ACGM` layout: metadata is the nonce only and the + * authentication tag is appended to the ciphertext). Expected values are never computed with the code under test. + */ +const { vectors } = fixtures; +const DECRYPTION_ERROR = 'Decryption error: unable to decrypt data'; + +const fromHex = (hex: string) => new Uint8Array(Buffer.from(hex, 'hex')); +const toHex = (data: ArrayBuffer | Uint8Array) => + Buffer.from(data instanceof Uint8Array ? data : new Uint8Array(data)).toString('hex'); +const arrayBuffer = (hex: string) => fromHex(hex).buffer as ArrayBuffer; +const base64 = (hex: string) => Buffer.from(hex, 'hex').toString('base64'); + +/** + * Flip lowest bit of the byte at `offset` in HEX string. + */ +const flipBit = (hex: string, offset: number) => { + const bytes = fromHex(hex); + bytes[offset] ^= 0x01; + return toHex(bytes); +}; + +/** + * Flip lowest bit of the last byte in HEX string (last byte of the authentication tag). + */ +const flipLastBit = (hex: string) => flipBit(hex, hex.length / 2 - 1); + +/** + * Build `ACGM` payload: `PNED | 0x01 | ACGM | metadata length | metadata (nonce) | ciphertext || tag`. + */ +const acgmPayload = (metadataHex: string, bodyHex: string) => + Buffer.from('PNED').toString('hex') + + '01' + + Buffer.from('ACGM').toString('hex') + + (metadataHex.length / 2).toString(16).padStart(2, '0') + + metadataHex + + bodyHex; + +/** + * Independent AES-256-GCM encryption (used only to build payloads which aren't covered by reference vectors). + */ +const referenceEncrypt = (cipherKey: string, plaintext: string | Buffer, nonceHex: string) => { + const key = createHash('sha256').update(cipherKey, 'utf8').digest(); + const cipher = createCipheriv('aes-256-gcm', key, Buffer.from(nonceHex, 'hex')); + const ciphertext = Buffer.concat([ + cipher.update(typeof plaintext === 'string' ? Buffer.from(plaintext, 'utf8') : plaintext), + cipher.final(), + ]); + + // Auth tag is available only after `final` and appended to the ciphertext. + return acgmPayload(nonceHex, Buffer.concat([ciphertext, cipher.getAuthTag()]).toString('hex')); +}; + +/** + * Await promise rejection and return error. + */ +const rejection = async (promise: Promise | (() => unknown)) => { + try { + await (typeof promise === 'function' ? promise() : promise); + } catch (error) { + return error as Error; + } + + throw new Error('Expected promise to be rejected'); +}; + +/** + * Read whole stream content (rejects if stream emits error). + */ +const readStream = (stream: NodeJS.ReadableStream) => + new Promise<{ data: Buffer; chunks: number }>((resolve, reject) => { + const chunks: Buffer[] = []; + stream.on('data', (chunk: Buffer) => chunks.push(chunk)); + stream.on('error', reject); + stream.on('end', () => resolve({ data: Buffer.concat(chunks), chunks: chunks.length })); + }); + +const primitives: Record WebCryptoPrimitives | NodeCryptoPrimitives> = { + web: () => new WebCryptoPrimitives(), + node: () => new NodeCryptoPrimitives(), +}; + +const cryptors = { + web: (cipherKey: string) => WebCryptoModule.aesGcmCryptor({ cipherKey }), + node: (cipherKey: string) => NodeCryptoModule.aesGcmCryptor({ cipherKey }), +}; + +/** + * Cryptor input in format used by crypto modules for specific platform. + */ +const cryptorInput = (platform: keyof typeof cryptors, dataHex: string, metadataHex: string | null) => + platform === 'web' + ? { data: arrayBuffer(dataHex), metadata: metadataHex === null ? null : arrayBuffer(metadataHex) } + : { data: Buffer.from(dataHex, 'hex'), metadata: metadataHex === null ? null : Buffer.from(metadataHex, 'hex') }; + +const modules = { + 'web aesCbcCryptoModule': (cipherKey: string) => WebCryptoModule.aesCbcCryptoModule({ cipherKey }), + 'web legacyCryptoModule': (cipherKey: string) => WebCryptoModule.legacyCryptoModule({ cipherKey }), + 'node aesCbcCryptoModule': (cipherKey: string) => NodeCryptoModule.aesCbcCryptoModule({ cipherKey }), + 'node legacyCryptoModule': (cipherKey: string) => NodeCryptoModule.legacyCryptoModule({ cipherKey }), +}; + +describe('AES-GCM (ACGM) decrypt-only cryptor', () => { + describe('reference vectors', () => { + it('are internally consistent (checked with node:crypto)', () => { + for (const vector of vectors) { + expect(createHash('sha256').update(vector.cipherKey, 'utf8').digest('hex'), vector.name).to.equal( + vector.keySha256, + ); + expect(vector.metadata, vector.name).to.equal(vector.nonce); + expect(vector.ciphertextWithTag, vector.name).to.equal(vector.ciphertext + vector.tag); + expect(vector.payload.slice(18, 20), vector.name).to.equal('0c'); + expect(vector.payload, vector.name).to.equal(acgmPayload(vector.metadata, vector.ciphertextWithTag)); + if (vector.plaintext !== undefined) + expect(Buffer.from(vector.plaintext, 'utf8').toString('hex'), vector.name).to.equal(vector.plaintextHex); + } + }); + }); + + describe('aesGcmDecrypt primitive', () => { + Object.entries(primitives).forEach(([name, create]) => { + it(`${name} decrypts every reference vector`, async () => { + for (const vector of vectors) { + const decrypted = await create().aesGcmDecrypt( + fromHex(vector.keySha256), + fromHex(vector.nonce), + fromHex(vector.ciphertextWithTag), + ); + expect(toHex(decrypted), `${name}: ${vector.name}`).to.equal(vector.plaintextHex); + } + }); + + it(`${name} rejects modified or missing tag`, async () => { + const [vector] = vectors; + const key = fromHex(vector.keySha256); + const nonce = fromHex(vector.nonce); + + await rejection(create().aesGcmDecrypt(key, nonce, fromHex(flipLastBit(vector.ciphertextWithTag)))); + await rejection(create().aesGcmDecrypt(key, nonce, fromHex(vector.ciphertext))); + await rejection(create().aesGcmDecrypt(key, nonce, fromHex(vector.tag.slice(2)))); + }); + }); + }); + + describe('cryptor', () => { + (Object.keys(cryptors) as (keyof typeof cryptors)[]).forEach((platform) => { + describe(platform, () => { + it('has ACGM identifier', () => { + expect(cryptors[platform]('enigma').identifier).to.equal('ACGM'); + }); + + it('decrypts every reference vector', async () => { + for (const vector of vectors) { + const cryptor = cryptors[platform](vector.cipherKey); + const decrypted = await cryptor.decrypt( + cryptorInput(platform, vector.ciphertextWithTag, vector.metadata) as never, + ); + expect(toHex(decrypted), vector.name).to.equal(vector.plaintextHex); + + if (platform === 'web') { + const fileData = await (cryptor as WebAesGcmCryptor).decryptFileData( + cryptorInput('web', vector.ciphertextWithTag, vector.metadata) as never, + ); + expect(toHex(fileData), vector.name).to.equal(vector.plaintextHex); + } + } + }); + + it('rejects wrong key and modified nonce, tag or ciphertext with the same generic error', async () => { + for (const vector of vectors) { + const cases: [string, string, string, string][] = [ + ['wrong key', 'wrong', vector.ciphertextWithTag, vector.metadata], + ['modified nonce', vector.cipherKey, vector.ciphertextWithTag, flipBit(vector.metadata, 11)], + ['modified tag', vector.cipherKey, flipLastBit(vector.ciphertextWithTag), vector.metadata], + ['modified ciphertext', vector.cipherKey, flipBit(vector.ciphertextWithTag, 0), vector.metadata], + ['appended byte', vector.cipherKey, vector.ciphertextWithTag + '00', vector.metadata], + ]; + + for (const [name, cipherKey, ciphertext, metadata] of cases) { + const error = await rejection( + async () => + await cryptors[platform](cipherKey).decrypt(cryptorInput(platform, ciphertext, metadata) as never), + ); + expect(error.message, `${vector.name}: ${name}`).to.equal(DECRYPTION_ERROR); + expect((error as Error & { cause?: unknown }).cause, `${vector.name}: ${name}`).to.equal(undefined); + } + } + }); + + it('rejects invalid metadata and empty content', async () => { + const [vector] = vectors; + const cryptor = cryptors[platform](vector.cipherKey); + + // `nonce || tag` is the old 28 bytes metadata layout. + for (const metadata of [null, '', vector.nonce.slice(2), vector.nonce + '00', vector.nonce + vector.tag]) { + const error = await rejection( + async () => await cryptor.decrypt(cryptorInput(platform, vector.ciphertextWithTag, metadata) as never), + ); + expect(error.message, `metadata: ${metadata}`).to.equal('Decryption error: invalid metadata'); + } + + // Empty plaintext (tag only) is rejected like for other cryptors. + for (const data of ['', vector.tag.slice(2), vector.tag]) { + const error = await rejection( + async () => await cryptor.decrypt(cryptorInput(platform, data, vector.metadata) as never), + ); + expect(error.message, `data: ${data}`).to.equal('Decryption error: empty content'); + } + }); + + it('refuses encryption', async () => { + const cryptor = cryptors[platform]('enigma'); + const message = 'Encryption error: AES-GCM cryptor (ACGM) supports decryption only.'; + + expect(() => cryptor.encrypt('hello')).to.throw(message); + if (platform === 'web') + expect( + (await rejection((cryptor as WebAesGcmCryptor).encryptFileData(new ArrayBuffer(4)))).message, + ).to.equal(message); + else + expect( + (await rejection((cryptor as NodeAesGcmCryptor).encryptStream(Readable.from([Buffer.from('a')])))) + .message, + ).to.equal(message); + }); + + it('factory requires cipher key', () => { + expect(() => (platform === 'web' ? WebCryptoModule : NodeCryptoModule).aesGcmCryptor({})).to.throw( + 'Crypto module error: cipher key not set.', + ); + }); + }); + }); + }); + + describe('crypto module', () => { + Object.entries(modules).forEach(([name, create]) => { + it(`${name} routes ACGM payloads (ArrayBuffer and base64)`, async () => { + for (const vector of vectors) { + const module = create(vector.cipherKey); + + for (const payload of [arrayBuffer(vector.payload), base64(vector.payload)]) { + const decrypted = await module.decrypt(payload); + expect(decrypted, vector.name).to.be.instanceOf(ArrayBuffer); + expect(toHex(decrypted as ArrayBuffer), vector.name).to.equal(vector.plaintextHex); + if (vector.plaintext !== undefined) + expect(new TextDecoder().decode(decrypted as ArrayBuffer), vector.name).to.equal(vector.plaintext); + } + } + }); + + it(`${name} rejects tampered payload and wrong key with the generic error`, async () => { + const [vector] = vectors; + const tampered = acgmPayload(vector.metadata, flipBit(vector.ciphertextWithTag, 3)); + + expect((await rejection(create(vector.cipherKey).decrypt(base64(tampered)))).message).to.equal( + DECRYPTION_ERROR, + ); + expect((await rejection(create('wrong').decrypt(base64(vector.payload)))).message).to.equal(DECRYPTION_ERROR); + }); + + it(`${name} rejects malformed ACGM payloads`, async () => { + const [vector] = vectors; + const module = create(vector.cipherKey); + + const oldLayout = acgmPayload(vector.nonce + vector.tag, vector.ciphertext); + expect((await rejection(module.decrypt(base64(oldLayout)))).message).to.equal( + 'Decryption error: invalid metadata', + ); + + for (const body of ['', vector.tag]) { + expect((await rejection(module.decrypt(base64(acgmPayload(vector.metadata, body))))).message).to.equal( + 'Decryption error: empty content', + ); + } + }); + + it(`${name} keeps encrypting with its default cryptor`, async () => { + const module = create('enigma'); + const encrypted = await module.encrypt('"hi"'); + const bytes = new Uint8Array(typeof encrypted === 'string' ? Buffer.from(encrypted, 'base64') : encrypted); + const isLegacy = name.includes('legacy'); + + if (isLegacy) expect(Buffer.from(bytes.slice(0, 4)).toString()).to.not.equal('PNED'); + else expect(Buffer.from(bytes.slice(5, 9)).toString()).to.equal('ACRH'); + }); + }); + + it('refuses ACGM cryptor as default cryptor', () => { + const message = "Crypto module error: AES-GCM cryptor (ACGM) can't be used as default cryptor."; + + expect(() => new WebCryptoModule({ default: cryptors.web('enigma') })).to.throw(message); + expect(() => WebCryptoModule.withDefaultCryptor(cryptors.web('enigma'))).to.throw(message); + expect(() => new NodeCryptoModule({ default: cryptors.node('enigma') })).to.throw(message); + expect(() => NodeCryptoModule.withDefaultCryptor(cryptors.node('enigma'))).to.throw(message); + }); + + it('requires explicit ACGM registration in custom modules', async () => { + const [vector] = vectors; + const withoutGcm = WebCryptoModule.withDefaultCryptor( + WebCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }).defaultCryptor, + ); + expect((await rejection(withoutGcm.decrypt(base64(vector.payload)))).message).to.equal('Unknown cryptor error'); + + const withGcm = new NodeCryptoModule({ + default: NodeCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }).defaultCryptor, + cryptors: [NodeCryptoModule.aesGcmCryptor({ cipherKey: vector.cipherKey })], + }); + expect(toHex((await withGcm.decrypt(base64(vector.payload))) as ArrayBuffer)).to.equal(vector.plaintextHex); + }); + + it('keeps the first registered cryptor when identifiers repeat', async () => { + const [vector] = vectors; + const marker = new TextEncoder().encode('first').buffer; + const first = { + identifier: 'ACGM', + encrypt: () => ({ data: new ArrayBuffer(0), metadata: null }), + encryptFileData: async () => ({ data: new ArrayBuffer(0), metadata: null }), + decrypt: () => marker, + decryptFileData: async () => marker, + }; + const module = new WebCryptoModule({ + default: WebCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }).defaultCryptor, + cryptors: [first, WebCryptoModule.aesGcmCryptor({ cipherKey: vector.cipherKey })], + }); + + expect(await module.decrypt(base64(vector.payload))).to.equal(marker); + }); + }); + + describe('client configured with cipherKey', () => { + it('decrypts ACGM payload produced by another SDK', async () => { + const pubnub = new PubNub({ subscribeKey: 'demo', publishKey: 'demo', userId: 'myUUID', cipherKey: 'enigma' }); + const payload = referenceEncrypt('enigma', JSON.stringify({ text: 'hi' }), '0f0e0d0c0b0a090807060504'); + + expect(await pubnub.decrypt(base64(payload))).to.deep.equal({ text: 'hi' }); + pubnub.destroy(); + }); + }); + + describe('files', () => { + /** + * File-sized payload (multiple stream chunks) produced with independent `node:crypto`. + */ + const largePlaintext = randomBytes(64 * 1024 + 7); + const largePayload = referenceEncrypt('enigma', largePlaintext, '8f5a2d1b6e9c4ef0a37d83b9'); + const fileVectors = [ + ...vectors, + { name: 'large', cipherKey: 'enigma', payload: largePayload, plaintextHex: largePlaintext.toString('hex') }, + ]; + + it('web module decrypts ACGM file with the routed cryptor', async () => { + for (const vector of fileVectors) { + const module = WebCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }); + const file = WebPubNubFile.create({ name: 'file.bin', data: arrayBuffer(vector.payload) } as never); + const decrypted = await module.decryptFile(file, WebPubNubFile); + + expect(toHex(await decrypted!.data.arrayBuffer()), vector.name).to.equal(vector.plaintextHex); + } + }); + + it('node module decrypts ACGM file from Buffer', async () => { + for (const vector of fileVectors) { + const module = NodeCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }); + const file = NodePubNubFile.create({ name: 'file.bin', data: Buffer.from(vector.payload, 'hex') }); + const decrypted = await module.decryptFile(file, NodePubNubFile); + + expect((await decrypted!.toBuffer()).toString('hex'), vector.name).to.equal(vector.plaintextHex); + } + }); + + /** + * Create stream-based file. `chunked` delivers payload in separate delayed chunks (like a download): first chunk + * ends inside the nonce and the last chunk boundary is inside the trailing authentication tag. Large payloads are + * delivered in 1000 bytes chunks. Otherwise the whole payload is buffered before decryption starts. + */ + const streamFile = (payloadHex: string, chunked = false) => { + const stream = new PassThrough(); + const file = NodePubNubFile.create({ name: 'file.bin', stream }); + const payload = Buffer.from(payloadHex, 'hex'); + file.contentLength = payload.byteLength; + + if (!chunked) stream.end(payload); + else { + const boundaries = [12]; + for (let offset = 1000; offset < payload.byteLength - 8; offset += 1000) boundaries.push(offset); + boundaries.push(payload.byteLength - 8); + + stream.write(payload.subarray(0, boundaries[0])); + boundaries.forEach((start, index) => { + const end = boundaries[index + 1] ?? payload.byteLength; + const chunk = payload.subarray(start, end); + setTimeout(() => (end === payload.byteLength ? stream.end(chunk) : stream.write(chunk)), 1); + }); + } + + return file; + }; + + [false, true].forEach((chunked) => { + it(`node module decrypts ACGM file from ${chunked ? 'chunked' : 'buffered'} stream`, async () => { + for (const vector of fileVectors) { + const module = NodeCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }); + const decrypted = await module.decryptFile(streamFile(vector.payload, chunked), NodePubNubFile); + const { data } = await readStream(decrypted!.data as Readable); + + expect(data.toString('hex'), vector.name).to.equal(vector.plaintextHex); + } + }); + }); + + [false, true].forEach((chunked) => { + it(`node module never releases plaintext from tampered ${chunked ? 'chunked' : 'buffered'} stream`, async () => { + const [vector] = vectors; + const module = NodeCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }); + + for (const tampered of [ + acgmPayload(vector.metadata, flipLastBit(vector.ciphertextWithTag)), + largePayload.slice(0, -2) + flipLastBit(largePayload.slice(-2)), + ]) { + const decrypted = await module.decryptFile(streamFile(tampered, chunked), NodePubNubFile); + const stream = decrypted!.data as Readable; + let chunks = 0; + stream.on('data', () => chunks++); + + const error = await new Promise((resolve) => stream.on('error', resolve)); + expect(error.message).to.equal(DECRYPTION_ERROR); + expect(chunks).to.equal(0); + } + }); + }); + + it('node module rejects stream without ciphertext', async () => { + const [vector] = vectors; + const module = NodeCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }); + const decrypted = await module.decryptFile(streamFile(acgmPayload(vector.metadata, vector.tag)), NodePubNubFile); + + const error = await new Promise((resolve) => (decrypted!.data as Readable).on('error', resolve)); + expect(error.message).to.equal('Decryption error: empty content'); + }); + + it('node module rejects stream with old (nonce and tag) metadata layout', async () => { + const [vector] = vectors; + const module = NodeCryptoModule.aesCbcCryptoModule({ cipherKey: vector.cipherKey }); + const oldLayout = acgmPayload(vector.nonce + vector.tag, vector.ciphertext); + + expect((await rejection(module.decryptFile(streamFile(oldLayout), NodePubNubFile))).message).to.equal( + 'Decryption error: invalid metadata', + ); + }); + }); +}); diff --git a/test/unit/crypto/bytes.test.ts b/test/unit/crypto/bytes.test.ts new file mode 100644 index 000000000..050ca9107 --- /dev/null +++ b/test/unit/crypto/bytes.test.ts @@ -0,0 +1,266 @@ +/* global describe, it, afterEach */ +/* eslint-disable @typescript-eslint/no-require-imports */ + +import { expect } from 'chai'; + +import NobleCryptoPrimitives from '../../../src/crypto/primitives/react-native'; +import vectors from '../../fixtures/crypto/legacy-vectors'; + +type BytesModule = typeof import('../../../src/core/components/cryptography/bytes'); +type LegacyCryptoModule = typeof import('../../../src/core/components/cryptography/index'); +type Codecs = { TextEncoder: typeof TextEncoder; TextDecoder: typeof TextDecoder }; + +const globals = globalThis as unknown as Record; +const nativeCodecs: Codecs = { TextEncoder: globalThis.TextEncoder, TextDecoder: globalThis.TextDecoder }; +const bytesPath = require.resolve('../../../src/core/components/cryptography/bytes'); +const legacyCryptoPath = require.resolve('../../../src/core/components/cryptography/index'); + +/** + * Load real `fast-text-encoding` polyfill the way it initialises on React Native: without native + * `TextEncoder` / `TextDecoder` and without `Buffer` (otherwise it delegates to `Buffer`). + */ +const loadPolyfillCodecs = (): Codecs => { + const polyfillPath = require.resolve('fast-text-encoding'); + const saved = { TextEncoder: globals.TextEncoder, TextDecoder: globals.TextDecoder, Buffer: globals.Buffer }; + delete require.cache[polyfillPath]; + delete globals.TextEncoder; + delete globals.TextDecoder; + delete globals.Buffer; + + try { + require(polyfillPath); + const codecs = { TextEncoder: globals.TextEncoder, TextDecoder: globals.TextDecoder } as Codecs; + if (codecs.TextDecoder === saved.TextDecoder) throw new Error('fast-text-encoding polyfill not installed'); + return codecs; + } finally { + Object.assign(globals, saved); + delete require.cache[polyfillPath]; + } +}; + +/** + * Run `callback` with fresh SDK crypto modules evaluated while `codecs` are installed as globals. + */ +const withCodecs = async ( + codecs: Codecs, + callback: (modules: { bytes: BytesModule; legacy: LegacyCryptoModule }) => T | Promise, +): Promise => { + const saved = { TextEncoder: globals.TextEncoder, TextDecoder: globals.TextDecoder }; + const cached = [bytesPath, legacyCryptoPath].map((path) => [path, require.cache[path]] as const); + Object.assign(globals, codecs); + cached.forEach(([path]) => delete require.cache[path]); + + try { + return await callback({ bytes: require(bytesPath), legacy: require(legacyCryptoPath) }); + } finally { + Object.assign(globals, saved); + cached.forEach(([path, module]) => { + if (module) require.cache[path] = module; + else delete require.cache[path]; + }); + } +}; + +/** + * Reference implementation: CryptoJS `Utf8.stringify` (`decodeURIComponent(escape(latin1))`). + */ +const cryptoJSUtf8 = (bytes: Uint8Array): { ok: true; value: string } | { ok: false } => { + try { + return { ok: true, value: decodeURIComponent(escape(String.fromCharCode(...bytes))) }; + } catch (_error) { + return { ok: false }; + } +}; + +const decodeResult = (bytes: BytesModule, input: Uint8Array): { ok: true; value: string } | { ok: false } => { + try { + return { ok: true, value: bytes.decodeUtf8Strict(input) }; + } catch (error) { + expect((error as Error).message).to.equal('Malformed UTF-8 data'); + return { ok: false }; + } +}; + +const hex = (bytes: Uint8Array) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(' '); + +/** + * Edge cases covering every rule of Unicode well-formed UTF-8 (Table 3-7). + */ +const edgeCases: Record = { + empty: [], + 'ASCII and NUL': [0x22, 0x41, 0x00, 0x7f, 0x22], + 'two-byte (C2 80, DF BF)': [0xc2, 0x80, 0xdf, 0xbf], + 'three-byte boundaries (E0 A0 80, EF BF BF)': [0xe0, 0xa0, 0x80, 0xef, 0xbf, 0xbf], + 'last BMP code point before surrogates (ED 9F BF)': [0xed, 0x9f, 0xbf], + 'first code point after surrogates (EE 80 80)': [0xee, 0x80, 0x80], + 'U+FFFD (EF BF BD)': [0xef, 0xbf, 0xbd], + 'four-byte boundaries (F0 90 80 80, F4 8F BF BF)': [0xf0, 0x90, 0x80, 0x80, 0xf4, 0x8f, 0xbf, 0xbf], + 'emoji (F0 9F 8C 8D)': [0xf0, 0x9f, 0x8c, 0x8d], + 'leading BOM (EF BB BF 41)': [0xef, 0xbb, 0xbf, 0x41], + 'two leading BOMs (EF BB BF EF BB BF)': [0xef, 0xbb, 0xbf, 0xef, 0xbb, 0xbf], + 'BOM only (EF BB BF)': [0xef, 0xbb, 0xbf], + 'lone low surrogate (review finding: 22 ED B0 80 22)': [0x22, 0xed, 0xb0, 0x80, 0x22], + 'lone high surrogate (ED A0 80)': [0xed, 0xa0, 0x80], + 'last surrogate (ED BF BF)': [0xed, 0xbf, 0xbf], + 'CESU-8 surrogate pair (ED A0 BD ED B8 80)': [0xed, 0xa0, 0xbd, 0xed, 0xb8, 0x80], + 'overlong two-byte (C0 AF)': [0xc0, 0xaf], + 'overlong C1 (C1 BF)': [0xc1, 0xbf], + 'overlong three-byte (E0 9F BF)': [0xe0, 0x9f, 0xbf], + 'overlong four-byte (F0 8F BF BF)': [0xf0, 0x8f, 0xbf, 0xbf], + 'above U+10FFFF (F4 90 80 80)': [0xf4, 0x90, 0x80, 0x80], + 'invalid lead byte F5': [0xf5, 0x80, 0x80, 0x80], + 'invalid lead byte FF': [0xff], + 'stray continuation (41 80)': [0x41, 0x80], + 'truncated two-byte (C3)': [0x41, 0xc3], + 'truncated three-byte (E2 82)': [0xe2, 0x82], + 'truncated four-byte (F0 9F 8C)': [0xf0, 0x9f, 0x8c], + 'interrupted sequence (E2 41 82)': [0xe2, 0x41, 0x82], + 'continuation byte out of range (C3 C0)': [0xc3, 0xc0], +}; + +/** + * Deterministic pseudo-random generator (mulberry32). + */ +const random = (seed: number) => () => { + seed = (seed + 0x6d2b79f5) | 0; + let t = Math.imul(seed ^ (seed >>> 15), 1 | seed); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; +}; + +/** + * Seeded corpus: random byte arrays biased toward UTF-8 boundary bytes, lead + continuation byte sequences and + * random well-formed strings. + */ +const randomCorpus = (): Uint8Array[] => { + const next = random(0x5eed); + const interesting = [ + 0x00, 0x22, 0x41, 0x7f, 0x80, 0x8f, 0x90, 0x9f, 0xa0, 0xbb, 0xbd, 0xbf, 0xc0, 0xc1, 0xc2, 0xdf, 0xe0, 0xe1, 0xec, + 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf3, 0xf4, 0xf5, 0xfe, 0xff, + ]; + const corpus: Uint8Array[] = []; + + for (let i = 0; i < 5000; i++) { + const bytes = new Uint8Array(Math.floor(next() * 12)); + for (let j = 0; j < bytes.length; j++) + bytes[j] = next() < 0.6 ? interesting[Math.floor(next() * interesting.length)] : Math.floor(next() * 256); + corpus.push(bytes); + } + + // Lead byte followed by 1-3 continuation bytes (exercises second-byte range rules: E0, ED, F0, F4). + const leads = [0xc0, 0xc1, 0xc2, 0xdf, 0xe0, 0xe1, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf4, 0xf5]; + for (let i = 0; i < 3000; i++) { + const bytes = new Uint8Array(2 + Math.floor(next() * 3)); + bytes[0] = leads[Math.floor(next() * leads.length)]; + for (let j = 1; j < bytes.length; j++) bytes[j] = 0x80 + Math.floor(next() * 0x40); + corpus.push(bytes); + } + + const ranges = [ + [0x00, 0x7f], + [0x80, 0x7ff], + [0x800, 0xd7ff], + [0xe000, 0xffff], + [0x10000, 0x10ffff], + ]; + for (let i = 0; i < 1000; i++) { + let text = ''; + for (let j = Math.floor(next() * 8); j > 0; j--) { + const [min, max] = ranges[Math.floor(next() * ranges.length)]; + text += String.fromCodePoint(min + Math.floor(next() * (max - min + 1))); + } + corpus.push(new nativeCodecs.TextEncoder().encode(text)); + } + + return corpus; +}; + +describe('strict UTF-8 decoding (CryptoJS Utf8.stringify parity)', () => { + const environments: Record Codecs> = { + 'native codecs': () => nativeCodecs, + 'fast-text-encoding polyfill (React Native, no Buffer)': loadPolyfillCodecs, + }; + const corpus = randomCorpus(); + + afterEach(() => { + Object.assign(globals, nativeCodecs); + }); + + Object.entries(environments).forEach(([environment, codecs]) => { + describe(environment, () => { + it('matches CryptoJS for every well-formedness rule', async () => { + await withCodecs(codecs(), ({ bytes }) => { + for (const [name, values] of Object.entries(edgeCases)) { + const input = new Uint8Array(values); + expect(decodeResult(bytes, input), `${name} [${hex(input)}]`).to.deep.equal(cryptoJSUtf8(input)); + } + }); + }); + + it('matches CryptoJS for a seeded random corpus', async () => { + await withCodecs(codecs(), ({ bytes }) => { + for (const input of corpus) + expect(decodeResult(bytes, input), `[${hex(input)}]`).to.deep.equal(cryptoJSUtf8(input)); + }); + }); + + it('preserves leading BOM (same as CryptoJS)', async () => { + await withCodecs(codecs(), ({ bytes }) => { + expect(bytes.decodeUtf8Strict(new Uint8Array([0xef, 0xbb, 0xbf, 0x41]))).to.equal('ο»ΏA'); + }); + }); + + it('legacy decryption resolves to null for malformed UTF-8 and decrypts golden vectors', async () => { + await withCodecs(codecs(), async ({ legacy }) => { + const primitives = new NobleCryptoPrimitives(); + const crypto = new legacy.default({ cipherKey: 'customKey', useRandomIVs: false }, primitives); + const digest = await primitives.sha256(new nativeCodecs.TextEncoder().encode('customKey')); + const key = new Uint8Array(Array.from(hex(digest).replace(/ /g, '').slice(0, 32), (c) => c.charCodeAt(0))); + const iv = new Uint8Array(Array.from('0123456789012345', (c) => c.charCodeAt(0))); + const encrypt = async (values: number[]) => + Buffer.from(await primitives.aesCbcEncrypt(key, iv, new Uint8Array(values))).toString('base64'); + + // Review finding: `"\uDC00"` encoded as lone surrogate bytes. + expect(await crypto.decrypt(await encrypt([0x22, 0xed, 0xb0, 0x80, 0x22]))).to.equal(null); + // BOM is preserved, so JSON.parse fails (same as v13). + expect(await crypto.decrypt(await encrypt([0xef, 0xbb, 0xbf, 0x22, 0x22]))).to.equal(null); + // Valid non-ASCII payload still decrypts. + expect(await crypto.decrypt(await encrypt([0x22, 0xf0, 0x9f, 0x8c, 0x8d, 0x22]))).to.equal('🌍'); + + for (const vector of vectors.legacy.filter((v) => v.cipherKey === 'customKey')) { + expect(await crypto.decrypt(vector.staticIV)).to.deep.equal(JSON.parse(vector.plaintext)); + } + }); + }); + }); + }); + + it('preserves leading BOM with decoders which ignore `ignoreBOM` option', async () => { + class StrippingTextDecoder extends nativeCodecs.TextDecoder { + constructor(label?: string) { + super(label); + } + } + + await withCodecs({ TextEncoder: nativeCodecs.TextEncoder, TextDecoder: StrippingTextDecoder }, ({ bytes }) => { + expect(bytes.decodeUtf8Strict(new Uint8Array([0xef, 0xbb, 0xbf, 0x41]))).to.equal('\uFEFFA'); + expect(bytes.decodeUtf8Strict(new Uint8Array([0xef, 0xbb, 0xbf, 0xef, 0xbb, 0xbf]))).to.equal('\uFEFF\uFEFF'); + expect(bytes.decodeUtf8Strict(new Uint8Array([0x41]))).to.equal('A'); + }); + }); + + it('module can be loaded with TextDecoder polyfills which reject `fatal` option', async () => { + // Same behaviour as `fast-text-encoding` used on React Native. + class NoFatalTextDecoder extends nativeCodecs.TextDecoder { + constructor(label?: string, options?: TextDecoderOptions) { + if (options?.fatal) + throw new RangeError("Failed to construct 'TextDecoder': the 'fatal' option is unsupported."); + super(label, options); + } + } + + await withCodecs({ TextEncoder: nativeCodecs.TextEncoder, TextDecoder: NoFatalTextDecoder }, ({ bytes }) => { + expect(bytes.decodeUtf8Strict(new Uint8Array([0x22, 0x41, 0x22]))).to.equal('"A"'); + }); + }); +}); diff --git a/test/unit/crypto/crypto-module-parity.test.ts b/test/unit/crypto/crypto-module-parity.test.ts new file mode 100644 index 000000000..f5768a878 --- /dev/null +++ b/test/unit/crypto/crypto-module-parity.test.ts @@ -0,0 +1,144 @@ +/* global describe, it */ + +import { expect } from 'chai'; + +import { WebCryptoModule } from '../../../src/crypto/modules/WebCryptoModule/webCryptoModule'; +import { NodeCryptoModule } from '../../../src/crypto/modules/NodeCryptoModule/nodeCryptoModule'; +import { ICryptor as WebICryptor } from '../../../src/crypto/modules/WebCryptoModule/ICryptor'; +import { ICryptor as NodeICryptor } from '../../../src/crypto/modules/NodeCryptoModule/ICryptor'; +import vectors from '../../fixtures/crypto/legacy-vectors'; + +const decoder = new TextDecoder(); + +/** + * Normalize crypto module decryption result for comparison. + */ +const asPayload = (result: ArrayBuffer | unknown) => + result instanceof ArrayBuffer ? JSON.parse(decoder.decode(result)) : result; + +const toArrayBuffer = (data: ArrayBuffer | string) => + typeof data === 'string' ? new TextEncoder().encode(data).buffer : data; + +const modules = { + 'web aesCbcCryptoModule': (cipherKey: string) => WebCryptoModule.aesCbcCryptoModule({ cipherKey }), + 'web legacyCryptoModule': (cipherKey: string) => WebCryptoModule.legacyCryptoModule({ cipherKey }), + 'node aesCbcCryptoModule': (cipherKey: string) => NodeCryptoModule.aesCbcCryptoModule({ cipherKey }), + 'node legacyCryptoModule': (cipherKey: string) => NodeCryptoModule.legacyCryptoModule({ cipherKey }), +}; + +describe('crypto module parity with CryptoJS', () => { + Object.entries(modules).forEach(([name, create]) => { + it(`${name} decrypts every CryptoJS-era payload (web / node, AES-CBC / legacy)`, async () => { + for (const vector of vectors.cryptoModule) { + const module = create(vector.cipherKey); + const expected = JSON.parse(vector.plaintext); + + for (const ciphertext of [vector.webAesCbc, vector.webLegacy, vector.nodeAesCbc, vector.nodeLegacy]) + expect(asPayload(await module.decrypt(ciphertext))).to.deep.equal(expected); + } + }); + }); + + it('web and node modules decrypt each other output', async () => { + const payload = JSON.stringify({ text: 'Hello, δΈ–η•Œ! 🌍', n: 42 }); + + for (const [encryptWith, decryptWith] of [ + ['web aesCbcCryptoModule', 'node aesCbcCryptoModule'], + ['node aesCbcCryptoModule', 'web aesCbcCryptoModule'], + ['web legacyCryptoModule', 'node aesCbcCryptoModule'], + ['node legacyCryptoModule', 'web aesCbcCryptoModule'], + ] as const) { + const encrypted = await modules[encryptWith]('enigma').encrypt(payload); + expect(asPayload(await modules[decryptWith]('enigma').decrypt(encrypted))).to.deep.equal(JSON.parse(payload)); + } + }); + + it('AES-CBC payload keeps PNED header layout', async () => { + const encrypted = toArrayBuffer(await WebCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }).encrypt('"hi"')); + const bytes = new Uint8Array(encrypted); + + expect(decoder.decode(bytes.slice(0, 4))).to.equal('PNED'); + expect(bytes[4]).to.equal(1); + expect(decoder.decode(bytes.slice(5, 9))).to.equal('ACRH'); + expect(bytes[9]).to.equal(16); + // header (10) + IV (16) + single padded block (16) + expect(bytes.byteLength).to.equal(42); + }); + + it('web AES-CBC cryptor round-trips binary data', async () => { + const module = WebCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + const binary = new Uint8Array(256).map((_, index) => index); + const decrypted = await module.decrypt(await module.encrypt(binary.buffer)); + + expect(new Uint8Array(decrypted as ArrayBuffer)).to.deep.equal(binary); + }); + + it('web AES-CBC cryptor rejects empty content', async () => { + const module = WebCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + + try { + await module.encrypt(''); + expect.fail('Expected rejection'); + } catch (error) { + expect((error as Error).message).to.equal('encryption error. empty content'); + } + }); + + describe('custom cryptors', () => { + const encoder = new TextEncoder(); + const xor = (data: Uint8Array) => data.map((byte) => byte ^ 0x5a); + + it('web module supports synchronous and asynchronous custom cryptors', async () => { + const sync: WebICryptor = { + identifier: 'SYNC', + encrypt: (data) => ({ + data: xor(new Uint8Array(toArrayBuffer(data))).buffer, + metadata: new Uint8Array([1, 2]).buffer, + }), + decrypt: (data) => xor(new Uint8Array(data.data as ArrayBuffer)).buffer, + encryptFileData: async () => ({ data: new ArrayBuffer(0), metadata: null }), + decryptFileData: async () => new ArrayBuffer(0), + }; + const async: WebICryptor = { + ...sync, + identifier: 'ASYN', + encrypt: async (data) => sync.encrypt(data), + decrypt: async (data) => sync.decrypt(data), + }; + + for (const cryptor of [sync, async]) { + const module = WebCryptoModule.withDefaultCryptor(cryptor); + const decrypted = await module.decrypt(await module.encrypt(encoder.encode('"custom"').buffer)); + expect(asPayload(decrypted)).to.equal('custom'); + } + }); + + it('node module supports synchronous and asynchronous custom cryptors', async () => { + const sync = { + identifier: 'SYNC', + encrypt: (data: BufferSource | string) => ({ + data: Buffer.from(xor(new Uint8Array(toArrayBuffer(data as ArrayBuffer | string)))), + metadata: Buffer.from([1, 2]), + }), + decrypt: (data: { data: Buffer | string }) => { + const bytes = xor(new Uint8Array(data.data as Buffer)); + return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); + }, + encryptStream: async () => ({ stream: process.stdin, metadataLength: 0 }), + decryptStream: async () => process.stdin, + } as unknown as NodeICryptor; + const async = { + ...sync, + identifier: 'ASYN', + encrypt: async (data: BufferSource | string) => sync.encrypt(data), + decrypt: async (data: Parameters[0]) => sync.decrypt(data), + } as NodeICryptor; + + for (const cryptor of [sync, async]) { + const module = NodeCryptoModule.withDefaultCryptor(cryptor); + const decrypted = await module.decrypt(await module.encrypt(encoder.encode('"custom"').buffer)); + expect(asPayload(decrypted)).to.equal('custom'); + } + }); + }); +}); diff --git a/test/unit/crypto/error-parity.test.ts b/test/unit/crypto/error-parity.test.ts new file mode 100644 index 000000000..21030f397 --- /dev/null +++ b/test/unit/crypto/error-parity.test.ts @@ -0,0 +1,101 @@ +/* global describe, it */ + +import { expect } from 'chai'; + +import { WebCryptoModule } from '../../../src/crypto/modules/WebCryptoModule/webCryptoModule'; +import { NodeCryptoModule } from '../../../src/crypto/modules/NodeCryptoModule/nodeCryptoModule'; +import NobleCryptoPrimitives from '../../../src/crypto/primitives/react-native'; +import { CryptoPrimitives } from '../../../src/core/interfaces/crypto-primitives'; +import LegacyCrypto from '../../../src/core/components/cryptography/index'; +import NodeCryptoPrimitives from '../../../src/crypto/primitives/node'; +import WebCryptoPrimitives from '../../../src/crypto/primitives/web'; +import vectors from '../../fixtures/crypto/legacy-vectors'; + +const implementations: Record CryptoPrimitives> = { + web: () => new WebCryptoPrimitives(), + node: () => new NodeCryptoPrimitives(), + noble: () => new NobleCryptoPrimitives(), +}; + +/** + * Await promise rejection and return error. + */ +const rejection = async (promise: Promise) => { + try { + await promise; + } catch (error) { + return error as Error; + } + throw new Error('Expected promise to be rejected'); +}; + +describe('crypto error handling parity', () => { + describe('legacy cryptor resolves to null on undecryptable data (same as CryptoJS)', () => { + Object.entries(implementations).forEach(([name, primitives]) => { + it(`${name}: wrong key, truncated, non-base64 and malformed data`, async () => { + const vector = vectors.legacy.find((v) => v.cipherKey === 'customKey' && v.plaintext.length > 40)!; + const staticIV = new LegacyCrypto({ cipherKey: 'wrong-key', useRandomIVs: false }, primitives()); + const randomIV = new LegacyCrypto({ cipherKey: 'customKey', useRandomIVs: true }, primitives()); + + expect(await staticIV.decrypt(vector.staticIV)).to.equal(null); + expect(await randomIV.decrypt(vector.randomIV[0].slice(0, 20))).to.equal(null); + expect(await randomIV.decrypt('not base64 at all!')).to.equal(null); + expect(await randomIV.decrypt('')).to.equal(null); + }); + + it(`${name}: invalid UTF-8 plaintext`, async () => { + const instance = primitives(); + const crypto = new LegacyCrypto({ cipherKey: 'customKey', useRandomIVs: false }, instance); + // Derive legacy key the same way as cryptor does and encrypt invalid UTF-8 bytes. + const digest = Buffer.from(await instance.sha256(new TextEncoder().encode('customKey'))).toString('hex'); + const key = new TextEncoder().encode(digest.slice(0, 32)); + const iv = new TextEncoder().encode('0123456789012345'); + const ciphertext = await instance.aesCbcEncrypt(key, iv, new Uint8Array([0x22, 0xff, 0xfe, 0x22])); + + expect(await crypto.decrypt(Buffer.from(ciphertext).toString('base64'))).to.equal(null); + }); + + it(`${name}: leading BOM is not stripped (JSON.parse fails as with CryptoJS)`, async () => { + const instance = primitives(); + const crypto = new LegacyCrypto({ cipherKey: 'customKey', useRandomIVs: false }, instance); + const digest = Buffer.from(await instance.sha256(new TextEncoder().encode('customKey'))).toString('hex'); + const key = new TextEncoder().encode(digest.slice(0, 32)); + const iv = new TextEncoder().encode('0123456789012345'); + const ciphertext = await instance.aesCbcEncrypt(key, iv, new TextEncoder().encode('\uFEFF{"a":1}')); + + expect(await crypto.decrypt(Buffer.from(ciphertext).toString('base64'))).to.equal(null); + }); + }); + }); + + describe('crypto module errors keep "Decryption error" prefix', () => { + const vector = vectors.cryptoModule.find((v) => v.cipherKey === 'customKey')!; + + it('web AES-CBC cryptor with wrong key', async () => { + const module = WebCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + const error = await rejection(module.decrypt(vector.webAesCbc)); + + expect(error.message).to.match(/^Decryption error: /); + }); + + it('unknown header version (messages unchanged from v13)', async () => { + const data = new Uint8Array([...new TextEncoder().encode('PNED'), 2, ...new TextEncoder().encode('ACRH'), 0]); + const web = WebCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + const node = NodeCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }); + + expect((await rejection(web.decrypt(data.buffer))).message).to.equal('Decryption error: Unknown cryptor error'); + expect((await rejection(node.decrypt(data.buffer))).message).to.equal('unknown cryptor error'); + }); + + it('empty content', async () => { + for (const module of [ + WebCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }), + NodeCryptoModule.aesCbcCryptoModule({ cipherKey: 'enigma' }), + ]) { + const header = new Uint8Array([...new TextEncoder().encode('PNED'), 1, ...new TextEncoder().encode('ACRH'), 0]); + const error = await rejection(module.decrypt(header.buffer)); + expect(error.message).to.equal('Decryption error: empty content'); + } + }); + }); +}); diff --git a/test/unit/crypto/legacy-parity.test.ts b/test/unit/crypto/legacy-parity.test.ts new file mode 100644 index 000000000..22f3d1475 --- /dev/null +++ b/test/unit/crypto/legacy-parity.test.ts @@ -0,0 +1,127 @@ +/* global describe, it, afterEach */ + +import { expect } from 'chai'; + +import NobleCryptoPrimitives from '../../../src/crypto/primitives/react-native'; +import { CryptoPrimitives } from '../../../src/core/interfaces/crypto-primitives'; +import LegacyCrypto from '../../../src/core/components/cryptography/index'; +import NodeCryptoPrimitives from '../../../src/crypto/primitives/node'; +import WebCryptoPrimitives from '../../../src/crypto/primitives/web'; +import vectors from '../../fixtures/crypto/legacy-vectors'; + +/** + * Golden vectors were generated with CryptoJS (SDK v13). Every native implementation must produce and accept + * exactly the same ciphertexts. + */ +const implementations: Record CryptoPrimitives> = { + web: () => new WebCryptoPrimitives(), + node: () => new NodeCryptoPrimitives(), + noble: () => new NobleCryptoPrimitives(), +}; + +describe('legacy crypto parity with CryptoJS', () => { + const originalRandom = Math.random; + + afterEach(() => { + Math.random = originalRandom; + }); + + Object.entries(implementations).forEach(([name, primitives]) => { + describe(`${name} primitives`, () => { + it('produces byte-identical ciphertext with static IV', async () => { + for (const vector of vectors.legacy) { + const crypto = new LegacyCrypto({ cipherKey: vector.cipherKey, useRandomIVs: false }, primitives()); + expect(await crypto.encrypt(vector.plaintext), vector.plaintext.slice(0, 40)).to.equal(vector.staticIV); + } + }); + + it('decrypts CryptoJS static IV ciphertext', async () => { + for (const vector of vectors.legacy) { + const crypto = new LegacyCrypto({ cipherKey: vector.cipherKey, useRandomIVs: false }, primitives()); + expect(await crypto.decrypt(vector.staticIV)).to.deep.equal(JSON.parse(vector.plaintext)); + } + }); + + it('decrypts CryptoJS random IV ciphertext', async () => { + for (const vector of vectors.legacy) { + const crypto = new LegacyCrypto({ cipherKey: vector.cipherKey, useRandomIVs: true }, primitives()); + for (const ciphertext of vector.randomIV) + expect(await crypto.decrypt(ciphertext)).to.deep.equal(JSON.parse(vector.plaintext)); + } + }); + + it('supports custom cipher key per call', async () => { + const vector = vectors.legacy.find((v) => v.cipherKey === 'enigma')!; + const crypto = new LegacyCrypto({ cipherKey: 'other-key', useRandomIVs: false }, primitives()); + + expect(await crypto.encrypt(vector.plaintext, 'enigma')).to.equal(vector.staticIV); + expect(await crypto.decrypt(vector.staticIV, 'enigma')).to.deep.equal(JSON.parse(vector.plaintext)); + }); + + it('uses secure random IVs (not Math.random)', async () => { + Math.random = () => { + throw new Error('Math.random must not be used for IV generation'); + }; + const crypto = new LegacyCrypto({ cipherKey: 'customKey', useRandomIVs: true }, primitives()); + const first = await crypto.encrypt('{"hi":"there"}'); + const second = await crypto.encrypt('{"hi":"there"}'); + + expect(first).to.not.equal(second); + expect(Buffer.from(first, 'base64').subarray(0, 16)).to.not.deep.equal( + Buffer.from(second, 'base64').subarray(0, 16), + ); + }); + + it('stringifies non-string input before encryption', async () => { + const crypto = new LegacyCrypto({ cipherKey: 'customKey', useRandomIVs: false }, primitives()); + expect(await crypto.encrypt({ hi: 'there' })).to.equal('TejX6F2JNqH/gIiGHWN4Cw=='); + }); + + it('returns data as-is when cipher key not set', async () => { + const crypto = new LegacyCrypto({ useRandomIVs: false }, primitives()); + + expect(await crypto.encrypt('{"hi":"there"}')).to.equal('{"hi":"there"}'); + expect(await crypto.decrypt('{"hi":"there"}')).to.equal('{"hi":"there"}'); + }); + }); + }); + + describe('cross-implementation compatibility', () => { + const names = Object.keys(implementations); + + names.forEach((encryptWith) => + names.forEach((decryptWith) => { + it(`encrypts with ${encryptWith} and decrypts with ${decryptWith}`, async () => { + const payload = JSON.stringify({ text: 'Hello, δΈ–η•Œ! 🌍', list: [1, 2, 3], nested: { flag: true } }); + for (const useRandomIVs of [true, false]) { + const encryptor = new LegacyCrypto({ cipherKey: 'enigma', useRandomIVs }, implementations[encryptWith]()); + const decryptor = new LegacyCrypto({ cipherKey: 'enigma', useRandomIVs }, implementations[decryptWith]()); + + expect(await decryptor.decrypt(await encryptor.encrypt(payload))).to.deep.equal(JSON.parse(payload)); + } + }); + }), + ); + }); + + describe('custom encrypt / decrypt functions', () => { + it('supports synchronous and asynchronous functions', async () => { + const sync = new LegacyCrypto( + { customEncrypt: (data) => `sync:${data}`, customDecrypt: (data) => ({ decrypted: data }) }, + new NodeCryptoPrimitives(), + ); + const async = new LegacyCrypto( + { + customEncrypt: async (data) => `async:${data}`, + customDecrypt: async (data) => ({ decrypted: data }), + }, + new NodeCryptoPrimitives(), + ); + + expect(await sync.encrypt('data')).to.equal('sync:data'); + expect(await sync.decrypt('data')).to.deep.equal({ decrypted: 'data' }); + expect(await async.encrypt('data')).to.equal('async:data'); + expect(await async.decrypt('data')).to.deep.equal({ decrypted: 'data' }); + }); + }); +}); diff --git a/test/unit/crypto/react-native-primitives.test.ts b/test/unit/crypto/react-native-primitives.test.ts new file mode 100644 index 000000000..9686f816d --- /dev/null +++ b/test/unit/crypto/react-native-primitives.test.ts @@ -0,0 +1,57 @@ +/* global describe, it, afterEach */ +/* eslint-disable @typescript-eslint/no-require-imports */ + +import { expect } from 'chai'; +import Module from 'module'; + +import NobleCryptoPrimitives from '../../../src/crypto/primitives/react-native'; + +describe('React Native noble primitives', () => { + const cryptoDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'crypto'); + + afterEach(() => { + if (cryptoDescriptor) Object.defineProperty(globalThis, 'crypto', cryptoDescriptor); + }); + + it('generates random bytes with crypto.getRandomValues', () => { + const primitives = new NobleCryptoPrimitives(); + const first = primitives.randomBytes(16); + + expect(first).to.have.length(16); + expect(first).to.not.deep.equal(primitives.randomBytes(16)); + }); + + it('throws helpful error when secure random generator is missing (no Math.random fallback)', () => { + Object.defineProperty(globalThis, 'crypto', { value: {}, configurable: true, writable: true }); + + expect(() => new NobleCryptoPrimitives().randomBytes(16)).to.throw(/react-native-get-random-values/); + }); + + it('can be constructed without @noble packages and fails with install hint on first use', async () => { + const modulePath = require.resolve('../../../src/crypto/primitives/react-native'); + const moduleLoader = Module as unknown as { _load: (request: string, ...args: unknown[]) => unknown }; + const originalLoad = moduleLoader._load; + const cached = require.cache[modulePath]; + + delete require.cache[modulePath]; + moduleLoader._load = function (request: string, ...args: unknown[]) { + if (request.startsWith('@noble/')) throw new Error(`Cannot find module '${request}'`); + return originalLoad.call(this, request, ...args); + }; + + try { + const { default: IsolatedPrimitives } = require(modulePath) as { default: typeof NobleCryptoPrimitives }; + const primitives = new IsolatedPrimitives(); + + try { + await primitives.sha256(new Uint8Array([1])); + expect.fail('Expected rejection'); + } catch (error) { + expect((error as Error).message).to.include('npm install @noble/ciphers @noble/hashes'); + } + } finally { + moduleLoader._load = originalLoad; + if (cached) require.cache[modulePath] = cached; + } + }); +}); diff --git a/test/unit/crypto/request-signature.test.ts b/test/unit/crypto/request-signature.test.ts new file mode 100644 index 000000000..16296f983 --- /dev/null +++ b/test/unit/crypto/request-signature.test.ts @@ -0,0 +1,12 @@ +/* global describe, it */ + +import { expect } from 'chai'; + +import { hmacSha256 } from '../../../src/crypto/primitives/node'; +import vectors from '../../fixtures/crypto/legacy-vectors'; + +describe('request signature HMAC-SHA256 parity with CryptoJS', () => { + it('produces identical base64 digests', () => { + for (const vector of vectors.hmac) expect(hmacSha256(vector.input, vector.secretKey)).to.equal(vector.signature); + }); +}); diff --git a/test/unit/publish/publish.test.ts b/test/unit/publish/publish.test.ts index 2655f3585..02f77631a 100644 --- a/test/unit/publish/publish.test.ts +++ b/test/unit/publish/publish.test.ts @@ -240,13 +240,13 @@ describe('PublishRequest', () => { describe('encryption handling', () => { const mockCryptoModule: ICryptoModule = { set logger(_logger: any) {}, - encrypt: (data: string) => `encrypted_${data}`, - decrypt: (data: string | ArrayBuffer) => data.toString().replace('encrypted_', ''), + encrypt: async (data: string | ArrayBuffer) => `encrypted_${data}`, + decrypt: async (data: string | ArrayBuffer) => data.toString().replace('encrypted_', ''), encryptFile: undefined as any, decryptFile: undefined as any, }; - it('should encrypt message with cryptoModule for GET', () => { + it('should encrypt message with cryptoModule for GET', async () => { const testMessage = { secret: 'data' }; const request = new PublishRequest({ ...defaultParameters, @@ -255,12 +255,13 @@ describe('PublishRequest', () => { sendByPost: false, }); + await request.prepare(); const transportRequest = request.request(); const expectedEncryptedMessage = JSON.stringify(`encrypted_${JSON.stringify(testMessage)}`); assert(transportRequest.path.includes(encodeURIComponent(expectedEncryptedMessage))); }); - it('should encrypt message with cryptoModule for POST', () => { + it('should encrypt message with cryptoModule for POST', async () => { const testMessage = { secret: 'data' }; const request = new PublishRequest({ ...defaultParameters, @@ -269,15 +270,16 @@ describe('PublishRequest', () => { sendByPost: true, }); + await request.prepare(); const transportRequest = request.request(); const expectedEncryptedMessage = JSON.stringify(`encrypted_${JSON.stringify(testMessage)}`); assert.equal(transportRequest.body, expectedEncryptedMessage); }); - it('should handle ArrayBuffer encryption result', () => { + it('should handle ArrayBuffer encryption result', async () => { const arrayBufferCrypto: ICryptoModule = { set logger(_logger: any) {}, - encrypt: (data: string) => { + encrypt: async () => { const buffer = new ArrayBuffer(8); const view = new Uint8Array(buffer); view.set([1, 2, 3, 4, 5, 6, 7, 8]); @@ -294,13 +296,14 @@ describe('PublishRequest', () => { sendByPost: true, }); + await request.prepare(); const transportRequest = request.request(); const expectedEncodedBuffer = JSON.stringify(encode(new ArrayBuffer(8))); // We can't easily compare ArrayBuffer content, so just verify it's a string assert.equal(typeof transportRequest.body, 'string'); }); - it('should handle encryption failure', () => { + it('should handle encryption failure', async () => { const failingCrypto: ICryptoModule = { set logger(_logger: any) {}, encrypt: () => { @@ -311,12 +314,13 @@ describe('PublishRequest', () => { decryptFile: undefined as any, }; - assert.throws(() => { - new PublishRequest({ - ...defaultParameters, - crypto: failingCrypto, - }).request(); - }, /Encryption failed/); + // Encryption happens in `prepare()` (awaited by `sendRequest()` before `request()` is built). + const request = new PublishRequest({ + ...defaultParameters, + crypto: failingCrypto, + }); + + await assert.rejects(async () => request.prepare(), /Encryption failed/); }); }); diff --git a/tsconfig.rollup.json b/tsconfig.rollup.json index 6858ce22c..f2988186c 100644 --- a/tsconfig.rollup.json +++ b/tsconfig.rollup.json @@ -19,6 +19,7 @@ "node_modules", "test", "src/file/modules/node.ts", + "src/crypto/primitives/react-native.ts", "src/file/modules/react-native.ts", "src/node/**/*", "src/react_native/**/*",