@splitsoftware/splitio-commons 1.13.2-rc.6 → 1.13.2-rc.7

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 (34) hide show
  1. package/CHANGES.txt +2 -0
  2. package/cjs/evaluator/index.js +1 -1
  3. package/cjs/evaluator/matchers/semver_inlist.js +1 -1
  4. package/cjs/evaluator/matchersTransform/index.js +1 -0
  5. package/cjs/evaluator/matchersTransform/whitelist.js +1 -1
  6. package/cjs/evaluator/parser/index.js +7 -12
  7. package/cjs/logger/messages/debug.js +2 -2
  8. package/cjs/services/splitApi.js +5 -5
  9. package/cjs/storages/KeyBuilder.js +3 -2
  10. package/cjs/utils/constants/index.js +2 -1
  11. package/esm/evaluator/index.js +1 -1
  12. package/esm/evaluator/matchers/semver_inlist.js +1 -1
  13. package/esm/evaluator/matchersTransform/index.js +1 -0
  14. package/esm/evaluator/matchersTransform/whitelist.js +1 -1
  15. package/esm/evaluator/parser/index.js +7 -12
  16. package/esm/logger/messages/debug.js +2 -2
  17. package/esm/services/splitApi.js +6 -6
  18. package/esm/storages/KeyBuilder.js +3 -2
  19. package/esm/utils/constants/index.js +1 -0
  20. package/package.json +1 -1
  21. package/src/evaluator/index.ts +1 -1
  22. package/src/evaluator/matchers/semver_inlist.ts +1 -1
  23. package/src/evaluator/matchersTransform/index.ts +2 -1
  24. package/src/evaluator/matchersTransform/whitelist.ts +1 -1
  25. package/src/evaluator/parser/index.ts +4 -5
  26. package/src/evaluator/types.ts +1 -0
  27. package/src/logger/messages/debug.ts +2 -2
  28. package/src/services/splitApi.ts +5 -6
  29. package/src/storages/KeyBuilder.ts +3 -2
  30. package/src/utils/constants/index.ts +2 -0
  31. package/types/evaluator/matchersTransform/whitelist.d.ts +1 -1
  32. package/types/evaluator/types.d.ts +1 -0
  33. package/types/storages/KeyBuilder.d.ts +1 -1
  34. package/types/utils/constants/index.d.ts +1 -0
package/CHANGES.txt CHANGED
@@ -1,5 +1,7 @@
1
1
  1.14.0 (April XX, 2024)
2
+ - Added support for Semver matchers.
2
3
  - Updated impression label to 'unsupported matcher type' when the matcher type is not supported by the SDK.
4
+ - Updated Split API client to include the flags spec version query parameter for the `splitChanges` and `auth` endpoints.
3
5
 
4
6
  1.13.1 (January 10, 2024)
5
7
  - Updated client `destroy` method to release SDK key immediately and avoid unexpected warning logs when a factory is created with the same SDK key after the previous one was destroyed.
@@ -99,7 +99,7 @@ function getEvaluation(log, splitJSON, key, attributes, storage) {
99
99
  if (splitJSON) {
100
100
  var split_1 = Engine_1.Engine.parse(log, splitJSON, storage);
101
101
  evaluation = split_1.getTreatment(key, attributes, evaluateFeature);
102
- // If the storage is async and the evaluated feature flag is type IN_SPLIT_TREATMENT or IN_SEGMENT, evaluation is thenable
102
+ // If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
103
103
  if ((0, thenable_1.thenable)(evaluation)) {
104
104
  return evaluation.then(function (result) {
105
105
  result.changeNumber = split_1.getChangeNumber();
@@ -4,7 +4,7 @@ exports.inListSemverMatcherContext = void 0;
4
4
  var sets_1 = require("../../utils/lang/sets");
5
5
  var Semver_1 = require("../../utils/Semver");
6
6
  function inListSemverMatcherContext(ruleAttr) {
7
- // @TODO move eventually to `matchersTransform` and validate for all matchers
7
+ // @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers
8
8
  if (!ruleAttr || ruleAttr.length === 0)
9
9
  throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type');
10
10
  var listOfSemvers = new sets_1._Set(ruleAttr.map(function (version) { return new Semver_1.Semver(version).version; }));
@@ -82,6 +82,7 @@ function matchersTransform(matchers) {
82
82
  attribute: attribute,
83
83
  negate: negate,
84
84
  type: type,
85
+ name: matcherType,
85
86
  value: value,
86
87
  dataType: dataType // runtime input data type
87
88
  };
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.whitelistTransform = void 0;
4
4
  /**
5
- * Extract whitelist as a set. Used by 'WHITELIST' matcher.
5
+ * Extract whitelist array.
6
6
  */
7
7
  function whitelistTransform(whitelistObject) {
8
8
  return whitelistObject && whitelistObject.whitelist;
@@ -12,18 +12,18 @@ var thenable_1 = require("../../utils/promise/thenable");
12
12
  var constants_1 = require("../../logger/constants");
13
13
  function parser(log, conditions, storage) {
14
14
  var predicates = [];
15
- var _loop_1 = function (i) {
15
+ for (var i = 0; i < conditions.length; i++) {
16
16
  var _a = conditions[i], matcherGroup = _a.matcherGroup, partitions = _a.partitions, label = _a.label, conditionType = _a.conditionType;
17
17
  // transform data structure
18
18
  var matchers = (0, matchersTransform_1.matchersTransform)(matcherGroup.matchers);
19
19
  // create a set of pure functions from the matcher's dto
20
- var expressions = matchers.map(function (matcherDto, index) {
20
+ var expressions = matchers.map(function (matcherDto) {
21
21
  var matcher;
22
22
  try {
23
23
  matcher = (0, matchers_1.matcherFactory)(log, matcherDto, storage);
24
24
  }
25
25
  catch (error) {
26
- log.error(constants_1.ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
26
+ log.error(constants_1.ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
27
27
  }
28
28
  // Evaluator function.
29
29
  return function (key, attributes, splitEvaluator) {
@@ -34,12 +34,11 @@ function parser(log, conditions, storage) {
34
34
  result = matcher(value, splitEvaluator);
35
35
  }
36
36
  catch (error) {
37
- // @TODO should we propagate the error, to create impressions with label EXCEPTION?
38
- log.error(constants_1.ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
37
+ log.error(constants_1.ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
39
38
  }
40
39
  }
41
40
  function handleResult(result) {
42
- log.debug(constants_1.ENGINE_MATCHER_RESULT, [matcherGroup.matchers[index].matcherType, result, matcherDto.value, value]); // @ts-ignore
41
+ log.debug(constants_1.ENGINE_MATCHER_RESULT, [matcherDto.name, result, matcherDto.value, value]); // @ts-ignore
43
42
  return Boolean(result ^ matcherDto.negate);
44
43
  }
45
44
  return (0, thenable_1.thenable)(result) ?
@@ -51,14 +50,10 @@ function parser(log, conditions, storage) {
51
50
  if (expressions.length === 0) {
52
51
  // reset any data collected during parsing
53
52
  predicates = [];
54
- return "break";
53
+ // and break the loop
54
+ break;
55
55
  }
56
56
  predicates.push((0, condition_1.conditionContext)(log, (0, and_1.andCombinerContext)(log, expressions), treatments_1.Treatments.parse(partitions), label, conditionType));
57
- };
58
- for (var i = 0; i < conditions.length; i++) {
59
- var state_1 = _loop_1(i);
60
- if (state_1 === "break")
61
- break;
62
57
  }
63
58
  // Instanciate evaluator given the set of conditions using if else if logic
64
59
  return (0, ifelseif_1.ifElseIfCombinerContext)(log, predicates);
@@ -10,8 +10,8 @@ exports.codesDebug = info_1.codesInfo.concat([
10
10
  [c.ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Treatment found: %s'],
11
11
  [c.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, c.LOG_PREFIX_ENGINE_COMBINER + 'All predicates evaluated, no treatment found.'],
12
12
  [c.ENGINE_BUCKET, c.LOG_PREFIX_ENGINE + ': using algo "murmur" bucket %s for key %s using seed %s - treatment %s'],
13
- [c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
14
- [c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] will evaluate parent feature flag: "%s" with key: %s %s'],
13
+ [c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
14
+ [c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] will evaluate parent feature flag: "%s" with key: %s %s'],
15
15
  [c.ENGINE_VALUE, c.LOG_PREFIX_ENGINE_VALUE + 'Extracted attribute `%s`. %s will be used for matching.'],
16
16
  [c.ENGINE_SANITIZE, c.LOG_PREFIX_ENGINE + ':sanitize: Attempted to sanitize %s which should be of type %s. Sanitized and processed value => %s'],
17
17
  [c.ENGINE_MATCHER_RESULT, c.LOG_PREFIX_ENGINE_MATCHER + '[%s] Result: %s. Rule value: %s. Evaluation value: %s'],
@@ -32,16 +32,16 @@ function splitApiFactory(settings, platform, telemetryTracker) {
32
32
  return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
33
33
  },
34
34
  fetchAuth: function (userMatchingKeys) {
35
- var url = urls.auth + "/v2/auth";
36
- if (userMatchingKeys) { // accounting the possibility that `userMatchingKeys` is undefined (server-side API)
35
+ var url = urls.auth + "/v2/auth?s=" + constants_1.FLAGS_SPEC;
36
+ if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
37
37
  var queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
38
- if (queryParams) // accounting the possibility that `userKeys` and thus `queryParams` are empty
39
- url += '?' + queryParams;
38
+ if (queryParams)
39
+ url += '&' + queryParams;
40
40
  }
41
41
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(constants_1.TOKEN));
42
42
  },
43
43
  fetchSplitChanges: function (since, noCache, till) {
44
- var url = urls.sdk + "/splitChanges?since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
44
+ var url = urls.sdk + "/splitChanges?s=" + constants_1.FLAGS_SPEC + "&since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
45
45
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(constants_1.SPLITS))
46
46
  .catch(function (err) {
47
47
  if (err.statusCode === 414)
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getStorageHash = exports.KeyBuilder = exports.validatePrefix = void 0;
4
+ var constants_1 = require("../utils/constants");
4
5
  var lang_1 = require("../utils/lang");
5
6
  var murmur3_1 = require("../utils/murmur3/murmur3");
6
7
  var everythingAtTheEnd = /[^.]+$/;
@@ -66,10 +67,10 @@ var KeyBuilder = /** @class */ (function () {
66
67
  }());
67
68
  exports.KeyBuilder = KeyBuilder;
68
69
  /**
69
- * Generates a murmur32 hash based on the authorization key and the feature flags filter query.
70
+ * Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
70
71
  * The hash is in hexadecimal format (8 characters max, 32 bits).
71
72
  */
72
73
  function getStorageHash(settings) {
73
- return (0, murmur3_1.hash)(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString).toString(16);
74
+ return (0, murmur3_1.hash)(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString + "::" + constants_1.FLAGS_SPEC).toString(16);
74
75
  }
75
76
  exports.getStorageHash = getStorageHash;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MY_SEGMENT = exports.SEGMENT = exports.TOKEN = exports.TELEMETRY = exports.EVENTS = exports.IMPRESSIONS_COUNT = exports.IMPRESSIONS = exports.SPLITS = exports.NONE_ENUM = exports.DEBUG_ENUM = exports.OPTIMIZED_ENUM = exports.CONSUMER_PARTIAL_ENUM = exports.CONSUMER_ENUM = exports.STANDALONE_ENUM = exports.DEDUPED = exports.DROPPED = exports.QUEUED = exports.NAMES_FN_LABEL = exports.SPLITS_FN_LABEL = exports.SPLIT_FN_LABEL = exports.TRACK_FN_LABEL = exports.GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = exports.GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET = exports.GET_TREATMENTS_BY_FLAG_SETS = exports.GET_TREATMENTS_BY_FLAG_SET = exports.GET_TREATMENTS_WITH_CONFIG = exports.GET_TREATMENT_WITH_CONFIG = exports.GET_TREATMENTS = exports.GET_TREATMENT = exports.CONSENT_UNKNOWN = exports.CONSENT_DECLINED = exports.CONSENT_GRANTED = exports.STORAGE_PLUGGABLE = exports.STORAGE_REDIS = exports.STORAGE_LOCALSTORAGE = exports.STORAGE_MEMORY = exports.CONSUMER_PARTIAL_MODE = exports.CONSUMER_MODE = exports.PRODUCER_MODE = exports.STANDALONE_MODE = exports.LOCALHOST_MODE = exports.NONE = exports.OPTIMIZED = exports.DEBUG = exports.SPLIT_EVENT = exports.SPLIT_IMPRESSION = exports.NA = exports.UNKNOWN = exports.CONTROL_WITH_CONFIG = exports.CONTROL = void 0;
4
- exports.PAUSED = exports.ENABLED = exports.DISABLED = exports.NON_REQUESTED = exports.REQUESTED = exports.POLLING = exports.STREAMING = exports.AUTH_REJECTION = exports.SYNC_MODE_UPDATE = exports.ABLY_ERROR = exports.TOKEN_REFRESH = exports.SSE_CONNECTION_ERROR = exports.STREAMING_STATUS = exports.OCCUPANCY_SEC = exports.OCCUPANCY_PRI = exports.CONNECTION_ESTABLISHED = exports.TRACK = exports.TREATMENTS_WITH_CONFIG_BY_FLAGSETS = exports.TREATMENTS_WITH_CONFIG_BY_FLAGSET = exports.TREATMENTS_BY_FLAGSETS = exports.TREATMENTS_BY_FLAGSET = exports.TREATMENTS_WITH_CONFIG = exports.TREATMENT_WITH_CONFIG = exports.TREATMENTS = exports.TREATMENT = void 0;
4
+ exports.FLAGS_SPEC = exports.PAUSED = exports.ENABLED = exports.DISABLED = exports.NON_REQUESTED = exports.REQUESTED = exports.POLLING = exports.STREAMING = exports.AUTH_REJECTION = exports.SYNC_MODE_UPDATE = exports.ABLY_ERROR = exports.TOKEN_REFRESH = exports.SSE_CONNECTION_ERROR = exports.STREAMING_STATUS = exports.OCCUPANCY_SEC = exports.OCCUPANCY_PRI = exports.CONNECTION_ESTABLISHED = exports.TRACK = exports.TREATMENTS_WITH_CONFIG_BY_FLAGSETS = exports.TREATMENTS_WITH_CONFIG_BY_FLAGSET = exports.TREATMENTS_BY_FLAGSETS = exports.TREATMENTS_BY_FLAGSET = exports.TREATMENTS_WITH_CONFIG = exports.TREATMENT_WITH_CONFIG = exports.TREATMENTS = exports.TREATMENT = void 0;
5
5
  // Special treatments
6
6
  exports.CONTROL = 'control';
7
7
  exports.CONTROL_WITH_CONFIG = {
@@ -90,3 +90,4 @@ exports.NON_REQUESTED = 1;
90
90
  exports.DISABLED = 0;
91
91
  exports.ENABLED = 1;
92
92
  exports.PAUSED = 2;
93
+ exports.FLAGS_SPEC = '1.1';
@@ -93,7 +93,7 @@ function getEvaluation(log, splitJSON, key, attributes, storage) {
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 feature flag is type IN_SPLIT_TREATMENT or IN_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,7 +1,7 @@
1
1
  import { _Set } from '../../utils/lang/sets';
2
2
  import { Semver } from '../../utils/Semver';
3
3
  export function inListSemverMatcherContext(ruleAttr) {
4
- // @TODO move eventually to `matchersTransform` and validate for all matchers
4
+ // @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers
5
5
  if (!ruleAttr || ruleAttr.length === 0)
6
6
  throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type');
7
7
  var listOfSemvers = new _Set(ruleAttr.map(function (version) { return new Semver(version).version; }));
@@ -79,6 +79,7 @@ export function matchersTransform(matchers) {
79
79
  attribute: attribute,
80
80
  negate: negate,
81
81
  type: type,
82
+ name: matcherType,
82
83
  value: value,
83
84
  dataType: dataType // runtime input data type
84
85
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Extract whitelist as a set. Used by 'WHITELIST' matcher.
2
+ * Extract whitelist array.
3
3
  */
4
4
  export function whitelistTransform(whitelistObject) {
5
5
  return whitelistObject && whitelistObject.whitelist;
@@ -9,18 +9,18 @@ import { thenable } from '../../utils/promise/thenable';
9
9
  import { ENGINE_MATCHER_ERROR, ENGINE_MATCHER_RESULT } from '../../logger/constants';
10
10
  export function parser(log, conditions, storage) {
11
11
  var predicates = [];
12
- var _loop_1 = function (i) {
12
+ for (var i = 0; i < conditions.length; i++) {
13
13
  var _a = conditions[i], matcherGroup = _a.matcherGroup, partitions = _a.partitions, label = _a.label, conditionType = _a.conditionType;
14
14
  // transform data structure
15
15
  var matchers = matchersTransform(matcherGroup.matchers);
16
16
  // create a set of pure functions from the matcher's dto
17
- var expressions = matchers.map(function (matcherDto, index) {
17
+ var expressions = matchers.map(function (matcherDto) {
18
18
  var matcher;
19
19
  try {
20
20
  matcher = matcherFactory(log, matcherDto, storage);
21
21
  }
22
22
  catch (error) {
23
- log.error(ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
23
+ log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
24
24
  }
25
25
  // Evaluator function.
26
26
  return function (key, attributes, splitEvaluator) {
@@ -31,12 +31,11 @@ export function parser(log, conditions, storage) {
31
31
  result = matcher(value, splitEvaluator);
32
32
  }
33
33
  catch (error) {
34
- // @TODO should we propagate the error, to create impressions with label EXCEPTION?
35
- log.error(ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
34
+ log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
36
35
  }
37
36
  }
38
37
  function handleResult(result) {
39
- log.debug(ENGINE_MATCHER_RESULT, [matcherGroup.matchers[index].matcherType, result, matcherDto.value, value]); // @ts-ignore
38
+ log.debug(ENGINE_MATCHER_RESULT, [matcherDto.name, result, matcherDto.value, value]); // @ts-ignore
40
39
  return Boolean(result ^ matcherDto.negate);
41
40
  }
42
41
  return thenable(result) ?
@@ -48,14 +47,10 @@ export function parser(log, conditions, storage) {
48
47
  if (expressions.length === 0) {
49
48
  // reset any data collected during parsing
50
49
  predicates = [];
51
- return "break";
50
+ // and break the loop
51
+ break;
52
52
  }
53
53
  predicates.push(conditionContext(log, andCombinerContext(log, expressions), Treatments.parse(partitions), label, conditionType));
54
- };
55
- for (var i = 0; i < conditions.length; i++) {
56
- var state_1 = _loop_1(i);
57
- if (state_1 === "break")
58
- break;
59
54
  }
60
55
  // Instanciate evaluator given the set of conditions using if else if logic
61
56
  return ifElseIfCombinerContext(log, predicates);
@@ -6,8 +6,8 @@ export var codesDebug = codesInfo.concat([
6
6
  [c.ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Treatment found: %s'],
7
7
  [c.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, c.LOG_PREFIX_ENGINE_COMBINER + 'All predicates evaluated, no treatment found.'],
8
8
  [c.ENGINE_BUCKET, c.LOG_PREFIX_ENGINE + ': using algo "murmur" bucket %s for key %s using seed %s - treatment %s'],
9
- [c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
10
- [c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] will evaluate parent feature flag: "%s" with key: %s %s'],
9
+ [c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
10
+ [c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] will evaluate parent feature flag: "%s" with key: %s %s'],
11
11
  [c.ENGINE_VALUE, c.LOG_PREFIX_ENGINE_VALUE + 'Extracted attribute `%s`. %s will be used for matching.'],
12
12
  [c.ENGINE_SANITIZE, c.LOG_PREFIX_ENGINE + ':sanitize: Attempted to sanitize %s which should be of type %s. Sanitized and processed value => %s'],
13
13
  [c.ENGINE_MATCHER_RESULT, c.LOG_PREFIX_ENGINE_MATCHER + '[%s] Result: %s. Rule value: %s. Evaluation value: %s'],
@@ -1,6 +1,6 @@
1
1
  import { splitHttpClientFactory } from './splitHttpClient';
2
2
  import { objectAssign } from '../utils/lang/objectAssign';
3
- import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
3
+ import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT, FLAGS_SPEC } from '../utils/constants';
4
4
  import { ERROR_TOO_MANY_SETS } from '../logger/constants';
5
5
  var noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
6
6
  function userKeyToQueryParam(userKey) {
@@ -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?since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
41
+ var url = urls.sdk + "/splitChanges?s=" + FLAGS_SPEC + "&since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
42
42
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
43
43
  .catch(function (err) {
44
44
  if (err.statusCode === 414)
@@ -1,3 +1,4 @@
1
+ import { FLAGS_SPEC } from '../utils/constants';
1
2
  import { startsWith } from '../utils/lang';
2
3
  import { hash } from '../utils/murmur3/murmur3';
3
4
  var everythingAtTheEnd = /[^.]+$/;
@@ -62,9 +63,9 @@ var KeyBuilder = /** @class */ (function () {
62
63
  }());
63
64
  export { KeyBuilder };
64
65
  /**
65
- * Generates a murmur32 hash based on the authorization key and the feature flags filter query.
66
+ * Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
66
67
  * The hash is in hexadecimal format (8 characters max, 32 bits).
67
68
  */
68
69
  export function getStorageHash(settings) {
69
- return hash(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString).toString(16);
70
+ return hash(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString + "::" + FLAGS_SPEC).toString(16);
70
71
  }
@@ -86,3 +86,4 @@ export var NON_REQUESTED = 1;
86
86
  export var DISABLED = 0;
87
87
  export var ENABLED = 1;
88
88
  export var PAUSED = 2;
89
+ export var FLAGS_SPEC = '1.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.13.2-rc.6",
3
+ "version": "1.13.2-rc.7",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -151,7 +151,7 @@ function getEvaluation(
151
151
  const split = Engine.parse(log, splitJSON, storage);
152
152
  evaluation = split.getTreatment(key, attributes, evaluateFeature);
153
153
 
154
- // If the storage is async and the evaluated feature flag is type IN_SPLIT_TREATMENT or IN_SEGMENT, evaluation is thenable
154
+ // If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
155
155
  if (thenable(evaluation)) {
156
156
  return evaluation.then(result => {
157
157
  result.changeNumber = split.getChangeNumber();
@@ -2,7 +2,7 @@ import { _Set } from '../../utils/lang/sets';
2
2
  import { Semver } from '../../utils/Semver';
3
3
 
4
4
  export function inListSemverMatcherContext(ruleAttr: string[]) {
5
- // @TODO move eventually to `matchersTransform` and validate for all matchers
5
+ // @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers
6
6
  if (!ruleAttr || ruleAttr.length === 0) throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type');
7
7
 
8
8
  const listOfSemvers = new _Set(ruleAttr.map((version) => new Semver(version).version));
@@ -31,7 +31,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
31
31
  let type = matcherTypesMapper(matcherType);
32
32
  // As default input data type we use string (even for ALL_KEYS)
33
33
  let dataType = matcherDataTypes.STRING;
34
- let value: IMatcherDto['value'] = undefined;
34
+ let value = undefined;
35
35
 
36
36
  if (type === matcherTypes.IN_SEGMENT) {
37
37
  value = segmentTransform(userDefinedSegmentMatcherData as IInSegmentMatcherData);
@@ -98,6 +98,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
98
98
  attribute, // attribute over we should do the matching, undefined means 'use the key'
99
99
  negate, // should we negate the result?
100
100
  type, // which kind of matcher we should evaluate
101
+ name: matcherType,// name of the matcher for logging purposes
101
102
  value, // metadata used for the matching
102
103
  dataType // runtime input data type
103
104
  };
@@ -1,7 +1,7 @@
1
1
  import { ISplitMatcher } from '../../dtos/types';
2
2
 
3
3
  /**
4
- * Extract whitelist as a set. Used by 'WHITELIST' matcher.
4
+ * Extract whitelist array.
5
5
  */
6
6
  export function whitelistTransform(whitelistObject: ISplitMatcher['whitelistMatcherData']) {
7
7
  return whitelistObject && whitelistObject.whitelist;
@@ -28,12 +28,12 @@ export function parser(log: ILogger, conditions: ISplitCondition[], storage: ISt
28
28
  const matchers = matchersTransform(matcherGroup.matchers);
29
29
 
30
30
  // create a set of pure functions from the matcher's dto
31
- const expressions = matchers.map((matcherDto: IMatcherDto, index: number) => {
31
+ const expressions = matchers.map((matcherDto: IMatcherDto) => {
32
32
  let matcher: ReturnType<typeof matcherFactory>;
33
33
  try {
34
34
  matcher = matcherFactory(log, matcherDto, storage);
35
35
  } catch (error) {
36
- log.error(ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
36
+ log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
37
37
  }
38
38
 
39
39
  // Evaluator function.
@@ -45,13 +45,12 @@ export function parser(log: ILogger, conditions: ISplitCondition[], storage: ISt
45
45
  try {
46
46
  result = matcher(value, splitEvaluator);
47
47
  } catch (error) {
48
- // @TODO should we propagate the error, to create impressions with label EXCEPTION?
49
- log.error(ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
48
+ log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
50
49
  }
51
50
  }
52
51
 
53
52
  function handleResult(result: boolean) {
54
- log.debug(ENGINE_MATCHER_RESULT, [matcherGroup.matchers[index].matcherType, result, matcherDto.value, value]); // @ts-ignore
53
+ log.debug(ENGINE_MATCHER_RESULT, [matcherDto.name, result, matcherDto.value, value]); // @ts-ignore
55
54
  return Boolean(result ^ matcherDto.negate);
56
55
  }
57
56
 
@@ -10,6 +10,7 @@ export interface IDependencyMatcherValue {
10
10
 
11
11
  export interface IMatcherDto {
12
12
  type: number
13
+ name: string
13
14
  value?: string | number | boolean | string[] | IDependencyMatcherData | IBetweenMatcherData | IBetweenStringMatcherData | null
14
15
 
15
16
  attribute: string | null
@@ -7,8 +7,8 @@ export const codesDebug: [number, string][] = codesInfo.concat([
7
7
  [c.ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Treatment found: %s'],
8
8
  [c.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, c.LOG_PREFIX_ENGINE_COMBINER + 'All predicates evaluated, no treatment found.'],
9
9
  [c.ENGINE_BUCKET, c.LOG_PREFIX_ENGINE + ': using algo "murmur" bucket %s for key %s using seed %s - treatment %s'],
10
- [c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
11
- [c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] will evaluate parent feature flag: "%s" with key: %s %s'],
10
+ [c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
11
+ [c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] will evaluate parent feature flag: "%s" with key: %s %s'],
12
12
  [c.ENGINE_VALUE, c.LOG_PREFIX_ENGINE_VALUE + 'Extracted attribute `%s`. %s will be used for matching.'],
13
13
  [c.ENGINE_SANITIZE, c.LOG_PREFIX_ENGINE + ':sanitize: Attempted to sanitize %s which should be of type %s. Sanitized and processed value => %s'],
14
14
  [c.ENGINE_MATCHER_RESULT, c.LOG_PREFIX_ENGINE_MATCHER + '[%s] Result: %s. Rule value: %s. Evaluation value: %s'],
@@ -4,7 +4,7 @@ import { splitHttpClientFactory } from './splitHttpClient';
4
4
  import { ISplitApi } from './types';
5
5
  import { objectAssign } from '../utils/lang/objectAssign';
6
6
  import { ITelemetryTracker } from '../trackers/types';
7
- import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
7
+ import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT, FLAGS_SPEC } from '../utils/constants';
8
8
  import { ERROR_TOO_MANY_SETS } from '../logger/constants';
9
9
 
10
10
  const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
@@ -44,17 +44,16 @@ export function splitApiFactory(
44
44
  },
45
45
 
46
46
  fetchAuth(userMatchingKeys?: string[]) {
47
- let url = `${urls.auth}/v2/auth`;
48
- if (userMatchingKeys) { // accounting the possibility that `userMatchingKeys` is undefined (server-side API)
47
+ let url = `${urls.auth}/v2/auth?s=${FLAGS_SPEC}`;
48
+ if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
49
49
  const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
50
- if (queryParams) // accounting the possibility that `userKeys` and thus `queryParams` are empty
51
- url += '?' + queryParams;
50
+ if (queryParams) url += '&' + queryParams;
52
51
  }
53
52
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
54
53
  },
55
54
 
56
55
  fetchSplitChanges(since: number, noCache?: boolean, till?: number) {
57
- const url = `${urls.sdk}/splitChanges?since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
56
+ const url = `${urls.sdk}/splitChanges?s=${FLAGS_SPEC}&since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
58
57
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
59
58
  .catch((err) => {
60
59
  if (err.statusCode === 414) settings.log.error(ERROR_TOO_MANY_SETS);
@@ -1,4 +1,5 @@
1
1
  import { ISettings } from '../types';
2
+ import { FLAGS_SPEC } from '../utils/constants';
2
3
  import { startsWith } from '../utils/lang';
3
4
  import { hash } from '../utils/murmur3/murmur3';
4
5
 
@@ -81,9 +82,9 @@ export class KeyBuilder {
81
82
  }
82
83
 
83
84
  /**
84
- * Generates a murmur32 hash based on the authorization key and the feature flags filter query.
85
+ * Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
85
86
  * The hash is in hexadecimal format (8 characters max, 32 bits).
86
87
  */
87
88
  export function getStorageHash(settings: ISettings) {
88
- return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}`).toString(16);
89
+ return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}::${FLAGS_SPEC}`).toString(16);
89
90
  }
@@ -104,3 +104,5 @@ export const NON_REQUESTED = 1;
104
104
  export const DISABLED = 0;
105
105
  export const ENABLED = 1;
106
106
  export const PAUSED = 2;
107
+
108
+ export const FLAGS_SPEC = '1.1';
@@ -1,5 +1,5 @@
1
1
  import { ISplitMatcher } from '../../dtos/types';
2
2
  /**
3
- * Extract whitelist as a set. Used by 'WHITELIST' matcher.
3
+ * Extract whitelist array.
4
4
  */
5
5
  export declare function whitelistTransform(whitelistObject: ISplitMatcher['whitelistMatcherData']): string[] | null | undefined;
@@ -8,6 +8,7 @@ export interface IDependencyMatcherValue {
8
8
  }
9
9
  export interface IMatcherDto {
10
10
  type: number;
11
+ name: string;
11
12
  value?: string | number | boolean | string[] | IDependencyMatcherData | IBetweenMatcherData | IBetweenStringMatcherData | null;
12
13
  attribute: string | null;
13
14
  negate: boolean;
@@ -16,7 +16,7 @@ export declare class KeyBuilder {
16
16
  buildHashKey(): string;
17
17
  }
18
18
  /**
19
- * Generates a murmur32 hash based on the authorization key and the feature flags filter query.
19
+ * Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
20
20
  * The hash is in hexadecimal format (8 characters max, 32 bits).
21
21
  */
22
22
  export declare function getStorageHash(settings: ISettings): string;
@@ -78,3 +78,4 @@ export declare const NON_REQUESTED = 1;
78
78
  export declare const DISABLED = 0;
79
79
  export declare const ENABLED = 1;
80
80
  export declare const PAUSED = 2;
81
+ export declare const FLAGS_SPEC = "1.1";