UNPKG

@amplitude/experiment-core

Version:

Amplitude Experiment evaluation JavaScript implementation.

1,190 lines (1,178 loc) 67.4 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["experiment-core"] = {})); })(this, (function (exports) { 'use strict'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; } || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function () { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(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()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var EvaluationOperator = { IS: 'is', IS_NOT: 'is not', CONTAINS: 'contains', DOES_NOT_CONTAIN: 'does not contain', LESS_THAN: 'less', LESS_THAN_EQUALS: 'less or equal', GREATER_THAN: 'greater', GREATER_THAN_EQUALS: 'greater or equal', VERSION_LESS_THAN: 'version less', VERSION_LESS_THAN_EQUALS: 'version less or equal', VERSION_GREATER_THAN: 'version greater', VERSION_GREATER_THAN_EQUALS: 'version greater or equal', SET_IS: 'set is', SET_IS_NOT: 'set is not', SET_CONTAINS: 'set contains', SET_DOES_NOT_CONTAIN: 'set does not contain', SET_CONTAINS_ANY: 'set contains any', SET_DOES_NOT_CONTAIN_ANY: 'set does not contain any', REGEX_MATCH: 'regex match', REGEX_DOES_NOT_MATCH: 'regex does not match', }; var stringToUtf8ByteArray = function (str) { var out = []; var p = 0; for (var i = 0; i < str.length; i++) { var c = str.charCodeAt(i); if (c < 128) { out[p++] = c; } else if (c < 2048) { out[p++] = (c >> 6) | 192; out[p++] = (c & 63) | 128; } else if ((c & 0xfc00) == 0xd800 && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xfc00) == 0xdc00) { // Surrogate Pair c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff); out[p++] = (c >> 18) | 240; out[p++] = ((c >> 12) & 63) | 128; out[p++] = ((c >> 6) & 63) | 128; out[p++] = (c & 63) | 128; } else { out[p++] = (c >> 12) | 224; out[p++] = ((c >> 6) & 63) | 128; out[p++] = (c & 63) | 128; } } return Uint8Array.from(out); }; var C1_32 = -0x3361d2af; var C2_32 = 0x1b873593; var R1_32 = 15; var R2_32 = 13; var M_32 = 5; var N_32 = -0x19ab949c; var hash32x86 = function (input, seed) { if (seed === void 0) { seed = 0; } var data = stringToUtf8ByteArray(input); var length = data.length; var nBlocks = length >> 2; var hash = seed; // body for (var i = 0; i < nBlocks; i++) { var index_1 = i << 2; var k = readIntLe(data, index_1); hash = mix32(k, hash); } // tail var index = nBlocks << 2; var k1 = 0; switch (length - index) { case 3: k1 ^= data[index + 2] << 16; k1 ^= data[index + 1] << 8; k1 ^= data[index]; k1 = Math.imul(k1, C1_32); k1 = rotateLeft(k1, R1_32); k1 = Math.imul(k1, C2_32); hash ^= k1; break; case 2: k1 ^= data[index + 1] << 8; k1 ^= data[index]; k1 = Math.imul(k1, C1_32); k1 = rotateLeft(k1, R1_32); k1 = Math.imul(k1, C2_32); hash ^= k1; break; case 1: k1 ^= data[index]; k1 = Math.imul(k1, C1_32); k1 = rotateLeft(k1, R1_32); k1 = Math.imul(k1, C2_32); hash ^= k1; break; } hash ^= length; return fmix32(hash) >>> 0; }; var mix32 = function (k, hash) { var kResult = k; var hashResult = hash; kResult = Math.imul(kResult, C1_32); kResult = rotateLeft(kResult, R1_32); kResult = Math.imul(kResult, C2_32); hashResult ^= kResult; hashResult = rotateLeft(hashResult, R2_32); hashResult = Math.imul(hashResult, M_32); return (hashResult + N_32) | 0; }; var fmix32 = function (hash) { var hashResult = hash; hashResult ^= hashResult >>> 16; hashResult = Math.imul(hashResult, -0x7a143595); hashResult ^= hashResult >>> 13; hashResult = Math.imul(hashResult, -0x3d4d51cb); hashResult ^= hashResult >>> 16; return hashResult; }; var rotateLeft = function (x, n, width) { if (width === void 0) { width = 32; } if (n > width) n = n % width; var mask = (0xffffffff << (width - n)) >>> 0; var r = (((x & mask) >>> 0) >>> (width - n)) >>> 0; return ((x << n) | r) >>> 0; }; var readIntLe = function (data, index) { if (index === void 0) { index = 0; } var n = (data[index] << 24) | (data[index + 1] << 16) | (data[index + 2] << 8) | data[index + 3]; return reverseBytes(n); }; var reverseBytes = function (n) { return (((n & -0x1000000) >>> 24) | ((n & 0x00ff0000) >>> 8) | ((n & 0x0000ff00) << 8) | ((n & 0x000000ff) << 24)); }; var select = function (selectable, selector) { var e_1, _a; if (!selector || selector.length === 0) { return undefined; } try { for (var selector_1 = __values(selector), selector_1_1 = selector_1.next(); !selector_1_1.done; selector_1_1 = selector_1.next()) { var selectorElement = selector_1_1.value; if (!selectorElement || !selectable || typeof selectable !== 'object') { return undefined; } selectable = selectable[selectorElement]; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (selector_1_1 && !selector_1_1.done && (_a = selector_1.return)) _a.call(selector_1); } finally { if (e_1) throw e_1.error; } } if (selectable === undefined || selectable === null) { return undefined; } else { return selectable; } }; // major and minor should be non-negative numbers separated by a dot var MAJOR_MINOR_REGEX = '(\\d+)\\.(\\d+)'; // patch should be a non-negative number var PATCH_REGEX = '(\\d+)'; // prerelease is optional. If provided, it should be a hyphen followed by a // series of dot separated identifiers where an identifer can contain anything in [-0-9a-zA-Z] var PRERELEASE_REGEX = '(-(([-\\w]+\\.?)*))?'; // version pattern should be major.minor(.patchAndPreRelease) where .patchAndPreRelease is optional var VERSION_PATTERN = "^".concat(MAJOR_MINOR_REGEX, "(\\.").concat(PATCH_REGEX).concat(PRERELEASE_REGEX, ")?$"); var SemanticVersion = /** @class */ (function () { function SemanticVersion(major, minor, patch, preRelease) { if (preRelease === void 0) { preRelease = undefined; } this.major = major; this.minor = minor; this.patch = patch; this.preRelease = preRelease; } SemanticVersion.parse = function (version) { if (!version) { return undefined; } var matchGroup = new RegExp(VERSION_PATTERN).exec(version); if (!matchGroup) { return undefined; } var major = Number(matchGroup[1]); var minor = Number(matchGroup[2]); if (isNaN(major) || isNaN(minor)) { return undefined; } var patch = Number(matchGroup[4]) || 0; var preRelease = matchGroup[5] || undefined; return new SemanticVersion(major, minor, patch, preRelease); }; SemanticVersion.prototype.compareTo = function (other) { if (this.major > other.major) return 1; if (this.major < other.major) return -1; if (this.minor > other.minor) return 1; if (this.minor < other.minor) return -1; if (this.patch > other.patch) return 1; if (this.patch < other.patch) return -1; if (this.preRelease && !other.preRelease) return -1; if (!this.preRelease && other.preRelease) return 1; if (this.preRelease && other.preRelease) { if (this.preRelease > other.preRelease) return 1; if (this.preRelease < other.preRelease) return -1; return 0; } return 0; }; return SemanticVersion; }()); var EvaluationEngine = /** @class */ (function () { function EvaluationEngine() { } EvaluationEngine.prototype.evaluate = function (context, flags) { var e_1, _a; var results = {}; var target = { context: context, result: results, }; try { for (var flags_1 = __values(flags), flags_1_1 = flags_1.next(); !flags_1_1.done; flags_1_1 = flags_1.next()) { var flag = flags_1_1.value; // Evaluate flag and update results. var variant = this.evaluateFlag(target, flag); if (variant) { results[flag.key] = variant; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (flags_1_1 && !flags_1_1.done && (_a = flags_1.return)) _a.call(flags_1); } finally { if (e_1) throw e_1.error; } } return results; }; /** @alpha */ EvaluationEngine.prototype.evaluateWithTraces = function (context, flags) { var e_2, _a; var results = {}; var traces = {}; var target = { context: context, result: results, }; try { for (var flags_2 = __values(flags), flags_2_1 = flags_2.next(); !flags_2_1.done; flags_2_1 = flags_2.next()) { var flag = flags_2_1.value; var _b = this.evaluateFlagWithTrace(target, flag), variant = _b.variant, trace = _b.trace; if (variant) { results[flag.key] = variant; } traces[flag.key] = trace; } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (flags_2_1 && !flags_2_1.done && (_a = flags_2.return)) _a.call(flags_2); } finally { if (e_2) throw e_2.error; } } return { results: results, traces: traces }; }; EvaluationEngine.prototype.evaluateFlag = function (target, flag) { var e_3, _a; var result; try { for (var _b = __values(flag.segments), _c = _b.next(); !_c.done; _c = _b.next()) { var segment = _c.value; result = this.evaluateSegment(target, flag, segment); if (result) { // Merge all metadata into the result var metadata = __assign(__assign(__assign({}, flag.metadata), segment.metadata), result.metadata); result = __assign(__assign({}, result), { metadata: metadata }); break; } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_3) throw e_3.error; } } return result; }; EvaluationEngine.prototype.evaluateFlagWithTrace = function (target, flag) { var e_4, _a, e_5, _b, e_6, _c; var _d, _e; var steps = []; var winnerVariant; var winnerSegment; var matchedSegmentName; try { for (var _f = __values(flag.segments), _g = _f.next(); !_g.done; _g = _f.next()) { var segment = _g.value; if (!segment.conditions) { var variantKey = this.bucket(target, segment); var bucketed_1 = variantKey !== undefined; steps.push({ segmentMetadata: segment.metadata, conditionsPassed: true, bucketed: bucketed_1, bucketVariant: variantKey, conditionResult: undefined, matched: bucketed_1, }); if (!winnerVariant && variantKey !== undefined) { winnerVariant = flag.variants[variantKey]; winnerSegment = segment; matchedSegmentName = (_d = segment.metadata) === null || _d === void 0 ? void 0 : _d.segmentName; } continue; } var orConditionResults = []; var segmentMatched = false; try { for (var _h = (e_5 = void 0, __values(segment.conditions)), _j = _h.next(); !_j.done; _j = _h.next()) { var innerConditions = _j.value; var match = true; var andConditionResults = []; try { for (var innerConditions_1 = (e_6 = void 0, __values(innerConditions)), innerConditions_1_1 = innerConditions_1.next(); !innerConditions_1_1.done; innerConditions_1_1 = innerConditions_1.next()) { var condition = innerConditions_1_1.value; var propValue = select(target, condition.selector); var conditionMatch = this.matchCondition(target, condition); andConditionResults.push({ propValue: propValue, condition: condition, matched: conditionMatch, }); if (!conditionMatch) { match = false; break; } } } catch (e_6_1) { e_6 = { error: e_6_1 }; } finally { try { if (innerConditions_1_1 && !innerConditions_1_1.done && (_c = innerConditions_1.return)) _c.call(innerConditions_1); } finally { if (e_6) throw e_6.error; } } orConditionResults.push(andConditionResults); if (match) { segmentMatched = true; break; } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (_j && !_j.done && (_b = _h.return)) _b.call(_h); } finally { if (e_5) throw e_5.error; } } var bucketed = false; var bucketVariant = void 0; if (segmentMatched) { bucketVariant = this.bucket(target, segment); bucketed = bucketVariant !== undefined; } steps.push({ segmentMetadata: segment.metadata, conditionsPassed: segmentMatched, bucketed: bucketed, bucketVariant: bucketVariant, conditionResult: orConditionResults, matched: segmentMatched && bucketed, }); if (segmentMatched && bucketVariant !== undefined && !winnerVariant) { winnerVariant = flag.variants[bucketVariant]; winnerSegment = segment; matchedSegmentName = (_e = segment.metadata) === null || _e === void 0 ? void 0 : _e.segmentName; } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_g && !_g.done && (_a = _f.return)) _a.call(_f); } finally { if (e_4) throw e_4.error; } } if (winnerVariant && winnerSegment) { var metadata = __assign(__assign(__assign({}, flag.metadata), winnerSegment.metadata), winnerVariant.metadata); winnerVariant = __assign(__assign({}, winnerVariant), { metadata: metadata }); } var trace = { flagKey: flag.key, matched: winnerVariant !== undefined, matchedSegment: matchedSegmentName, steps: steps, }; return { variant: winnerVariant, trace: trace }; }; EvaluationEngine.prototype.evaluateSegment = function (target, flag, segment) { if (!segment.conditions) { // Null conditions always match var variantKey = this.bucket(target, segment); if (variantKey !== undefined) { return flag.variants[variantKey]; } else { return undefined; } } var match = this.evaluateConditions(target, segment.conditions); // On match, bucket the user. if (match) { var variantKey = this.bucket(target, segment); if (variantKey !== undefined) { return flag.variants[variantKey]; } else { return undefined; } } return undefined; }; EvaluationEngine.prototype.evaluateConditions = function (target, conditions) { var e_7, _a, e_8, _b; try { // Outer list logic is "or" (||) for (var conditions_1 = __values(conditions), conditions_1_1 = conditions_1.next(); !conditions_1_1.done; conditions_1_1 = conditions_1.next()) { var innerConditions = conditions_1_1.value; var match = true; try { for (var innerConditions_2 = (e_8 = void 0, __values(innerConditions)), innerConditions_2_1 = innerConditions_2.next(); !innerConditions_2_1.done; innerConditions_2_1 = innerConditions_2.next()) { var condition = innerConditions_2_1.value; match = this.matchCondition(target, condition); if (!match) { break; } } } catch (e_8_1) { e_8 = { error: e_8_1 }; } finally { try { if (innerConditions_2_1 && !innerConditions_2_1.done && (_b = innerConditions_2.return)) _b.call(innerConditions_2); } finally { if (e_8) throw e_8.error; } } if (match) { return true; } } } catch (e_7_1) { e_7 = { error: e_7_1 }; } finally { try { if (conditions_1_1 && !conditions_1_1.done && (_a = conditions_1.return)) _a.call(conditions_1); } finally { if (e_7) throw e_7.error; } } return false; }; EvaluationEngine.prototype.matchCondition = function (target, condition) { var propValue = select(target, condition.selector); if (propValue === undefined || propValue === null) { return this.matchNull(condition.op, condition.values); } else { var propValueStringList = this.coerceStringArray(propValue); if (this.isSetOperator(condition.op)) { if (!propValueStringList) { return false; } return this.matchSet(propValueStringList, condition.op, condition.values); } else if (propValueStringList) { return this.matchStringsNonSet(propValueStringList, condition.op, condition.values); } else { var propValueString = this.coerceString(propValue); if (propValueString !== undefined) { return this.matchString(propValueString, condition.op, condition.values); } else { return false; } } } }; EvaluationEngine.prototype.getHash = function (key) { return hash32x86(key); }; EvaluationEngine.prototype.bucket = function (target, segment) { var e_9, _a, e_10, _b; if (!segment.bucket) { // A null bucket means the segment is fully rolled out. Select the // default variant. return segment.variant; } // Select the bucketing value. var bucketingValue = this.coerceString(select(target, segment.bucket.selector)); if (!bucketingValue || bucketingValue.length === 0) { // A null or empty bucketing value cannot be bucketed. Select the // default variant. return segment.variant; } // Salt and has the value, and compute the allocation and distribution // values. var keyToHash = "".concat(segment.bucket.salt, "/").concat(bucketingValue); var hash = this.getHash(keyToHash); var allocationValue = hash % 100; var distributionValue = Math.floor(hash / 100); try { for (var _c = __values(segment.bucket.allocations), _d = _c.next(); !_d.done; _d = _c.next()) { var allocation = _d.value; var allocationStart = allocation.range[0]; var allocationEnd = allocation.range[1]; if (allocationValue >= allocationStart && allocationValue < allocationEnd) { try { for (var _e = (e_10 = void 0, __values(allocation.distributions)), _f = _e.next(); !_f.done; _f = _e.next()) { var distribution = _f.value; var distributionStart = distribution.range[0]; var distributionEnd = distribution.range[1]; if (distributionValue >= distributionStart && distributionValue < distributionEnd) { return distribution.variant; } } } catch (e_10_1) { e_10 = { error: e_10_1 }; } finally { try { if (_f && !_f.done && (_b = _e.return)) _b.call(_e); } finally { if (e_10) throw e_10.error; } } } } } catch (e_9_1) { e_9 = { error: e_9_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_9) throw e_9.error; } } return segment.variant; }; EvaluationEngine.prototype.matchNull = function (op, filterValues) { var containsNone = this.containsNone(filterValues); switch (op) { case EvaluationOperator.IS: case EvaluationOperator.CONTAINS: case EvaluationOperator.LESS_THAN: case EvaluationOperator.LESS_THAN_EQUALS: case EvaluationOperator.GREATER_THAN: case EvaluationOperator.GREATER_THAN_EQUALS: case EvaluationOperator.VERSION_LESS_THAN: case EvaluationOperator.VERSION_LESS_THAN_EQUALS: case EvaluationOperator.VERSION_GREATER_THAN: case EvaluationOperator.VERSION_GREATER_THAN_EQUALS: case EvaluationOperator.SET_IS: case EvaluationOperator.SET_CONTAINS: case EvaluationOperator.SET_CONTAINS_ANY: return containsNone; case EvaluationOperator.IS_NOT: case EvaluationOperator.DOES_NOT_CONTAIN: case EvaluationOperator.SET_DOES_NOT_CONTAIN: case EvaluationOperator.SET_DOES_NOT_CONTAIN_ANY: return !containsNone; default: return false; } }; EvaluationEngine.prototype.matchSet = function (propValues, op, filterValues) { switch (op) { case EvaluationOperator.SET_IS: return this.setEquals(propValues, filterValues); case EvaluationOperator.SET_IS_NOT: return !this.setEquals(propValues, filterValues); case EvaluationOperator.SET_CONTAINS: return this.matchesSetContainsAll(propValues, filterValues); case EvaluationOperator.SET_DOES_NOT_CONTAIN: return !this.matchesSetContainsAll(propValues, filterValues); case EvaluationOperator.SET_CONTAINS_ANY: return this.matchesSetContainsAny(propValues, filterValues); case EvaluationOperator.SET_DOES_NOT_CONTAIN_ANY: return !this.matchesSetContainsAny(propValues, filterValues); default: return false; } }; EvaluationEngine.prototype.matchString = function (propValue, op, filterValues) { var _this = this; switch (op) { case EvaluationOperator.IS: return this.matchesIs(propValue, filterValues); case EvaluationOperator.IS_NOT: return !this.matchesIs(propValue, filterValues); case EvaluationOperator.CONTAINS: return this.matchesContains(propValue, filterValues); case EvaluationOperator.DOES_NOT_CONTAIN: return !this.matchesContains(propValue, filterValues); case EvaluationOperator.LESS_THAN: case EvaluationOperator.LESS_THAN_EQUALS: case EvaluationOperator.GREATER_THAN: case EvaluationOperator.GREATER_THAN_EQUALS: return this.matchesComparable(propValue, op, filterValues, function (value) { return _this.parseNumber(value); }, this.comparator); case EvaluationOperator.VERSION_LESS_THAN: case EvaluationOperator.VERSION_LESS_THAN_EQUALS: case EvaluationOperator.VERSION_GREATER_THAN: case EvaluationOperator.VERSION_GREATER_THAN_EQUALS: return this.matchesComparable(propValue, op, filterValues, function (value) { return SemanticVersion.parse(value); }, this.versionComparator); case EvaluationOperator.REGEX_MATCH: return this.matchesRegex(propValue, filterValues); case EvaluationOperator.REGEX_DOES_NOT_MATCH: return !this.matchesRegex(propValue, filterValues); default: return false; } }; EvaluationEngine.prototype.matchStringsNonSet = function (propValues, op, filterValues) { var _this = this; return propValues.some(function (element) { return _this.matchString(element, op, filterValues); }); }; EvaluationEngine.prototype.matchesIs = function (propValue, filterValues) { if (this.containsBooleans(filterValues)) { var lower_1 = propValue.toLowerCase(); if (lower_1 === 'true' || lower_1 === 'false') { return filterValues.some(function (value) { return value.toLowerCase() === lower_1; }); } } return filterValues.some(function (value) { return propValue === value; }); }; EvaluationEngine.prototype.matchesContains = function (propValue, filterValues) { var e_11, _a; try { for (var filterValues_1 = __values(filterValues), filterValues_1_1 = filterValues_1.next(); !filterValues_1_1.done; filterValues_1_1 = filterValues_1.next()) { var filterValue = filterValues_1_1.value; if (propValue.toLowerCase().includes(filterValue.toLowerCase())) { return true; } } } catch (e_11_1) { e_11 = { error: e_11_1 }; } finally { try { if (filterValues_1_1 && !filterValues_1_1.done && (_a = filterValues_1.return)) _a.call(filterValues_1); } finally { if (e_11) throw e_11.error; } } return false; }; EvaluationEngine.prototype.matchesComparable = function (propValue, op, filterValues, typeTransformer, typeComparator) { var _this = this; var propValueTransformed = typeTransformer(propValue); var filterValuesTransformed = filterValues .map(function (filterValue) { return typeTransformer(filterValue); }) .filter(function (filterValue) { return filterValue !== undefined; }); if (propValueTransformed === undefined || filterValuesTransformed.length === 0) { return filterValues.some(function (filterValue) { return _this.comparator(propValue, op, filterValue); }); } else { return filterValuesTransformed.some(function (filterValueTransformed) { return typeComparator(propValueTransformed, op, filterValueTransformed); }); } }; EvaluationEngine.prototype.comparator = function (propValue, op, filterValue) { switch (op) { case EvaluationOperator.LESS_THAN: case EvaluationOperator.VERSION_LESS_THAN: return propValue < filterValue; case EvaluationOperator.LESS_THAN_EQUALS: case EvaluationOperator.VERSION_LESS_THAN_EQUALS: return propValue <= filterValue; case EvaluationOperator.GREATER_THAN: case EvaluationOperator.VERSION_GREATER_THAN: return propValue > filterValue; case EvaluationOperator.GREATER_THAN_EQUALS: case EvaluationOperator.VERSION_GREATER_THAN_EQUALS: return propValue >= filterValue; default: return false; } }; EvaluationEngine.prototype.versionComparator = function (propValue, op, filterValue) { var compareTo = propValue.compareTo(filterValue); switch (op) { case EvaluationOperator.LESS_THAN: case EvaluationOperator.VERSION_LESS_THAN: return compareTo < 0; case EvaluationOperator.LESS_THAN_EQUALS: case EvaluationOperator.VERSION_LESS_THAN_EQUALS: return compareTo <= 0; case EvaluationOperator.GREATER_THAN: case EvaluationOperator.VERSION_GREATER_THAN: return compareTo > 0; case EvaluationOperator.GREATER_THAN_EQUALS: case EvaluationOperator.VERSION_GREATER_THAN_EQUALS: return compareTo >= 0; default: return false; } }; EvaluationEngine.prototype.matchesRegex = function (propValue, filterValues) { return filterValues.some(function (filterValue) { return Boolean(new RegExp(filterValue).exec(propValue)); }); }; EvaluationEngine.prototype.containsNone = function (filterValues) { return filterValues.some(function (filterValue) { return filterValue === '(none)'; }); }; EvaluationEngine.prototype.containsBooleans = function (filterValues) { return filterValues.some(function (filterValue) { switch (filterValue.toLowerCase()) { case 'true': case 'false': return true; default: return false; } }); }; EvaluationEngine.prototype.parseNumber = function (value) { var _a; return (_a = Number(value)) !== null && _a !== void 0 ? _a : undefined; }; EvaluationEngine.prototype.coerceString = function (value) { if (value === undefined || value === null) { return undefined; } if (typeof value === 'object') { return JSON.stringify(value); } return String(value); }; EvaluationEngine.prototype.coerceStringArray = function (value) { var _this = this; if (Array.isArray(value)) { var anyArray = value; return anyArray .map(function (e) { return _this.coerceString(e); }) .filter(Boolean); } var stringValue = String(value); if (!stringValue.startsWith('[')) { return undefined; } try { var parsedValue = JSON.parse(stringValue); if (Array.isArray(parsedValue)) { return parsedValue .map(function (e) { return _this.coerceString(e); }) .filter(Boolean); } else { return undefined; } } catch (_a) { return undefined; } }; EvaluationEngine.prototype.isSetOperator = function (op) { switch (op) { case EvaluationOperator.SET_IS: case EvaluationOperator.SET_IS_NOT: case EvaluationOperator.SET_CONTAINS: case EvaluationOperator.SET_DOES_NOT_CONTAIN: case EvaluationOperator.SET_CONTAINS_ANY: case EvaluationOperator.SET_DOES_NOT_CONTAIN_ANY: return true; default: return false; } }; EvaluationEngine.prototype.setEquals = function (xa, ya) { var xs = new Set(xa); var ys = new Set(ya); return xs.size === ys.size && __spreadArray([], __read(ys), false).every(function (y) { return xs.has(y); }); }; EvaluationEngine.prototype.matchesSetContainsAll = function (propValues, filterValues) { var e_12, _a; if (propValues.length < filterValues.length) { return false; } try { for (var filterValues_2 = __values(filterValues), filterValues_2_1 = filterValues_2.next(); !filterValues_2_1.done; filterValues_2_1 = filterValues_2.next()) { var filterValue = filterValues_2_1.value; if (!this.matchesIs(filterValue, propValues)) { return false; } } } catch (e_12_1) { e_12 = { error: e_12_1 }; } finally { try { if (filterValues_2_1 && !filterValues_2_1.done && (_a = filterValues_2.return)) _a.call(filterValues_2); } finally { if (e_12) throw e_12.error; } } return true; }; EvaluationEngine.prototype.matchesSetContainsAny = function (propValues, filterValues) { var e_13, _a; try { for (var filterValues_3 = __values(filterValues), filterValues_3_1 = filterValues_3.next(); !filterValues_3_1.done; filterValues_3_1 = filterValues_3.next()) { var filterValue = filterValues_3_1.value; if (this.matchesIs(filterValue, propValues)) { return true; } } } catch (e_13_1) { e_13 = { error: e_13_1 }; } finally { try { if (filterValues_3_1 && !filterValues_3_1.done && (_a = filterValues_3.return)) _a.call(filterValues_3); } finally { if (e_13) throw e_13.error; } } return false; }; return EvaluationEngine; }()); var topologicalSort = function (flags, flagKeys) { var e_1, _a; var available = __assign({}, flags); var result = []; var startingKeys = flagKeys || Object.keys(available); try { for (var startingKeys_1 = __values(startingKeys), startingKeys_1_1 = startingKeys_1.next(); !startingKeys_1_1.done; startingKeys_1_1 = startingKeys_1.next()) { var flagKey = startingKeys_1_1.value; var traversal = parentTraversal(flagKey, available); if (traversal) { result.push.apply(result, __spreadArray([], __read(traversal), false)); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (startingKeys_1_1 && !startingKeys_1_1.done && (_a = startingKeys_1.return)) _a.call(startingKeys_1); } finally { if (e_1) throw e_1.error; } } return result; }; var parentTraversal = function (flagKey, available, path) { var e_2, _a; if (path === void 0) { path = []; } var flag = available[flagKey]; if (!flag) { return undefined; } else if (!flag.dependencies || flag.dependencies.length === 0) { delete available[flag.key]; return [flag]; } path.push(flag.key); var result = []; var _loop_1 = function (parentKey) { if (path.some(function (p) { return p === parentKey; })) { throw Error("Detected a cycle between flags ".concat(path)); } var traversal = parentTraversal(parentKey, available, path); if (traversal) { result.push.apply(result, __spreadArray([], __read(traversal), false)); } }; try { for (var _b = __values(flag.dependencies), _c = _b.next(); !_c.done; _c = _b.next()) { var parentKey = _c.value; _loop_1(parentKey); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } result.push(flag); path.pop(); delete available[flag.key]; return result; }; /** * 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 version = '3.7.8'; /** * @deprecated use lowercase `version`. */ const VERSION = version; 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 => new Uint8Array(Array.prototype.slice.call(it, 0)); 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 f