UNPKG

rox-serverless

Version:

CloudBees Feature Management ROX JS SDK for serverless

1,448 lines (1,384 loc) 62.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const levels = { debug: 0, info: 1, warn: 2, error: 3 }; let level = 'error'; class Logger { constructor() { this.debug = (data, ...args) => { if (levels[level] <= levels.debug && console /* console not present on old IEs */) { console.log(data, ...args); // eslint-disable-line no-console } }; this.info = (data, ...args) => { if (levels[level] <= levels.info && console /* console not present on old IEs */) { console.info(data, ...args); // eslint-disable-line no-console } }; this.warn = (data, ...args) => { if (levels[level] <= levels.warn && console /* console not present on old IEs */) { console.warn(data, ...args); // eslint-disable-line no-console } }; this.error = (data, ...args) => { if (console /* console not present on old IEs */) { console.error(data, ...args); // eslint-disable-line no-console } }; this.setVerboseMode = debugLevel => { if (debugLevel === 'verbose') { level = 'debug'; this.debug('Active verbose mode'); } else { level = 'error'; } }; this.setLogger = (newLogger) => { logger = newLogger; }; } } let logger = new Logger(); var RoxLogger = logger; const ExceptionTrigger = { DYNAMIC_PROPERTIES_RULE: 'DYNAMIC_PROPERTIES_RULE', CONFIGURATION_FETCHED_HANDLER: 'CONFIGURATION_FETCHED_HANDLER', IMPRESSION_HANDLER: 'IMPRESSION_HANDLER', CUSTOM_PROPERTY_GENERATOR: 'CUSTOM_PROPERTY_GENERATOR' }; class UserspaceUnhandledErrorInvoker { invoke(exceptionTrigger, error) { if (!this.userUnhandledErrorHandler) { RoxLogger.error('User Unhandled Error Occured, no fallback handler was set, exception ignored.', error); return; } try { this.userUnhandledErrorHandler(exceptionTrigger, error); } catch (err) { RoxLogger.error('User Unhandled Error Handler itself threw an exception. original exception:' + error, err); } } setHandler(handler) { if (!(handler instanceof Function)) { RoxLogger.warn('UserspaceUnhandledErrorHandler must be a function. default will be used.'); return; } this.userUnhandledErrorHandler = handler; } } // wanted to create with new on client.js and pass it as an object, but 'new RoxParser()' appears in too many places :( var globalUserUnhandledErrorInvoker = new UserspaceUnhandledErrorInvoker(); class DeploymentConfiguration { constructor(condition) { this.condition = condition; } } class Experiment { constructor(identifier, name, archived, sticky, deploymentConfiguration, flags, labels, stickinessProperty) { this.identifier = identifier; this.name = name; this.archived = archived; this.sticky = sticky; this.deploymentConfiguration = deploymentConfiguration; this.flags = flags; this.labels = labels; this.stickinessProperty = stickinessProperty; } } class TargetGroup { constructor(identifier, condition) { this.identifier = identifier; this.condition = condition; } } class StringTokenizer { constructor(string, delimiters, returnDelim) { this._string = string; this._delimiters = delimiters; this._returnDelim = returnDelim; this._position = 0; } countTokens() { let count = 0; let inToken = false; for (let i = this._position, length = this._string.length; i < length; i++) { if (this._delimiters.indexOf(this._string.charAt(i)) != -1) { if (this._returnDelim) count++; if (inToken) { count++; inToken = false; } } else { inToken = true; } } if (inToken) count++; return count; } hasMoreElements() { return this.hasMoreTokens(); } hasMoreTokens() { if (!this._delimiters) { return false; } const length = this._string.length; if (this._position < length) { if (this._returnDelim) return true; for (let i = this._position; i < length; i++) { if (this._delimiters.indexOf(this._string.charAt(i)) == -1) { return true; } } } return false; } nextElement() { return this.nextToken(); } nextToken() { if (!this._delimiters) { return undefined; } let i = this._position; const length = this._string.length; if (i < length) { if (this._returnDelim) { if (this._delimiters.indexOf(this._string.charAt(this._position)) != -1) return this._string.charAt(this._position++); for (this._position++; this._position < length; this._position++) if (this._delimiters.indexOf(this._string.charAt(this._position)) != -1) return this._string.substr(i, this._position - i); return this._string.substr(i); } while (i < length && this._delimiters.indexOf(this._string.charAt(this._position)) != -1) i++; this._position = i; if (i < length) { for (this._position++; this._position < length; this._position++) if (this._delimiters.indexOf(this._string.charAt(this._position)) != -1) return this._string.substr(i, this._position - i); return this._string.substr(i); } } return undefined; } nextTokenWithDelimiters(delimiters) { this._delimiters = delimiters; return this.nextToken(); } } class TargetGroupRepository { constructor() { this.map = {}; } addTargetGroup(targetGroup) { this.map[targetGroup.identifier] = targetGroup; } setTargetGroups(targetGroups) { this.map = {}; targetGroups = targetGroups || []; targetGroups.forEach(function (element) { this.map[element.identifier] = element; }, this); } targetGroupWithName(name) { return this.map[name]; } get targetGroups() { return Object.keys(this.map).map(t => this.map[t]); } } const instance$1 = new TargetGroupRepository(); class CustomPropertyRepository { constructor() { this.store = new Map(); } has(property) { return this.store.has(property.name); } get(name) { return this.store.get(name); } set(property) { this.store.set(property.name, property); } setIfNotExists(property) { if (this.has(property)) { return; } this.set(property); } clear() { this.store.clear(); } get items() { return Array.from(this.store.values()); } } var customPropsRepository = new CustomPropertyRepository(); class FlagsSetter { constructor(flagRepo, experimentsRepo) { this.flagRepo = flagRepo; this.experimentsRepo = experimentsRepo; } prepareFlagsWithExperiments() { const experiments = this.experimentsRepo.experiments || []; RoxLogger.debug(`Set experiments ${JSON.stringify(experiments)}`); const flagsWithCondition = []; experiments.forEach((experiment) => { if (!experiment) { return; } experiment.flags.forEach((flag) => { if (!flag) { return; } const flagObject = this.flagRepo.flagWithName(flag.name); if (flagObject) { flagsWithCondition.push(flagObject); this.connectExperimentToFlag(flagObject, experiment.deploymentConfiguration.condition); } }); }); this.flagRepo.flags.forEach((flagToSet) => { if (!flagsWithCondition.some((f) => f === flagToSet)) { this.connectExperimentToFlag(flagToSet, undefined); } }); } setAddedFlag(addedFlag) { const experiment = this.experimentsRepo.experimentForFlag(addedFlag); if (experiment) { this.connectExperimentToFlag(addedFlag, experiment.deploymentConfiguration.condition); } } connectExperimentToFlag(flag, condition) { flag.condition = condition; } } class ExperimentsRepository { constructor() { this.map = {}; } setExperiments(experiments) { this.map = {}; experiments = experiments || []; experiments.forEach(function (element) { this.map[element.identifier] = element; }, this); } experimentWithName(name) { return this.map[name]; } get experiments() { return Object.keys(this.map).map(t => this.map[t]); } experimentForFlagName(flagName) { return this.experiments.find(e => e.flags && e.flags.some(f => f.name === flagName)); } experimentForFlag(flag) { return this.experimentForFlagName(flag.name); } } const instance = new ExperimentsRepository(); class RoxFlagRepository { constructor() { this.map = {}; } addFlag(name, flag) { flag.name = name; this.map[name] = flag; new FlagsSetter(this, instance).setAddedFlag(flag); } flagWithName(name) { return this.map[name]; } get flags() { return Object.keys(this.map).map(t => this.map[t]); } get items() { return this.flags; } } var FlagsRepository = new RoxFlagRepository(); var md5$1 = {exports: {}}; var crypt = {exports: {}}; (function() { var base64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', crypt$1 = { // Bit-wise rotation left rotl: function(n, b) { return (n << b) | (n >>> (32 - b)); }, // Bit-wise rotation right rotr: function(n, b) { return (n << (32 - b)) | (n >>> b); }, // Swap big-endian to little-endian and vice versa endian: function(n) { // If number given, swap endian if (n.constructor == Number) { return crypt$1.rotl(n, 8) & 0x00FF00FF | crypt$1.rotl(n, 24) & 0xFF00FF00; } // Else, assume array and swap all items for (var i = 0; i < n.length; i++) n[i] = crypt$1.endian(n[i]); return n; }, // Generate an array of any length of random bytes randomBytes: function(n) { for (var bytes = []; n > 0; n--) bytes.push(Math.floor(Math.random() * 256)); return bytes; }, // Convert a byte array to big-endian 32-bit words bytesToWords: function(bytes) { for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) words[b >>> 5] |= bytes[i] << (24 - b % 32); return words; }, // Convert big-endian 32-bit words to a byte array wordsToBytes: function(words) { for (var bytes = [], b = 0; b < words.length * 32; b += 8) bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); return bytes; }, // Convert a byte array to a hex string bytesToHex: function(bytes) { for (var hex = [], i = 0; i < bytes.length; i++) { hex.push((bytes[i] >>> 4).toString(16)); hex.push((bytes[i] & 0xF).toString(16)); } return hex.join(''); }, // Convert a hex string to a byte array hexToBytes: function(hex) { for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16)); return bytes; }, // Convert a byte array to a base-64 string bytesToBase64: function(bytes) { for (var base64 = [], i = 0; i < bytes.length; i += 3) { var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; for (var j = 0; j < 4; j++) if (i * 8 + j * 6 <= bytes.length * 8) base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)); else base64.push('='); } return base64.join(''); }, // Convert a base-64 string to a byte array base64ToBytes: function(base64) { // Remove non-base-64 characters base64 = base64.replace(/[^A-Z0-9+\/]/ig, ''); for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) { if (imod4 == 0) continue; bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))); } return bytes; } }; crypt.exports = crypt$1; })(); var charenc = { // UTF-8 encoding utf8: { // Convert a string to a byte array stringToBytes: function(str) { return charenc.bin.stringToBytes(unescape(encodeURIComponent(str))); }, // Convert a byte array to a string bytesToString: function(bytes) { return decodeURIComponent(escape(charenc.bin.bytesToString(bytes))); } }, // Binary encoding bin: { // Convert a string to a byte array stringToBytes: function(str) { for (var bytes = [], i = 0; i < str.length; i++) bytes.push(str.charCodeAt(i) & 0xFF); return bytes; }, // Convert a byte array to a string bytesToString: function(bytes) { for (var str = [], i = 0; i < bytes.length; i++) str.push(String.fromCharCode(bytes[i])); return str.join(''); } } }; var charenc_1 = charenc; /*! * Determine if an object is a Buffer * * @author Feross Aboukhadijeh <https://feross.org> * @license MIT */ // The _isBuffer check is for Safari 5-7 support, because it's missing // Object.prototype.constructor. Remove this eventually var isBuffer_1 = function (obj) { return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) }; function isBuffer (obj) { return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) } // For Node v0.10 support. Remove this eventually. function isSlowBuffer (obj) { return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) } (function(){ var crypt$1 = crypt.exports, utf8 = charenc_1.utf8, isBuffer = isBuffer_1, bin = charenc_1.bin, // The core md5 = function (message, options) { // Convert to byte array if (message.constructor == String) if (options && options.encoding === 'binary') message = bin.stringToBytes(message); else message = utf8.stringToBytes(message); else if (isBuffer(message)) message = Array.prototype.slice.call(message, 0); else if (!Array.isArray(message) && message.constructor !== Uint8Array) message = message.toString(); // else, assume byte array already var m = crypt$1.bytesToWords(message), l = message.length * 8, a = 1732584193, b = -271733879, c = -1732584194, d = 271733878; // Swap endian for (var i = 0; i < m.length; i++) { m[i] = ((m[i] << 8) | (m[i] >>> 24)) & 0x00FF00FF | ((m[i] << 24) | (m[i] >>> 8)) & 0xFF00FF00; } // Padding m[l >>> 5] |= 0x80 << (l % 32); m[(((l + 64) >>> 9) << 4) + 14] = l; // Method shortcuts var FF = md5._ff, GG = md5._gg, HH = md5._hh, II = md5._ii; for (var i = 0; i < m.length; i += 16) { var aa = a, bb = b, cc = c, dd = d; a = FF(a, b, c, d, m[i+ 0], 7, -680876936); d = FF(d, a, b, c, m[i+ 1], 12, -389564586); c = FF(c, d, a, b, m[i+ 2], 17, 606105819); b = FF(b, c, d, a, m[i+ 3], 22, -1044525330); a = FF(a, b, c, d, m[i+ 4], 7, -176418897); d = FF(d, a, b, c, m[i+ 5], 12, 1200080426); c = FF(c, d, a, b, m[i+ 6], 17, -1473231341); b = FF(b, c, d, a, m[i+ 7], 22, -45705983); a = FF(a, b, c, d, m[i+ 8], 7, 1770035416); d = FF(d, a, b, c, m[i+ 9], 12, -1958414417); c = FF(c, d, a, b, m[i+10], 17, -42063); b = FF(b, c, d, a, m[i+11], 22, -1990404162); a = FF(a, b, c, d, m[i+12], 7, 1804603682); d = FF(d, a, b, c, m[i+13], 12, -40341101); c = FF(c, d, a, b, m[i+14], 17, -1502002290); b = FF(b, c, d, a, m[i+15], 22, 1236535329); a = GG(a, b, c, d, m[i+ 1], 5, -165796510); d = GG(d, a, b, c, m[i+ 6], 9, -1069501632); c = GG(c, d, a, b, m[i+11], 14, 643717713); b = GG(b, c, d, a, m[i+ 0], 20, -373897302); a = GG(a, b, c, d, m[i+ 5], 5, -701558691); d = GG(d, a, b, c, m[i+10], 9, 38016083); c = GG(c, d, a, b, m[i+15], 14, -660478335); b = GG(b, c, d, a, m[i+ 4], 20, -405537848); a = GG(a, b, c, d, m[i+ 9], 5, 568446438); d = GG(d, a, b, c, m[i+14], 9, -1019803690); c = GG(c, d, a, b, m[i+ 3], 14, -187363961); b = GG(b, c, d, a, m[i+ 8], 20, 1163531501); a = GG(a, b, c, d, m[i+13], 5, -1444681467); d = GG(d, a, b, c, m[i+ 2], 9, -51403784); c = GG(c, d, a, b, m[i+ 7], 14, 1735328473); b = GG(b, c, d, a, m[i+12], 20, -1926607734); a = HH(a, b, c, d, m[i+ 5], 4, -378558); d = HH(d, a, b, c, m[i+ 8], 11, -2022574463); c = HH(c, d, a, b, m[i+11], 16, 1839030562); b = HH(b, c, d, a, m[i+14], 23, -35309556); a = HH(a, b, c, d, m[i+ 1], 4, -1530992060); d = HH(d, a, b, c, m[i+ 4], 11, 1272893353); c = HH(c, d, a, b, m[i+ 7], 16, -155497632); b = HH(b, c, d, a, m[i+10], 23, -1094730640); a = HH(a, b, c, d, m[i+13], 4, 681279174); d = HH(d, a, b, c, m[i+ 0], 11, -358537222); c = HH(c, d, a, b, m[i+ 3], 16, -722521979); b = HH(b, c, d, a, m[i+ 6], 23, 76029189); a = HH(a, b, c, d, m[i+ 9], 4, -640364487); d = HH(d, a, b, c, m[i+12], 11, -421815835); c = HH(c, d, a, b, m[i+15], 16, 530742520); b = HH(b, c, d, a, m[i+ 2], 23, -995338651); a = II(a, b, c, d, m[i+ 0], 6, -198630844); d = II(d, a, b, c, m[i+ 7], 10, 1126891415); c = II(c, d, a, b, m[i+14], 15, -1416354905); b = II(b, c, d, a, m[i+ 5], 21, -57434055); a = II(a, b, c, d, m[i+12], 6, 1700485571); d = II(d, a, b, c, m[i+ 3], 10, -1894986606); c = II(c, d, a, b, m[i+10], 15, -1051523); b = II(b, c, d, a, m[i+ 1], 21, -2054922799); a = II(a, b, c, d, m[i+ 8], 6, 1873313359); d = II(d, a, b, c, m[i+15], 10, -30611744); c = II(c, d, a, b, m[i+ 6], 15, -1560198380); b = II(b, c, d, a, m[i+13], 21, 1309151649); a = II(a, b, c, d, m[i+ 4], 6, -145523070); d = II(d, a, b, c, m[i+11], 10, -1120210379); c = II(c, d, a, b, m[i+ 2], 15, 718787259); b = II(b, c, d, a, m[i+ 9], 21, -343485551); a = (a + aa) >>> 0; b = (b + bb) >>> 0; c = (c + cc) >>> 0; d = (d + dd) >>> 0; } return crypt$1.endian([a, b, c, d]); }; // Auxiliary functions md5._ff = function (a, b, c, d, x, s, t) { var n = a + (b & c | ~b & d) + (x >>> 0) + t; return ((n << s) | (n >>> (32 - s))) + b; }; md5._gg = function (a, b, c, d, x, s, t) { var n = a + (b & d | c & ~d) + (x >>> 0) + t; return ((n << s) | (n >>> (32 - s))) + b; }; md5._hh = function (a, b, c, d, x, s, t) { var n = a + (b ^ c ^ d) + (x >>> 0) + t; return ((n << s) | (n >>> (32 - s))) + b; }; md5._ii = function (a, b, c, d, x, s, t) { var n = a + (c ^ (b | ~d)) + (x >>> 0) + t; return ((n << s) | (n >>> (32 - s))) + b; }; // Package private blocksize md5._blocksize = 16; md5._digestsize = 16; md5$1.exports = function (message, options) { if (message === undefined || message === null) throw new Error('Illegal argument ' + message); var digestbytes = crypt$1.wordsToBytes(md5(message, options)); return options && options.asBytes ? digestbytes : options && options.asString ? bin.bytesToString(digestbytes) : crypt$1.bytesToHex(digestbytes); }; })(); /** * base64.ts * * Licensed under the BSD 3-Clause License. * http://opensource.org/licenses/BSD-3-Clause * * References: * http://en.wikipedia.org/wiki/Base64 * * @author Dan Kogai (https://github.com/dankogai) */ const _hasatob = typeof atob === 'function'; const _hasbtoa = typeof btoa === 'function'; const _hasBuffer = typeof Buffer === 'function'; const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined; const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined; const b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; const b64chs = Array.prototype.slice.call(b64ch); const b64tab = ((a) => { let tab = {}; a.forEach((c, i) => tab[c] = i); return tab; })(b64chs); const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/; const _fromCC = String.fromCharCode.bind(String); const _U8Afrom = typeof Uint8Array.from === 'function' ? Uint8Array.from.bind(Uint8Array) : (it, fn = (x) => x) => new Uint8Array(Array.prototype.slice.call(it, 0).map(fn)); const _mkUriSafe = (src) => src .replace(/=/g, '').replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_'); const _tidyB64 = (s) => s.replace(/[^A-Za-z0-9\+\/]/g, ''); /** * polyfill version of `btoa` */ const btoaPolyfill = (bin) => { // console.log('polyfilled'); let u32, c0, c1, c2, asc = ''; const pad = bin.length % 3; for (let i = 0; i < bin.length;) { if ((c0 = bin.charCodeAt(i++)) > 255 || (c1 = bin.charCodeAt(i++)) > 255 || (c2 = bin.charCodeAt(i++)) > 255) throw new TypeError('invalid character found'); u32 = (c0 << 16) | (c1 << 8) | c2; asc += b64chs[u32 >> 18 & 63] + b64chs[u32 >> 12 & 63] + b64chs[u32 >> 6 & 63] + b64chs[u32 & 63]; } return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc; }; /** * does what `window.btoa` of web browsers do. * @param {String} bin binary string * @returns {string} Base64-encoded string */ const _btoa = _hasbtoa ? (bin) => btoa(bin) : _hasBuffer ? (bin) => Buffer.from(bin, 'binary').toString('base64') : btoaPolyfill; const _fromUint8Array = _hasBuffer ? (u8a) => Buffer.from(u8a).toString('base64') : (u8a) => { // cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326 const maxargs = 0x1000; let strs = []; for (let i = 0, l = u8a.length; i < l; i += maxargs) { strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs))); } return _btoa(strs.join('')); }; // This trick is found broken https://github.com/dankogai/js-base64/issues/130 // const utob = (src: string) => unescape(encodeURIComponent(src)); // reverting good old fationed regexp const cb_utob = (c) => { if (c.length < 2) { var cc = c.charCodeAt(0); return cc < 0x80 ? c : cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6)) + _fromCC(0x80 | (cc & 0x3f))) : (_fromCC(0xe0 | ((cc >>> 12) & 0x0f)) + _fromCC(0x80 | ((cc >>> 6) & 0x3f)) + _fromCC(0x80 | (cc & 0x3f))); } else { var cc = 0x10000 + (c.charCodeAt(0) - 0xD800) * 0x400 + (c.charCodeAt(1) - 0xDC00); return (_fromCC(0xf0 | ((cc >>> 18) & 0x07)) + _fromCC(0x80 | ((cc >>> 12) & 0x3f)) + _fromCC(0x80 | ((cc >>> 6) & 0x3f)) + _fromCC(0x80 | (cc & 0x3f))); } }; const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g; /** * @deprecated should have been internal use only. * @param {string} src UTF-8 string * @returns {string} UTF-16 string */ const utob = (u) => u.replace(re_utob, cb_utob); // const _encode = _hasBuffer ? (s) => Buffer.from(s, 'utf8').toString('base64') : _TE ? (s) => _fromUint8Array(_TE.encode(s)) : (s) => _btoa(utob(s)); /** * converts a UTF-8-encoded string to a Base64 string. * @param {boolean} [urlsafe] if `true` make the result URL-safe * @returns {string} Base64 string */ const encode = (src, urlsafe = false) => urlsafe ? _mkUriSafe(_encode(src)) : _encode(src); // This trick is found broken https://github.com/dankogai/js-base64/issues/130 // const btou = (src: string) => decodeURIComponent(escape(src)); // reverting good old fationed regexp const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g; const cb_btou = (cccc) => { switch (cccc.length) { case 4: var cp = ((0x07 & cccc.charCodeAt(0)) << 18) | ((0x3f & cccc.charCodeAt(1)) << 12) | ((0x3f & cccc.charCodeAt(2)) << 6) | (0x3f & cccc.charCodeAt(3)), offset = cp - 0x10000; return (_fromCC((offset >>> 10) + 0xD800) + _fromCC((offset & 0x3FF) + 0xDC00)); case 3: return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12) | ((0x3f & cccc.charCodeAt(1)) << 6) | (0x3f & cccc.charCodeAt(2))); default: return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6) | (0x3f & cccc.charCodeAt(1))); } }; /** * @deprecated should have been internal use only. * @param {string} src UTF-16 string * @returns {string} UTF-8 string */ const btou = (b) => b.replace(re_btou, cb_btou); /** * polyfill version of `atob` */ const atobPolyfill = (asc) => { // console.log('polyfilled'); asc = asc.replace(/\s+/g, ''); if (!b64re.test(asc)) throw new TypeError('malformed base64.'); asc += '=='.slice(2 - (asc.length & 3)); let u24, bin = '', r1, r2; for (let i = 0; i < asc.length;) { u24 = b64tab[asc.charAt(i++)] << 18 | b64tab[asc.charAt(i++)] << 12 | (r1 = b64tab[asc.charAt(i++)]) << 6 | (r2 = b64tab[asc.charAt(i++)]); bin += r1 === 64 ? _fromCC(u24 >> 16 & 255) : r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255) : _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255); } return bin; }; /** * does what `window.atob` of web browsers do. * @param {String} asc Base64-encoded string * @returns {string} binary string */ const _atob = _hasatob ? (asc) => atob(_tidyB64(asc)) : _hasBuffer ? (asc) => Buffer.from(asc, 'base64').toString('binary') : atobPolyfill; // const _toUint8Array = _hasBuffer ? (a) => _U8Afrom(Buffer.from(a, 'base64')) : (a) => _U8Afrom(_atob(a), c => c.charCodeAt(0)); // const _decode = _hasBuffer ? (a) => Buffer.from(a, 'base64').toString('utf8') : _TD ? (a) => _TD.decode(_toUint8Array(a)) : (a) => btou(_atob(a)); const _unURI = (a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/')); /** * converts a Base64 string to a UTF-8 string. * @param {String} src Base64 string. Both normal and URL-safe are supported * @returns {string} UTF-8 string */ const decode = (src) => _decode(_unURI(src)); const _defaultHandler = (propName, context) => (context ? context[propName] : undefined); let _handler = _defaultHandler; function DynamicPropertiesHandler() { return _handler; } function _versionCompare(v1, v2, options = { zeroExtend: true, lexicographical: true }) { const lexicographical = options && options.lexicographical; const zeroExtend = options && options.zeroExtend; let v1parts = v1.split('.'); let v2parts = v2.split('.'); function isValidPart(x) { return (lexicographical ? /[0-9A-Za-z_-]+$/ : /^\d+$/).test(x); } if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) { return NaN; } if (zeroExtend) { while (v1parts.length < v2parts.length) v1parts.push('0'); while (v2parts.length < v1parts.length) v2parts.push('0'); } if (!lexicographical) { v1parts = v1parts.map(Number); v2parts = v2parts.map(Number); } for (let i = 0; i < v1parts.length; ++i) { if (v2parts.length == i) { return 1; } if (v1parts[i] == v2parts[i]) { continue; } else if (v1parts[i] > v2parts[i]) { return 1; } else { return -1; } } if (v1parts.length != v2parts.length) { return -1; } return 0; } const getBucket = (seed) => { let hash = md5$1.exports(seed, { asBytes: true }); hash = ((hash[0] & 0xff) | ((hash[1] & 0xff) << 8) | ((hash[2] & 0xff) << 16) | ((hash[3] & 0xff) << 24)) >>> 0; const bucket = hash / (Math.pow(2, 32) - 1); return bucket; }; const isUndefined = (op) => op === undefined; const now = () => Date.now(); const and = (op1, op2) => op1 && op2; const or = (op1, op2) => op1 || op2; const ne = (op1, op2) => (isUndefined(op1) ? false : op1) !== (isUndefined(op2) ? false : op2); const eq = (op1, op2) => (isUndefined(op1) ? false : op1) === (isUndefined(op2) ? false : op2); const not = (op) => !op; const ifThen = (conditionExpression, trueExpression, falseExpression) => conditionExpression ? trueExpression : falseExpression; const lt = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'number') { op1 = Number(op1); if (isNaN(op1)) { return false; } } if (typeof op2 !== 'number') { op2 = Number(op2); if (isNaN(op2)) { return false; } } return op1 < op2; }; const lte = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'number') { op1 = Number(op1); if (isNaN(op1)) { return false; } } if (typeof op2 !== 'number') { op2 = Number(op2); if (isNaN(op2)) { return false; } } return op1 <= op2; }; const tsToNum = (op1) => { if (isUndefined(op1)) return undefined; if (op1 instanceof Date) { return op1.getTime() / 1000; // epoc is in seconds, getTime in miliseconds } return undefined; }; const gt = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'number') { op1 = Number(op1); if (isNaN(op1)) { return false; } } if (typeof op2 !== 'number') { op2 = Number(op2); if (isNaN(op2)) { return false; } } return op1 > op2; }; const numne = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'number') { op1 = Number(op1); if (isNaN(op1)) { return false; } } if (typeof op2 !== 'number') { op2 = Number(op2); if (isNaN(op2)) { return false; } } return op1 !== op2; }; const numeq = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'number') { op1 = Number(op1); if (isNaN(op1)) { return false; } } if (typeof op2 !== 'number') { op2 = Number(op2); if (isNaN(op2)) { return false; } } return op1 === op2; }; const gte = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'number') { op1 = Number(op1); if (isNaN(op1)) { return false; } } if (typeof op2 !== 'number') { op2 = Number(op2); if (isNaN(op2)) { return false; } } return op1 >= op2; }; const match = (op1, op2, op3) => { const text = op1; const regex = new RegExp(op2, op3); const match = regex.exec(text); if (match) { return true; } return false; }; const semverLt = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'string' || typeof op2 !== 'string') return false; return _versionCompare(op1, op2, { zeroExtend: true }) < 0; }; const semverLte = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'string' || typeof op2 !== 'string') return false; return _versionCompare(op1, op2, { zeroExtend: true }) <= 0; }; const semverGt = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'string' || typeof op2 !== 'string') return false; return _versionCompare(op1, op2, { zeroExtend: true }) > 0; }; const semverGte = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'string' || typeof op2 !== 'string') return false; return _versionCompare(op1, op2, { zeroExtend: true }) >= 0; }; const semverEq = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'string' || typeof op2 !== 'string') return false; return _versionCompare(op1, op2) == 0; }; const semverNe = (op1, op2) => { if (isUndefined(op1) || isUndefined(op2)) return false; if (typeof op1 !== 'string' || typeof op2 !== 'string') return false; return _versionCompare(op1, op2) != 0; }; const mergeSeed = (seed1, seed2) => seed1 + '.' + seed2; const isInPercentage = (percentage, seed) => { const bucket = getBucket(seed); const isInPercentage = bucket <= percentage; return isInPercentage; }; const isInPercentageRange = (percentageLow, percentageHigh, seed) => { const bucket = getBucket(seed); const isInPercentage = bucket >= percentageLow && bucket <= percentageHigh; return isInPercentage; }; const flagValue = (flagName, context = {}, internalContext = {}) => { const flag = FlagsRepository.flagWithName(flagName); if (flag) { const contextClone = Object.assign({}, internalContext); flag.getInternalValue(contextClone, context); if (!contextClone.isPeek && !contextClone.result.isOverride && !contextClone.result.isFreezed) { flag._flagImpression(contextClone.result.value, contextClone.result.usedContext); } return contextClone.result.value; } const exp = instance.experimentForFlagName(flagName); if (exp && exp.deploymentConfiguration) { const expressionValue = new RoxxParser().evaluateExpression(exp.deploymentConfiguration.condition, internalContext, context); return expressionValue || 'false'; } return 'false'; }; const isInTargetGroup = (targetGroup, context = {}, callContext = {}) => { const tg = instance$1.targetGroupWithName(targetGroup); if (tg) { return new RoxxParser().evaluateExpression(tg.condition, callContext, context); } else { return false; } }; const isTargetGroupPaired = () => false; const property = (propName, context = {}) => { const prop = customPropsRepository.get(propName); if (!prop) { const handler = DynamicPropertiesHandler && DynamicPropertiesHandler(); let handlerResult; if (handler) { if (!handler._isUserDefined) { handlerResult = handler(propName, context); } else { try { handlerResult = handler(propName, context); } catch (err) { err.isUserError = true; err.trigger = ExceptionTrigger.DYNAMIC_PROPERTIES_RULE; throw err; } } } return handlerResult; } else { return prop.getValue(context); } }; const inArray = (target, list) => (list ? list.includes(target) : false); const md5 = (str) => { if (typeof str !== 'string') { return undefined; } return md5$1.exports(str); }; const concat = (str1, str2) => { if (typeof str1 !== 'string' || typeof str2 !== 'string') { return undefined; } return `${str1}${str2}`; }; const b64d = (value) => { if (typeof value !== 'string') { return undefined; } return decodeURIComponent(decode(value)); }; const operatorsWithContext = [isInTargetGroup, flagValue, property]; var RoxxOperatorsMap = /*#__PURE__*/Object.freeze({ __proto__: null, isUndefined: isUndefined, now: now, and: and, or: or, ne: ne, eq: eq, not: not, ifThen: ifThen, lt: lt, lte: lte, tsToNum: tsToNum, gt: gt, numne: numne, numeq: numeq, gte: gte, match: match, semverLt: semverLt, semverLte: semverLte, semverGt: semverGt, semverGte: semverGte, semverEq: semverEq, semverNe: semverNe, mergeSeed: mergeSeed, isInPercentage: isInPercentage, isInPercentageRange: isInPercentageRange, flagValue: flagValue, isInTargetGroup: isInTargetGroup, isTargetGroupPaired: isTargetGroupPaired, property: property, inArray: inArray, md5: md5, concat: concat, b64d: b64d, operatorsWithContext: operatorsWithContext }); const operatorsList = Object.keys(RoxxOperatorsMap); const RoxxEscapedQuote = '\\"'; const RoxxEscapedQuotePlaceholder = '\\RO_Q'; const RoxxStringDelimiter = '"'; const RoxxArrayStartDelimiter = '['; const RoxxArrayEndDelimiter = ']'; const RoxxDictStartDelimiter = '{'; const RoxxDictEndDelimiter = '}'; const RoxxTokenDelimiters = '{}[]():, \t\r\n"'; /** * Type identifier for ope*rator* tokens */ const RoxxTokenTypeRator = 'operator'; /** * Type identifier for ope*rand* tokens */ const RoxxTokenTypeRand = 'operand'; /** * */ class RoxxTokenizer { constructor() { this.tokenArray = []; this.arrayAccumulator = undefined; this.dictionaryAccumulator = undefined; this.dictKey = undefined; } /** * Converts a string token into a Roxx type object. * @param {string} token - The token to convert into a Roxx type. * @returns {object} */ _stringToRoxx(token) { if (operatorsList.includes(token)) return { type: RoxxTokenTypeRator, value: token }; if (token == 'true') return { type: RoxxTokenTypeRand, value: true }; if (token == 'false') return { type: RoxxTokenTypeRand, value: false }; if (token == 'undefined') return { type: RoxxTokenTypeRand, value: undefined }; if (token.charAt(0) == '"' && token.charAt(token.length - 1) == '"') return { type: RoxxTokenTypeRand, value: token.substr(1, token.length - 2) }; if (!isNaN(token)) return { type: RoxxTokenTypeRand, value: +token }; // we will never write literal date, it will only come from a custom property (but if we want to, this suppose to work) // const tokenAsDate = Date.parse(token) // if (!isNaN(tokenAsDate)) return {type: RoxxTokenTypeRand, value: new Date(tokenAsDate)} return { type: 'UNKNOWN' }; } /** * Pushes a token into either the arrayAccumulator or the tokenArray * according to context. * @param {*} token - the token to push */ push(token) { if (this.dictionaryAccumulator && !this.dictKey) { this.dictKey = token.value; } else if (this.dictionaryAccumulator && this.dictKey) { this.dictionaryAccumulator[this.dictKey] = token.value; this.dictKey = undefined; } else if (this.arrayAccumulator) { this.arrayAccumulator.push(token.value); } else { this.tokenArray.push(token); } } /** * Produces a token array from an expression string. This array is later consumed by RoxxParser * @see RoxxParser * @param {string} expr - Roxx expression to tokenize * @returns {Array} */ tokenize(expr) { this.tokenArray = []; this.arrayAccumulator = undefined; this.dictionaryAccumulator = undefined; let delimitersToUse = RoxxTokenDelimiters; const expression = expr.replace(RoxxEscapedQuote, RoxxEscapedQuotePlaceholder); const tokenizer = new StringTokenizer(expression, delimitersToUse, true); let token, prevToken; while (tokenizer.hasMoreTokens()) { prevToken = token; token = tokenizer.nextTokenWithDelimiters(delimitersToUse); switch (token) { case RoxxDictStartDelimiter: this.dictionaryAccumulator = {}; break; case RoxxDictEndDelimiter: this.tokenArray.push({ type: RoxxTokenTypeRand, value: this.dictionaryAccumulator, }); this.dictionaryAccumulator = undefined; break; case RoxxArrayStartDelimiter: this.arrayAccumulator = []; break; case RoxxArrayEndDelimiter: this.tokenArray.push({ type: RoxxTokenTypeRand, value: this.arrayAccumulator }); this.arrayAccumulator = undefined; break; case RoxxStringDelimiter: if (prevToken == RoxxStringDelimiter) { // if previous token was also a string delimiter it means we encounterd an emptry string. this.push({ type: RoxxTokenTypeRand, value: '' }); } // Swap delimiters to use if needed. we do this so we can have strings with chars that are normally used as delimiters. delimitersToUse = delimitersToUse == RoxxStringDelimiter ? RoxxTokenDelimiters : RoxxStringDelimiter; break; default: if (delimitersToUse == RoxxStringDelimiter) { // If get a token that's not RoxxStringDelimiter while using RoxxStringDelimiter as delimiters It's the string value! this.push({ type: RoxxTokenTypeRand, value: token.replace(RoxxEscapedQuotePlaceholder, RoxxEscapedQuote), }); } else if (RoxxTokenDelimiters.indexOf(token) == -1) { // Is this token a delimiter or something else? if it's something else convert it to a roxx type and push. this.push(this._stringToRoxx(token)); } break; } } return this.tokenArray; } } const TARGET_URL_QUERY_PARAM_NAME = 'roxTargetUrl'; const AUTH_QUERY_PARAM_NAME = 'roxAuth'; class ProxyConfig { constructor(proxySettings) { this._proxyUrl = ''; this._proxyAuthHeader = ''; this._proxySettings = false; if (proxySettings) { this._proxySettings = Object.assign({}, proxySettings); // calculating constants this._proxyUrl = `${this._proxySettings.protocol}://${this._proxySettings.host}${this._proxySettings.port ? `:${this._proxySettings.port}` : ''}`; if (this._proxySettings.auth && this._proxySettings.auth.username && this._proxySettings.auth.password) { this._proxyAuthHeader = `Basic ${encode(`${this._proxySettings.auth.username}:${this._proxySettings.auth.password}`)}`; this._proxyAuthHeaderEncoded = encodeURIComponent(this._proxyAuthHeader); } } } applyProxyToRequest(request) { if (this._proxySettings) { request.options = request.options ? Object.assign({}, request.options) : {}; const options = request.options; options.params = options.params || {}; options.params[TARGET_URL_QUERY_PARAM_NAME] = request.url; if (this._proxyAuthHeader) { options.params[AUTH_QUERY_PARAM_NAME] = this._proxyAuthHeader; } request.url = this._proxyUrl; } } applyProxyToSseRequest(request) { if (this._proxySettings) { request.url = `${this._proxyUrl}?${TARGET_URL_QUERY_PARAM_NAME}=${encodeURIComponent(request.url)}`; if (this._proxyAuthHeaderEncoded) { request.url = `${request.url}&${AUTH_QUERY_PARAM_NAME}=${this._proxyAuthHeaderEncoded}`; } } } get proxyUrl() { return this._proxyUrl; } } var ApiProvider; (function (ApiProvider) { ApiProvider["RolloutEu"] = "eu"; ApiProvider["Platform"] = "platform"; })(ApiProvider || (ApiProvider = {})); const API_HOST = 'x-api.rollout.io'; let _proxyConfig = new ProxyConfig(); const DEFAULT_CONFIGURATION = (hostProvider) => { let apiHost = API_HOST; if (hostProvider == ApiProvider.RolloutEu) { apiHost = `${'eu-'}${API_HOST}`; // as this goes into the url, not using the roxOptions.hosting value. instead using RoxOptions.hosting === "eu" as indication to use "eu-x-api..." } const configuration = { API_HOST: apiHost, CD_API_ENDPOINT: `https://${apiHost}/device/get_configuration`, CD_S3_ENDPOINT: `https://conf.rollout.io/`, SS_API_ENDPOINT: `https://${apiHost}/device/update_state_store/`, SS_S3_ENDPOINT: `https://statestore.rollout.io/`, CLIENT_DATA_CACHE_KEY: 'client_data', NOTIFICATIONS_ENDPOINT: `https://push.rollout.io/sse`, ANALYTICS_ENDPOINT: `https://analytic.rollout.io`, ERROR_REPORTER: undefined, }; if (hostProvider == ApiProvider.Platform) { configuration.API_HOST = 'api.cloudbees.io'; configuration.CD_API_ENDPOINT = 'https://api.cloudbees.io/device/get_configuration'; configuration.CD_S3_ENDPOINT = 'https://rox-conf.cloudbees.io/'; configuration.SS_API_ENDPOINT = 'https://api.cloudbees.io/device/update_state_store/'; configuration.SS_S3_ENDPOINT = 'https://rox-state.cloudbees.io/'; configuration.ANALYTICS_ENDPOINT = 'https://fm-analytics.cloudbees.io'; configuration.NOTIFICATIONS_ENDPOINT = 'https://sdk-notification-service.cloudbees.io/sse'; } return configuration; }; const SELFMANAGEDMODE_CONFIGURATION = ({ analyticsURL, serverURL, pushUpdateURL, configurationURL, stateURL, }) => ({ CD_API_ENDPOINT: `${serverURL}/device/get_configuration`, SS_API_ENDPOINT: `${serverURL}/device/update_state_store/`, CLIENT_DATA_CACHE_KEY: 'client_data', ANALYTICS_ENDPOINT: analyticsURL, NOTIFICATIONS_ENDPOINT: `${pushUpdateURL}/sse`, CD_S3_ENDPOINT: configurationURL, SS_S3_ENDPOINT: stateURL, ERROR_REPORTER: undefined, }); let _activeConfiguration = Object.assign({}, DEFAULT_CONFIGURATION()); var Config = { get: (key) => _activeConfiguration[key], set: (key, value) => { _activeConfiguration[key] = value; }, setActive: function (newConfiguration) { _activeConfiguration = Object.assign({}, newConfiguration); }, setProxy: function (proxySettings) { _proxyConfig = new ProxyConfig(proxySettings); }, getProxy: () => _proxyConfig, setHosting: function (hosting) { if (hosting === ApiProvider.RolloutEu) { _activeConfiguration = Object.assign({}, DEFAULT_CONFIGURATION('eu-')); } if (hosting === ApiProvider.Platform) { _activeConfiguration = Object.assign({}, DEFAULT_CONFIGURATION(hosting)); } }, setSelfManagedMode: function (conf) { _activeConfiguration = Object.assign({}, DEFAULT_CONFIGURATION(), SELFMANAGEDMODE_CONFIGURATION(conf)); }, setErrorReporter: function (errorReporter) { _activeConfiguration.ERROR_REPORTER = errorReporter; }, /** * Get the error reporter, or undefined if there is none configured. * Don't forget to check for undefined before using! */ getErrorReporter: () => _activeConfiguration.ERROR_REPORTER, }; class RoxxParser { /** * A parser for Roxx expressions. * Roxx expre