@splitsoftware/splitio-commons 1.13.2-rc.1 → 1.13.2-rc.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGES.txt +4 -3
  2. package/cjs/evaluator/Engine.js +5 -6
  3. package/cjs/evaluator/combiners/ifelseif.js +2 -3
  4. package/cjs/evaluator/condition/index.js +2 -3
  5. package/cjs/evaluator/index.js +4 -5
  6. package/cjs/evaluator/matchers/gte.js +3 -3
  7. package/cjs/evaluator/matchers/index.js +11 -1
  8. package/cjs/evaluator/matchers/lte.js +3 -3
  9. package/cjs/evaluator/matchers/matcherTypes.js +6 -1
  10. package/cjs/evaluator/matchers/semver_between.js +14 -0
  11. package/cjs/evaluator/matchers/semver_eq.js +13 -0
  12. package/cjs/evaluator/matchers/semver_gte.js +13 -0
  13. package/cjs/evaluator/matchers/semver_inlist.js +17 -0
  14. package/cjs/evaluator/matchers/semver_lte.js +13 -0
  15. package/cjs/evaluator/matchers/string.js +2 -9
  16. package/cjs/evaluator/matchers/whitelist.js +3 -2
  17. package/cjs/evaluator/matchersTransform/index.js +21 -16
  18. package/cjs/evaluator/matchersTransform/whitelist.js +2 -3
  19. package/cjs/evaluator/parser/index.js +19 -6
  20. package/cjs/logger/constants.js +4 -4
  21. package/cjs/logger/index.js +8 -1
  22. package/cjs/logger/messages/debug.js +0 -1
  23. package/cjs/logger/messages/error.js +2 -1
  24. package/cjs/services/splitApi.js +5 -5
  25. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -1
  26. package/cjs/storages/pluggable/index.js +1 -1
  27. package/cjs/utils/Semver.js +103 -0
  28. package/cjs/utils/labels/index.js +1 -1
  29. package/cjs/utils/settingsValidation/logger/pluggableLogger.js +1 -1
  30. package/esm/evaluator/Engine.js +5 -5
  31. package/esm/evaluator/combiners/ifelseif.js +2 -2
  32. package/esm/evaluator/condition/index.js +2 -2
  33. package/esm/evaluator/index.js +4 -4
  34. package/esm/evaluator/matchers/gte.js +3 -3
  35. package/esm/evaluator/matchers/index.js +11 -1
  36. package/esm/evaluator/matchers/lte.js +3 -3
  37. package/esm/evaluator/matchers/matcherTypes.js +6 -1
  38. package/esm/evaluator/matchers/semver_between.js +10 -0
  39. package/esm/evaluator/matchers/semver_eq.js +9 -0
  40. package/esm/evaluator/matchers/semver_gte.js +9 -0
  41. package/esm/evaluator/matchers/semver_inlist.js +13 -0
  42. package/esm/evaluator/matchers/semver_lte.js +9 -0
  43. package/esm/evaluator/matchers/string.js +3 -10
  44. package/esm/evaluator/matchers/whitelist.js +4 -3
  45. package/esm/evaluator/matchersTransform/index.js +21 -16
  46. package/esm/evaluator/matchersTransform/whitelist.js +2 -3
  47. package/esm/evaluator/parser/index.js +19 -6
  48. package/esm/logger/constants.js +1 -1
  49. package/esm/logger/index.js +9 -2
  50. package/esm/logger/messages/debug.js +0 -1
  51. package/esm/logger/messages/error.js +2 -1
  52. package/esm/services/splitApi.js +5 -5
  53. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -1
  54. package/esm/storages/pluggable/index.js +1 -1
  55. package/esm/utils/Semver.js +100 -0
  56. package/esm/utils/labels/index.js +1 -1
  57. package/esm/utils/settingsValidation/logger/pluggableLogger.js +1 -1
  58. package/package.json +1 -2
  59. package/src/dtos/types.ts +34 -1
  60. package/src/evaluator/Engine.ts +5 -6
  61. package/src/evaluator/combiners/ifelseif.ts +2 -2
  62. package/src/evaluator/condition/index.ts +2 -2
  63. package/src/evaluator/index.ts +4 -4
  64. package/src/evaluator/matchers/between.ts +1 -1
  65. package/src/evaluator/matchers/boolean.ts +1 -1
  66. package/src/evaluator/matchers/cont_all.ts +1 -1
  67. package/src/evaluator/matchers/cont_any.ts +1 -1
  68. package/src/evaluator/matchers/cont_str.ts +1 -1
  69. package/src/evaluator/matchers/eq.ts +1 -1
  70. package/src/evaluator/matchers/eq_set.ts +1 -1
  71. package/src/evaluator/matchers/ew.ts +1 -1
  72. package/src/evaluator/matchers/gte.ts +4 -4
  73. package/src/evaluator/matchers/index.ts +28 -18
  74. package/src/evaluator/matchers/lte.ts +4 -4
  75. package/src/evaluator/matchers/matcherTypes.ts +6 -1
  76. package/src/evaluator/matchers/part_of.ts +1 -1
  77. package/src/evaluator/matchers/semver_between.ts +16 -0
  78. package/src/evaluator/matchers/semver_eq.ts +14 -0
  79. package/src/evaluator/matchers/semver_gte.ts +14 -0
  80. package/src/evaluator/matchers/semver_inlist.ts +17 -0
  81. package/src/evaluator/matchers/semver_lte.ts +14 -0
  82. package/src/evaluator/matchers/string.ts +5 -13
  83. package/src/evaluator/matchers/sw.ts +1 -1
  84. package/src/evaluator/matchers/whitelist.ts +6 -4
  85. package/src/evaluator/matchersTransform/index.ts +31 -23
  86. package/src/evaluator/matchersTransform/whitelist.ts +4 -5
  87. package/src/evaluator/parser/index.ts +19 -7
  88. package/src/evaluator/types.ts +3 -3
  89. package/src/logger/constants.ts +1 -1
  90. package/src/logger/index.ts +8 -2
  91. package/src/logger/messages/debug.ts +0 -1
  92. package/src/logger/messages/error.ts +2 -1
  93. package/src/services/splitApi.ts +4 -5
  94. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +1 -1
  95. package/src/storages/pluggable/index.ts +1 -1
  96. package/src/utils/Semver.ts +111 -0
  97. package/src/utils/labels/index.ts +1 -1
  98. package/src/utils/settingsValidation/logger/pluggableLogger.ts +1 -1
  99. package/types/dtos/types.d.ts +26 -1
  100. package/types/evaluator/matchers/sember_inlist.d.ts +3 -0
  101. package/types/evaluator/matchers/semver_between.d.ts +3 -0
  102. package/types/evaluator/matchers/semver_eq.d.ts +2 -0
  103. package/types/evaluator/matchers/semver_gte.d.ts +2 -0
  104. package/types/evaluator/matchers/semver_inlist.d.ts +2 -0
  105. package/types/evaluator/matchers/semver_lte.d.ts +2 -0
  106. package/types/evaluator/matchers/whitelist.d.ts +1 -2
  107. package/types/evaluator/matchersTransform/set.d.ts +2 -2
  108. package/types/evaluator/matchersTransform/string.d.ts +7 -0
  109. package/types/evaluator/matchersTransform/whitelist.d.ts +3 -3
  110. package/types/evaluator/types.d.ts +3 -3
  111. package/types/logger/constants.d.ts +1 -1
  112. package/types/utils/labels/index.d.ts +1 -1
  113. package/types/utils/semVer.d.ts +15 -0
  114. package/cjs/evaluator/matchersTransform/set.js +0 -10
  115. package/esm/evaluator/matchersTransform/set.js +0 -6
  116. package/src/evaluator/matchersTransform/set.ts +0 -8
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Semver = void 0;
4
+ var lang_1 = require("../utils/lang");
5
+ var NUMERIC_IDENTIFIER_REGEX = /^[0-9]+$/;
6
+ var METADATA_DELIMITER = '+';
7
+ var PRERELEASE_DELIMITER = '-';
8
+ var VALUE_DELIMITER = '.';
9
+ /**
10
+ * Compares two strings. If both strings are numeric identifiers, they are compared numerically. Otherwise, they are compared lexicographically.
11
+ * This could be implemented using `a.localeCompare(b, undefined, { numeric: true })` but locale options are not broadly supported.
12
+ */
13
+ function compareStrings(a, b) {
14
+ if (NUMERIC_IDENTIFIER_REGEX.test(a) && NUMERIC_IDENTIFIER_REGEX.test(b)) {
15
+ var result = a.length - b.length;
16
+ if (result !== 0) {
17
+ return result;
18
+ }
19
+ }
20
+ return a < b ? -1 : a > b ? 1 : 0;
21
+ }
22
+ // Sanitizes a numeric identifier by removing leading zeros
23
+ function sanitizeNumericIdentifier(value) {
24
+ return value.replace(/^0+(?=\d)/, '');
25
+ }
26
+ function throwError(version) {
27
+ throw new Error('Unable to convert to Semver, incorrect format: ' + version);
28
+ }
29
+ var Semver = /** @class */ (function () {
30
+ function Semver(version) {
31
+ if (!(0, lang_1.isString)(version))
32
+ throwError(version);
33
+ // Separate metadata if exists
34
+ var index = version.indexOf(METADATA_DELIMITER);
35
+ var _a = index === -1 ? [version] : [version.slice(0, index), version.slice(index + 1)], vWithoutMetadata = _a[0], metadata = _a[1];
36
+ if (metadata === '')
37
+ throwError(version);
38
+ // Set pre-release versions if exists
39
+ index = vWithoutMetadata.indexOf(PRERELEASE_DELIMITER);
40
+ if (index === -1) {
41
+ this._isStable = true;
42
+ this._preRelease = [];
43
+ }
44
+ else {
45
+ this._isStable = false;
46
+ this._preRelease = vWithoutMetadata.slice(index + 1).split(VALUE_DELIMITER).map(function (value) {
47
+ if (!value)
48
+ throwError(version);
49
+ return NUMERIC_IDENTIFIER_REGEX.test(value) ?
50
+ sanitizeNumericIdentifier(value) :
51
+ value;
52
+ });
53
+ vWithoutMetadata = vWithoutMetadata.slice(0, index);
54
+ }
55
+ // Set major, minor, and patch versions
56
+ var vParts = vWithoutMetadata.split(VALUE_DELIMITER).map(function (value) {
57
+ if (!value || !NUMERIC_IDENTIFIER_REGEX.test(value))
58
+ throwError(version);
59
+ return sanitizeNumericIdentifier(value);
60
+ });
61
+ if (vParts.length !== 3)
62
+ throwError(version);
63
+ this._major = vParts[0];
64
+ this._minor = vParts[1];
65
+ this._patch = vParts[2];
66
+ // Set version string
67
+ this.version = vParts.join(VALUE_DELIMITER);
68
+ if (this._preRelease.length)
69
+ this.version += PRERELEASE_DELIMITER + this._preRelease.join(VALUE_DELIMITER);
70
+ if (metadata)
71
+ this.version += METADATA_DELIMITER + metadata;
72
+ }
73
+ /**
74
+ * Precedence comparision between 2 Semver objects.
75
+ *
76
+ * @return `0` if `this === toCompare`, `-1` if `this < toCompare`, and `1` if `this > toCompare`
77
+ */
78
+ Semver.prototype.compare = function (toCompare) {
79
+ if (this.version === toCompare.version)
80
+ return 0;
81
+ var result = compareStrings(this._major, toCompare._major);
82
+ if (result !== 0)
83
+ return result;
84
+ result = compareStrings(this._minor, toCompare._minor);
85
+ if (result !== 0)
86
+ return result;
87
+ result = compareStrings(this._patch, toCompare._patch);
88
+ if (result !== 0)
89
+ return result;
90
+ if (!this._isStable && toCompare._isStable)
91
+ return -1;
92
+ if (this._isStable && !toCompare._isStable)
93
+ return 1;
94
+ for (var i = 0, length_1 = Math.min(this._preRelease.length, toCompare._preRelease.length); i < length_1; i++) {
95
+ var result_1 = compareStrings(this._preRelease[i], toCompare._preRelease[i]);
96
+ if (result_1 !== 0)
97
+ return result_1;
98
+ }
99
+ return this._preRelease.length - toCompare._preRelease.length;
100
+ };
101
+ return Semver;
102
+ }());
103
+ exports.Semver = Semver;
@@ -8,4 +8,4 @@ exports.SDK_NOT_READY = 'not ready';
8
8
  exports.EXCEPTION = 'exception';
9
9
  exports.SPLIT_ARCHIVED = 'archived';
10
10
  exports.NOT_IN_SPLIT = 'not in split';
11
- exports.UNSUPPORTED_MATCHER_TYPE = 'unsupported matcher type';
11
+ exports.UNSUPPORTED_MATCHER_TYPE = 'targeting rule type unsupported by sdk';
@@ -4,7 +4,7 @@ exports.validateLogger = void 0;
4
4
  var logger_1 = require("../../../logger");
5
5
  var commons_1 = require("./commons");
6
6
  function isLogger(log) {
7
- return log && typeof log.debug === 'function' && typeof log.info === 'function' && typeof log.warn === 'function' && typeof log.error === 'function' && typeof log.setLogLevel === 'function';
7
+ return log !== null && typeof log === 'object' && typeof log.debug === 'function' && typeof log.info === 'function' && typeof log.warn === 'function' && typeof log.error === 'function' && typeof log.setLogLevel === 'function';
8
8
  }
9
9
  // By default it starts disabled.
10
10
  var initialLogLevel = logger_1.LogLevels.NONE;
@@ -2,12 +2,12 @@ import { get } from '../utils/lang';
2
2
  import { parser } from './parser';
3
3
  import { keyParser } from '../utils/key';
4
4
  import { thenable } from '../utils/promise/thenable';
5
- import * as LabelsConstants from '../utils/labels';
5
+ import { EXCEPTION, NO_CONDITION_MATCH, SPLIT_ARCHIVED, SPLIT_KILLED } from '../utils/labels';
6
6
  import { CONTROL } from '../utils/constants';
7
7
  function evaluationResult(result, defaultTreatment) {
8
8
  return {
9
9
  treatment: get(result, 'treatment', defaultTreatment),
10
- label: get(result, 'label', LabelsConstants.NO_CONDITION_MATCH)
10
+ label: get(result, 'label', NO_CONDITION_MATCH)
11
11
  };
12
12
  }
13
13
  var Engine = /** @class */ (function () {
@@ -38,16 +38,16 @@ var Engine = /** @class */ (function () {
38
38
  catch (err) {
39
39
  return {
40
40
  treatment: CONTROL,
41
- label: LabelsConstants.EXCEPTION
41
+ label: EXCEPTION
42
42
  };
43
43
  }
44
44
  if (this.isGarbage()) {
45
45
  treatment = CONTROL;
46
- label = LabelsConstants.SPLIT_ARCHIVED;
46
+ label = SPLIT_ARCHIVED;
47
47
  }
48
48
  else if (killed) {
49
49
  treatment = defaultTreatment;
50
- label = LabelsConstants.SPLIT_KILLED;
50
+ label = SPLIT_KILLED;
51
51
  }
52
52
  else {
53
53
  var evaluation = this.evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator);
@@ -1,6 +1,6 @@
1
1
  import { findIndex } from '../../utils/lang';
2
2
  import { thenable } from '../../utils/promise/thenable';
3
- import * as LabelsConstants from '../../utils/labels';
3
+ import { UNSUPPORTED_MATCHER_TYPE } from '../../utils/labels';
4
4
  import { CONTROL } from '../../utils/constants';
5
5
  import { ENGINE_COMBINER_IFELSEIF, ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, ERROR_ENGINE_COMBINER_IFELSEIF } from '../../logger/constants';
6
6
  export function ifElseIfCombinerContext(log, predicates) {
@@ -8,7 +8,7 @@ export function ifElseIfCombinerContext(log, predicates) {
8
8
  log.error(ERROR_ENGINE_COMBINER_IFELSEIF);
9
9
  return {
10
10
  treatment: CONTROL,
11
- label: LabelsConstants.UNSUPPORTED_MATCHER_TYPE
11
+ label: UNSUPPORTED_MATCHER_TYPE
12
12
  };
13
13
  }
14
14
  function computeTreatment(predicateResults) {
@@ -1,6 +1,6 @@
1
1
  import { getTreatment, shouldApplyRollout } from './engineUtils';
2
2
  import { thenable } from '../../utils/promise/thenable';
3
- import * as LabelsConstants from '../../utils/labels';
3
+ import { NOT_IN_SPLIT } from '../../utils/labels';
4
4
  // Build Evaluation object if and only if matchingResult is true
5
5
  function match(log, matchingResult, bucketingKey, seed, treatments, label) {
6
6
  if (matchingResult) {
@@ -20,7 +20,7 @@ export function conditionContext(log, matcherEvaluator, treatments, label, condi
20
20
  if (conditionType === 'ROLLOUT' && !shouldApplyRollout(trafficAllocation, key.bucketingKey, trafficAllocationSeed)) {
21
21
  return {
22
22
  treatment: undefined,
23
- label: LabelsConstants.NOT_IN_SPLIT
23
+ label: NOT_IN_SPLIT
24
24
  };
25
25
  }
26
26
  // matcherEvaluator could be Async, this relays on matchers return value, so we need
@@ -1,12 +1,12 @@
1
1
  import { Engine } from './Engine';
2
2
  import { thenable } from '../utils/promise/thenable';
3
- import * as LabelsConstants from '../utils/labels';
3
+ import { EXCEPTION, SPLIT_NOT_FOUND } from '../utils/labels';
4
4
  import { CONTROL } from '../utils/constants';
5
5
  import { setToArray, returnSetsUnion, _Set } from '../utils/lang/sets';
6
6
  import { WARN_FLAGSET_WITHOUT_FLAGS } from '../logger/constants';
7
7
  var treatmentException = {
8
8
  treatment: CONTROL,
9
- label: LabelsConstants.EXCEPTION,
9
+ label: EXCEPTION,
10
10
  config: null
11
11
  };
12
12
  function treatmentsException(splitNames) {
@@ -87,13 +87,13 @@ export function evaluateFeaturesByFlagSets(log, key, flagSets, attributes, stora
87
87
  function getEvaluation(log, splitJSON, key, attributes, storage) {
88
88
  var evaluation = {
89
89
  treatment: CONTROL,
90
- label: LabelsConstants.SPLIT_NOT_FOUND,
90
+ label: SPLIT_NOT_FOUND,
91
91
  config: null
92
92
  };
93
93
  if (splitJSON) {
94
94
  var split_1 = Engine.parse(log, splitJSON, storage);
95
95
  evaluation = split_1.getTreatment(key, attributes, evaluateFeature);
96
- // If the storage is async and the evaluated split uses segment, evaluation is thenable
96
+ // If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
97
97
  if (thenable(evaluation)) {
98
98
  return evaluation.then(function (result) {
99
99
  result.changeNumber = split_1.getChangeNumber();
@@ -1,8 +1,8 @@
1
1
  import { ENGINE_MATCHER_GREATER } from '../../logger/constants';
2
2
  export function greaterThanEqualMatcherContext(log, ruleAttr) {
3
3
  return function greaterThanEqualMatcher(runtimeAttr) {
4
- var isGreaterEqualThan = runtimeAttr >= ruleAttr;
5
- log.debug(ENGINE_MATCHER_GREATER, [runtimeAttr, ruleAttr, isGreaterEqualThan]);
6
- return isGreaterEqualThan;
4
+ var isGreaterThanEqual = runtimeAttr >= ruleAttr;
5
+ log.debug(ENGINE_MATCHER_GREATER, [runtimeAttr, ruleAttr, isGreaterThanEqual]);
6
+ return isGreaterThanEqual;
7
7
  };
8
8
  }
@@ -15,6 +15,11 @@ import { containsStringMatcherContext } from './cont_str';
15
15
  import { dependencyMatcherContext } from './dependency';
16
16
  import { booleanMatcherContext } from './boolean';
17
17
  import { stringMatcherContext } from './string';
18
+ import { equalToSemverMatcherContext } from './semver_eq';
19
+ import { greaterThanEqualToSemverMatcherContext } from './semver_gte';
20
+ import { lessThanEqualToSemverMatcherContext } from './semver_lte';
21
+ import { betweenSemverMatcherContext } from './semver_between';
22
+ import { inListSemverMatcherContext } from './semver_inlist';
18
23
  var matchers = [
19
24
  undefined,
20
25
  allMatcherContext,
@@ -33,7 +38,12 @@ var matchers = [
33
38
  containsStringMatcherContext,
34
39
  dependencyMatcherContext,
35
40
  booleanMatcherContext,
36
- stringMatcherContext // MATCHES_STRING: 17
41
+ stringMatcherContext,
42
+ equalToSemverMatcherContext,
43
+ greaterThanEqualToSemverMatcherContext,
44
+ lessThanEqualToSemverMatcherContext,
45
+ betweenSemverMatcherContext,
46
+ inListSemverMatcherContext, // IN_LIST_SEMVER: 22
37
47
  ];
38
48
  /**
39
49
  * Matcher factory.
@@ -1,8 +1,8 @@
1
1
  import { ENGINE_MATCHER_LESS } from '../../logger/constants';
2
2
  export function lessThanEqualMatcherContext(log, ruleAttr) {
3
3
  return function lessThanEqualMatcher(runtimeAttr) {
4
- var isLessEqualThan = runtimeAttr <= ruleAttr;
5
- log.debug(ENGINE_MATCHER_LESS, [runtimeAttr, ruleAttr, isLessEqualThan]);
6
- return isLessEqualThan;
4
+ var isLessThanEqual = runtimeAttr <= ruleAttr;
5
+ log.debug(ENGINE_MATCHER_LESS, [runtimeAttr, ruleAttr, isLessThanEqual]);
6
+ return isLessThanEqual;
7
7
  };
8
8
  }
@@ -16,7 +16,12 @@ export var matcherTypes = {
16
16
  CONTAINS_STRING: 14,
17
17
  IN_SPLIT_TREATMENT: 15,
18
18
  EQUAL_TO_BOOLEAN: 16,
19
- MATCHES_STRING: 17
19
+ MATCHES_STRING: 17,
20
+ EQUAL_TO_SEMVER: 18,
21
+ GREATER_THAN_OR_EQUAL_TO_SEMVER: 19,
22
+ LESS_THAN_OR_EQUAL_TO_SEMVER: 20,
23
+ BETWEEN_SEMVER: 21,
24
+ IN_LIST_SEMVER: 22,
20
25
  };
21
26
  export var matcherDataTypes = {
22
27
  BOOLEAN: 'BOOLEAN',
@@ -0,0 +1,10 @@
1
+ import { Semver } from '../../utils/Semver';
2
+ export function betweenSemverMatcherContext(log, ruleAttr) {
3
+ var startSemver = new Semver(ruleAttr.start);
4
+ var endSemver = new Semver(ruleAttr.end);
5
+ return function betweenSemverMatcher(runtimeAttr) {
6
+ var runtimeSemver = new Semver(runtimeAttr);
7
+ var isBetween = startSemver.compare(runtimeSemver) <= 0 && endSemver.compare(runtimeSemver) >= 0;
8
+ return isBetween;
9
+ };
10
+ }
@@ -0,0 +1,9 @@
1
+ import { Semver } from '../../utils/Semver';
2
+ export function equalToSemverMatcherContext(log, ruleAttr) {
3
+ var ruleSemver = new Semver(ruleAttr);
4
+ return function equalToSemverMatcher(runtimeAttr) {
5
+ var runtimeSemver = new Semver(runtimeAttr);
6
+ var isEqual = ruleSemver.version === runtimeSemver.version;
7
+ return isEqual;
8
+ };
9
+ }
@@ -0,0 +1,9 @@
1
+ import { Semver } from '../../utils/Semver';
2
+ export function greaterThanEqualToSemverMatcherContext(log, ruleAttr) {
3
+ var ruleSemver = new Semver(ruleAttr);
4
+ return function greaterThanEqualToSemverMatcher(runtimeAttr) {
5
+ var runtimeSemver = new Semver(runtimeAttr);
6
+ var isGreaterThanEqual = runtimeSemver.compare(ruleSemver) >= 0;
7
+ return isGreaterThanEqual;
8
+ };
9
+ }
@@ -0,0 +1,13 @@
1
+ import { _Set } from '../../utils/lang/sets';
2
+ import { Semver } from '../../utils/Semver';
3
+ export function inListSemverMatcherContext(log, ruleAttr) {
4
+ // @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers
5
+ if (!ruleAttr || ruleAttr.length === 0)
6
+ throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type');
7
+ var listOfSemvers = new _Set(ruleAttr.map(function (version) { return new Semver(version).version; }));
8
+ return function inListSemverMatcher(runtimeAttr) {
9
+ var runtimeSemver = new Semver(runtimeAttr).version;
10
+ var isInList = listOfSemvers.has(runtimeSemver);
11
+ return isInList;
12
+ };
13
+ }
@@ -0,0 +1,9 @@
1
+ import { Semver } from '../../utils/Semver';
2
+ export function lessThanEqualToSemverMatcherContext(log, ruleAttr) {
3
+ var ruleSemver = new Semver(ruleAttr);
4
+ return function lessThanEqualToSemverMatcher(runtimeAttr) {
5
+ var runtimeSemver = new Semver(runtimeAttr);
6
+ var isLessThanEqual = runtimeSemver.compare(ruleSemver) <= 0;
7
+ return isLessThanEqual;
8
+ };
9
+ }
@@ -1,15 +1,8 @@
1
- import { ENGINE_MATCHER_STRING_INVALID, ENGINE_MATCHER_STRING } from '../../logger/constants';
1
+ import { ENGINE_MATCHER_STRING } from '../../logger/constants';
2
2
  export function stringMatcherContext(log, ruleAttr) {
3
+ var regex = new RegExp(ruleAttr);
3
4
  return function stringMatcher(runtimeAttr) {
4
- var re;
5
- try {
6
- re = new RegExp(ruleAttr);
7
- }
8
- catch (e) {
9
- log.debug(ENGINE_MATCHER_STRING_INVALID, [ruleAttr]);
10
- return false;
11
- }
12
- var regexMatches = re.test(runtimeAttr);
5
+ var regexMatches = regex.test(runtimeAttr);
13
6
  log.debug(ENGINE_MATCHER_STRING, [runtimeAttr, ruleAttr, regexMatches ? 'yes' : 'no']);
14
7
  return regexMatches;
15
8
  };
@@ -1,9 +1,10 @@
1
- import { setToArray } from '../../utils/lang/sets';
1
+ import { _Set } from '../../utils/lang/sets';
2
2
  import { ENGINE_MATCHER_WHITELIST } from '../../logger/constants';
3
3
  export function whitelistMatcherContext(log, ruleAttr) {
4
+ var whitelistSet = new _Set(ruleAttr);
4
5
  return function whitelistMatcher(runtimeAttr) {
5
- var isInWhitelist = ruleAttr.has(runtimeAttr);
6
- log.debug(ENGINE_MATCHER_WHITELIST, [runtimeAttr, setToArray(ruleAttr).join(','), isInWhitelist]);
6
+ var isInWhitelist = whitelistSet.has(runtimeAttr);
7
+ log.debug(ENGINE_MATCHER_WHITELIST, [runtimeAttr, ruleAttr.join(','), isInWhitelist]);
7
8
  return isInWhitelist;
8
9
  };
9
10
  }
@@ -2,7 +2,6 @@ import { findIndex } from '../../utils/lang';
2
2
  import { matcherTypes, matcherTypesMapper, matcherDataTypes } from '../matchers/matcherTypes';
3
3
  import { segmentTransform } from './segment';
4
4
  import { whitelistTransform } from './whitelist';
5
- import { setTransform } from './set';
6
5
  import { numericTransform } from './unaryNumeric';
7
6
  import { zeroSinceHH, zeroSinceSS } from '../convertions';
8
7
  /**
@@ -10,37 +9,34 @@ import { zeroSinceHH, zeroSinceSS } from '../convertions';
10
9
  */
11
10
  export function matchersTransform(matchers) {
12
11
  var parsedMatchers = matchers.map(function (matcher) {
13
- var matcherType = matcher.matcherType /* string */, negate = matcher.negate /* boolean */, keySelector = matcher.keySelector /* keySelectorObject */, segmentObject = matcher.userDefinedSegmentMatcherData /* segmentObject */, whitelistObject = matcher.whitelistMatcherData /* whiteListObject, provided by 'WHITELIST', set and string matchers */, unaryNumericObject = matcher.unaryNumericMatcherData /* unaryNumericObject */, betweenObject = matcher.betweenMatcherData /* betweenObject */, dependencyObject = matcher.dependencyMatcherData /* dependencyObject */, booleanMatcherData = matcher.booleanMatcherData, stringMatcherData = matcher.stringMatcherData;
12
+ var matcherType = matcher.matcherType, negate = matcher.negate, keySelector = matcher.keySelector, userDefinedSegmentMatcherData = matcher.userDefinedSegmentMatcherData, whitelistMatcherData = matcher.whitelistMatcherData, /* whitelistObject, provided by 'WHITELIST', 'IN_LIST_SEMVER', set and string matchers */ unaryNumericMatcherData = matcher.unaryNumericMatcherData, betweenMatcherData = matcher.betweenMatcherData, dependencyMatcherData = matcher.dependencyMatcherData, booleanMatcherData = matcher.booleanMatcherData, stringMatcherData = matcher.stringMatcherData, betweenStringMatcherData = matcher.betweenStringMatcherData;
14
13
  var attribute = keySelector && keySelector.attribute;
15
14
  var type = matcherTypesMapper(matcherType);
16
15
  // As default input data type we use string (even for ALL_KEYS)
17
16
  var dataType = matcherDataTypes.STRING;
18
17
  var value = undefined;
19
18
  if (type === matcherTypes.IN_SEGMENT) {
20
- value = segmentTransform(segmentObject);
21
- }
22
- else if (type === matcherTypes.WHITELIST) {
23
- value = whitelistTransform(whitelistObject);
19
+ value = segmentTransform(userDefinedSegmentMatcherData);
24
20
  }
25
21
  else if (type === matcherTypes.EQUAL_TO) {
26
- value = numericTransform(unaryNumericObject);
22
+ value = numericTransform(unaryNumericMatcherData);
27
23
  dataType = matcherDataTypes.NUMBER;
28
- if (unaryNumericObject.dataType === 'DATETIME') {
24
+ if (unaryNumericMatcherData.dataType === 'DATETIME') {
29
25
  value = zeroSinceHH(value);
30
26
  dataType = matcherDataTypes.DATETIME;
31
27
  }
32
28
  }
33
29
  else if (type === matcherTypes.GREATER_THAN_OR_EQUAL_TO ||
34
30
  type === matcherTypes.LESS_THAN_OR_EQUAL_TO) {
35
- value = numericTransform(unaryNumericObject);
31
+ value = numericTransform(unaryNumericMatcherData);
36
32
  dataType = matcherDataTypes.NUMBER;
37
- if (unaryNumericObject.dataType === 'DATETIME') {
33
+ if (unaryNumericMatcherData.dataType === 'DATETIME') {
38
34
  value = zeroSinceSS(value);
39
35
  dataType = matcherDataTypes.DATETIME;
40
36
  }
41
37
  }
42
38
  else if (type === matcherTypes.BETWEEN) {
43
- value = betweenObject;
39
+ value = betweenMatcherData;
44
40
  dataType = matcherDataTypes.NUMBER;
45
41
  if (value.dataType === 'DATETIME') {
46
42
  value.start = zeroSinceSS(value.start);
@@ -48,33 +44,42 @@ export function matchersTransform(matchers) {
48
44
  dataType = matcherDataTypes.DATETIME;
49
45
  }
50
46
  }
47
+ else if (type === matcherTypes.BETWEEN_SEMVER) {
48
+ value = betweenStringMatcherData;
49
+ }
51
50
  else if (type === matcherTypes.EQUAL_TO_SET ||
52
51
  type === matcherTypes.CONTAINS_ANY_OF_SET ||
53
52
  type === matcherTypes.CONTAINS_ALL_OF_SET ||
54
53
  type === matcherTypes.PART_OF_SET) {
55
- value = setTransform(whitelistObject);
54
+ value = whitelistTransform(whitelistMatcherData);
56
55
  dataType = matcherDataTypes.SET;
57
56
  }
58
- else if (type === matcherTypes.STARTS_WITH ||
57
+ else if (type === matcherTypes.WHITELIST ||
58
+ type === matcherTypes.IN_LIST_SEMVER ||
59
+ type === matcherTypes.STARTS_WITH ||
59
60
  type === matcherTypes.ENDS_WITH ||
60
61
  type === matcherTypes.CONTAINS_STRING) {
61
- value = setTransform(whitelistObject);
62
+ value = whitelistTransform(whitelistMatcherData);
62
63
  }
63
64
  else if (type === matcherTypes.IN_SPLIT_TREATMENT) {
64
- value = dependencyObject;
65
+ value = dependencyMatcherData;
65
66
  dataType = matcherDataTypes.NOT_SPECIFIED;
66
67
  }
67
68
  else if (type === matcherTypes.EQUAL_TO_BOOLEAN) {
68
69
  dataType = matcherDataTypes.BOOLEAN;
69
70
  value = booleanMatcherData;
70
71
  }
71
- else if (type === matcherTypes.MATCHES_STRING) {
72
+ else if (type === matcherTypes.MATCHES_STRING ||
73
+ type === matcherTypes.EQUAL_TO_SEMVER ||
74
+ type === matcherTypes.GREATER_THAN_OR_EQUAL_TO_SEMVER ||
75
+ type === matcherTypes.LESS_THAN_OR_EQUAL_TO_SEMVER) {
72
76
  value = stringMatcherData;
73
77
  }
74
78
  return {
75
79
  attribute: attribute,
76
80
  negate: negate,
77
81
  type: type,
82
+ name: matcherType,
78
83
  value: value,
79
84
  dataType: dataType // runtime input data type
80
85
  };
@@ -1,7 +1,6 @@
1
- import { _Set } from '../../utils/lang/sets';
2
1
  /**
3
- * Extract whitelist as a set. Used by 'WHITELIST' matcher.
2
+ * Extract whitelist array.
4
3
  */
5
4
  export function whitelistTransform(whitelistObject) {
6
- return new _Set(whitelistObject.whitelist);
5
+ return whitelistObject && whitelistObject.whitelist;
7
6
  }
@@ -6,6 +6,7 @@ import { conditionContext } from '../condition';
6
6
  import { ifElseIfCombinerContext } from '../combiners/ifelseif';
7
7
  import { andCombinerContext } from '../combiners/and';
8
8
  import { thenable } from '../../utils/promise/thenable';
9
+ import { ENGINE_MATCHER_ERROR } from '../../logger/constants';
9
10
  export function parser(log, conditions, storage) {
10
11
  var predicates = [];
11
12
  for (var i = 0; i < conditions.length; i++) {
@@ -14,16 +15,28 @@ export function parser(log, conditions, storage) {
14
15
  var matchers = matchersTransform(matcherGroup.matchers);
15
16
  // create a set of pure functions from the matcher's dto
16
17
  var expressions = matchers.map(function (matcherDto) {
17
- var matcher = matcherFactory(log, matcherDto, storage);
18
+ var matcher;
19
+ try {
20
+ matcher = matcherFactory(log, matcherDto, storage);
21
+ }
22
+ catch (error) {
23
+ log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
24
+ }
18
25
  // Evaluator function.
19
26
  return function (key, attributes, splitEvaluator) {
20
27
  var value = sanitizeValue(log, key, matcherDto, attributes);
21
- var result = value !== undefined && matcher ? matcher(value, splitEvaluator) : false;
22
- if (thenable(result)) {
23
- // @ts-ignore
24
- return result.then(function (res) { return Boolean(res ^ matcherDto.negate); });
28
+ var result = false;
29
+ if (value !== undefined && matcher) {
30
+ try {
31
+ result = matcher(value, splitEvaluator);
32
+ }
33
+ catch (error) {
34
+ log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
35
+ }
25
36
  }
26
- // @ts-ignore
37
+ if (thenable(result)) { // @ts-ignore
38
+ return result.then(function (res) { return Boolean(res ^ matcherDto.negate); });
39
+ } // @ts-ignore
27
40
  return Boolean(result ^ matcherDto.negate);
28
41
  };
29
42
  });
@@ -25,7 +25,6 @@ export var ENGINE_MATCHER_LESS = 16;
25
25
  export var ENGINE_MATCHER_PART_OF = 17;
26
26
  export var ENGINE_MATCHER_SEGMENT = 18;
27
27
  export var ENGINE_MATCHER_STRING = 19;
28
- export var ENGINE_MATCHER_STRING_INVALID = 20;
29
28
  export var ENGINE_MATCHER_STARTS_WITH = 21;
30
29
  export var ENGINE_MATCHER_WHITELIST = 22;
31
30
  export var ENGINE_VALUE = 23;
@@ -128,6 +127,7 @@ export var ERROR_NOT_BOOLEAN = 325;
128
127
  export var ERROR_MIN_CONFIG_PARAM = 326;
129
128
  export var ERROR_TOO_MANY_SETS = 327;
130
129
  export var ERROR_SETS_FILTER_EXCLUSIVE = 328;
130
+ export var ENGINE_MATCHER_ERROR = 329;
131
131
  // Log prefixes (a.k.a. tags or categories)
132
132
  export var LOG_PREFIX_SETTINGS = 'settings';
133
133
  export var LOG_PREFIX_INSTANTIATION = 'Factory instantiation';
@@ -1,5 +1,5 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
- import { find } from '../utils/lang';
2
+ import { find, isObject } from '../utils/lang';
3
3
  import { _Map } from '../utils/lang/maps';
4
4
  export var LogLevels = {
5
5
  DEBUG: 'DEBUG',
@@ -24,7 +24,14 @@ export function _sprintf(format, args) {
24
24
  if (args === void 0) { args = []; }
25
25
  var i = 0;
26
26
  return format.replace(/%s/g, function () {
27
- return args[i++];
27
+ var arg = args[i++];
28
+ if (isObject(arg) || Array.isArray(arg)) {
29
+ try {
30
+ arg = JSON.stringify(arg);
31
+ }
32
+ catch (e) { /* empty */ }
33
+ }
34
+ return arg;
28
35
  });
29
36
  }
30
37
  var defaultOptions = {
@@ -22,7 +22,6 @@ export var codesDebug = codesInfo.concat([
22
22
  [c.ENGINE_MATCHER_PART_OF, c.LOG_PREFIX_ENGINE_MATCHER + '[partOfMatcher] %s is part of %s? %s'],
23
23
  [c.ENGINE_MATCHER_SEGMENT, c.LOG_PREFIX_ENGINE_MATCHER + '[segmentMatcher] evaluated %s / %s => %s'],
24
24
  [c.ENGINE_MATCHER_STRING, c.LOG_PREFIX_ENGINE_MATCHER + '[stringMatcher] does %s matches with %s? %s'],
25
- [c.ENGINE_MATCHER_STRING_INVALID, c.LOG_PREFIX_ENGINE_MATCHER + '[stringMatcher] %s is an invalid regex'],
26
25
  [c.ENGINE_MATCHER_STARTS_WITH, c.LOG_PREFIX_ENGINE_MATCHER + '[startsWithMatcher] %s starts with %s? %s'],
27
26
  [c.ENGINE_MATCHER_WHITELIST, c.LOG_PREFIX_ENGINE_MATCHER + '[whitelistMatcher] evaluated %s in [%s] => %s'],
28
27
  [c.ENGINE_VALUE, c.LOG_PREFIX_ENGINE_VALUE + 'Extracted attribute [%s], [%s] will be used for matching.'],
@@ -1,7 +1,8 @@
1
1
  import * as c from '../constants';
2
2
  export var codesError = [
3
3
  // evaluator
4
- [c.ERROR_ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Invalid feature flag, no valid rules or unsupported matcher type found'],
4
+ [c.ERROR_ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Invalid feature flag, no valid rules or unsupported targeting rule type found'],
5
+ [c.ENGINE_MATCHER_ERROR, c.LOG_PREFIX_ENGINE_MATCHER + '[%s] %s'],
5
6
  // SDK
6
7
  [c.ERROR_LOGLEVEL_INVALID, 'logger: Invalid Log Level - No changes to the logs will be applied.'],
7
8
  [c.ERROR_CLIENT_CANNOT_GET_READY, 'The SDK will not get ready. Reason: %s'],
@@ -29,16 +29,16 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
29
29
  return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
30
30
  },
31
31
  fetchAuth: function (userMatchingKeys) {
32
- var url = urls.auth + "/v2/auth";
33
- if (userMatchingKeys) { // accounting the possibility that `userMatchingKeys` is undefined (server-side API)
32
+ var url = urls.auth + "/v2/auth?s=" + FLAGS_SPEC;
33
+ if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
34
34
  var queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
35
- if (queryParams) // accounting the possibility that `userKeys` and thus `queryParams` are empty
36
- url += '?' + queryParams;
35
+ if (queryParams)
36
+ url += '&' + queryParams;
37
37
  }
38
38
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
39
39
  },
40
40
  fetchSplitChanges: function (since, noCache, till) {
41
- var url = urls.sdk + "/splitChanges?s=" + FLAGS_SPEC + "&since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
41
+ var url = urls.sdk + "/splitChanges?s=" + FLAGS_SPEC + "&since=" + since + (filterQueryString || '') + (till ? '&till=' + till : '');
42
42
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
43
43
  .catch(function (err) {
44
44
  if (err.statusCode === 414)
@@ -123,7 +123,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
123
123
  SplitsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
124
124
  // when using a new split query, we must update it at the store
125
125
  if (this.updateNewFilter) {
126
- this.log.info(LOG_PREFIX + 'SDK key or feature flag filter criteria was modified. Updating cache');
126
+ this.log.info(LOG_PREFIX + 'SDK key, flags filter criteria or flags spec version was modified. Updating cache');
127
127
  var storageHashKey = this.keys.buildHashKey();
128
128
  try {
129
129
  localStorage.setItem(storageHashKey, this.storageHash);