@splitsoftware/splitio-commons 1.13.2-rc.2 → 1.13.2-rc.3

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 (66) hide show
  1. package/CHANGES.txt +0 -1
  2. package/cjs/evaluator/matchers/gte.js +3 -3
  3. package/cjs/evaluator/matchers/index.js +11 -1
  4. package/cjs/evaluator/matchers/matcherTypes.js +6 -1
  5. package/cjs/evaluator/matchers/semver_between.js +14 -0
  6. package/cjs/evaluator/matchers/semver_eq.js +12 -0
  7. package/cjs/evaluator/matchers/semver_gte.js +12 -0
  8. package/cjs/evaluator/matchers/semver_inlist.js +14 -0
  9. package/cjs/evaluator/matchers/semver_lte.js +12 -0
  10. package/cjs/evaluator/matchersTransform/index.js +20 -13
  11. package/cjs/services/splitApi.js +5 -5
  12. package/cjs/storages/KeyBuilder.js +2 -3
  13. package/cjs/utils/Semver.js +100 -0
  14. package/cjs/utils/constants/index.js +1 -2
  15. package/cjs/utils/settingsValidation/logger/pluggableLogger.js +1 -1
  16. package/esm/evaluator/matchers/gte.js +3 -3
  17. package/esm/evaluator/matchers/index.js +11 -1
  18. package/esm/evaluator/matchers/matcherTypes.js +6 -1
  19. package/esm/evaluator/matchers/semver_between.js +10 -0
  20. package/esm/evaluator/matchers/semver_eq.js +8 -0
  21. package/esm/evaluator/matchers/semver_gte.js +8 -0
  22. package/esm/evaluator/matchers/semver_inlist.js +10 -0
  23. package/esm/evaluator/matchers/semver_lte.js +8 -0
  24. package/esm/evaluator/matchersTransform/index.js +20 -13
  25. package/esm/services/splitApi.js +6 -6
  26. package/esm/storages/KeyBuilder.js +2 -3
  27. package/esm/utils/Semver.js +97 -0
  28. package/esm/utils/constants/index.js +0 -1
  29. package/esm/utils/settingsValidation/logger/pluggableLogger.js +1 -1
  30. package/package.json +1 -2
  31. package/src/dtos/types.ts +34 -1
  32. package/src/evaluator/matchers/between.ts +1 -1
  33. package/src/evaluator/matchers/boolean.ts +1 -1
  34. package/src/evaluator/matchers/cont_all.ts +1 -1
  35. package/src/evaluator/matchers/cont_any.ts +1 -1
  36. package/src/evaluator/matchers/cont_str.ts +1 -1
  37. package/src/evaluator/matchers/eq.ts +1 -1
  38. package/src/evaluator/matchers/eq_set.ts +1 -1
  39. package/src/evaluator/matchers/ew.ts +1 -1
  40. package/src/evaluator/matchers/gte.ts +4 -4
  41. package/src/evaluator/matchers/index.ts +28 -18
  42. package/src/evaluator/matchers/lte.ts +1 -1
  43. package/src/evaluator/matchers/matcherTypes.ts +6 -1
  44. package/src/evaluator/matchers/part_of.ts +1 -1
  45. package/src/evaluator/matchers/semver_between.ts +18 -0
  46. package/src/evaluator/matchers/semver_eq.ts +13 -0
  47. package/src/evaluator/matchers/semver_gte.ts +13 -0
  48. package/src/evaluator/matchers/semver_inlist.ts +15 -0
  49. package/src/evaluator/matchers/semver_lte.ts +13 -0
  50. package/src/evaluator/matchers/string.ts +1 -1
  51. package/src/evaluator/matchers/sw.ts +1 -1
  52. package/src/evaluator/matchers/whitelist.ts +1 -1
  53. package/src/evaluator/matchersTransform/index.ts +32 -21
  54. package/src/evaluator/types.ts +2 -2
  55. package/src/services/splitApi.ts +6 -5
  56. package/src/storages/KeyBuilder.ts +2 -3
  57. package/src/utils/Semver.ts +107 -0
  58. package/src/utils/constants/index.ts +0 -2
  59. package/src/utils/settingsValidation/logger/pluggableLogger.ts +1 -1
  60. package/types/dtos/types.d.ts +26 -1
  61. package/types/evaluator/matchers/semver_between.d.ts +2 -2
  62. package/types/evaluator/matchers/semver_inlist.d.ts +3 -0
  63. package/types/evaluator/types.d.ts +2 -2
  64. package/types/storages/KeyBuilder.d.ts +1 -1
  65. package/types/utils/constants/index.d.ts +0 -1
  66. package/types/utils/semVer.d.ts +8 -15
@@ -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, FLAGS_SPEC } from '../utils/constants';
7
+ import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } 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,16 +44,17 @@ export function splitApiFactory(
44
44
  },
45
45
 
46
46
  fetchAuth(userMatchingKeys?: string[]) {
47
- let url = `${urls.auth}/v2/auth?s=${FLAGS_SPEC}`;
48
- if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
47
+ let url = `${urls.auth}/v2/auth`;
48
+ if (userMatchingKeys) { // accounting the possibility that `userMatchingKeys` is undefined (server-side API)
49
49
  const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
50
- if (queryParams) url += '&' + queryParams;
50
+ if (queryParams) // accounting the possibility that `userKeys` and thus `queryParams` are empty
51
+ url += '?' + queryParams;
51
52
  }
52
53
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
53
54
  },
54
55
 
55
56
  fetchSplitChanges(since: number, noCache?: boolean, till?: number) {
56
- const url = `${urls.sdk}/splitChanges?s=${FLAGS_SPEC}&since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
57
+ const url = `${urls.sdk}/splitChanges?since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
57
58
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
58
59
  .catch((err) => {
59
60
  if (err.statusCode === 414) settings.log.error(ERROR_TOO_MANY_SETS);
@@ -1,5 +1,4 @@
1
1
  import { ISettings } from '../types';
2
- import { FLAGS_SPEC } from '../utils/constants';
3
2
  import { startsWith } from '../utils/lang';
4
3
  import { hash } from '../utils/murmur3/murmur3';
5
4
 
@@ -82,9 +81,9 @@ export class KeyBuilder {
82
81
  }
83
82
 
84
83
  /**
85
- * Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
84
+ * Generates a murmur32 hash based on the authorization key and the feature flags filter query.
86
85
  * The hash is in hexadecimal format (8 characters max, 32 bits).
87
86
  */
88
87
  export function getStorageHash(settings: ISettings) {
89
- return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}::${FLAGS_SPEC}`).toString(16);
88
+ return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}`).toString(16);
90
89
  }
@@ -0,0 +1,107 @@
1
+ const NUMERIC_IDENTIFIER_REGEX = /^[0-9]+$/;
2
+
3
+ const METADATA_DELIMITER = '+';
4
+ const PRERELEASE_DELIMITER = '-';
5
+ const VALUE_DELIMITER = '.';
6
+
7
+ /**
8
+ * Compares two strings. If both strings are numeric identifiers, they are compared numerically. Otherwise, they are compared lexicographically.
9
+ * This could be implemented using `a.localeCompare(b, undefined, { numeric: true })` but locale options are not broadly supported.
10
+ */
11
+ function compareStrings(a: string, b: string): number {
12
+ if (NUMERIC_IDENTIFIER_REGEX.test(a) && NUMERIC_IDENTIFIER_REGEX.test(b)) {
13
+ const result = a.length - b.length;
14
+ if (result !== 0) {
15
+ return result;
16
+ }
17
+ }
18
+ return a < b ? -1 : a > b ? 1 : 0;
19
+ }
20
+
21
+ // Sanitizes a numeric identifier by removing leading zeros
22
+ function sanitizeNumericIdentifier(value: string): string {
23
+ return value.replace(/^0+(?=\d)/, '');
24
+ }
25
+
26
+ function throwError(version: string) {
27
+ throw new Error('Unable to convert to Semver, incorrect format: ' + version);
28
+ }
29
+
30
+ export class Semver {
31
+
32
+ private readonly _major: string;
33
+ private readonly _minor: string;
34
+ private readonly _patch: string;
35
+ private readonly _preRelease: string[];
36
+ private readonly _isStable: boolean;
37
+
38
+ // Version string for 'equal' and 'in list' comparisons
39
+ public readonly version: string;
40
+
41
+ public constructor(version: string) {
42
+ // Separate metadata if exists
43
+ let index = version.indexOf(METADATA_DELIMITER);
44
+ let [vWithoutMetadata, metadata] = index === -1 ? [version] : [version.slice(0, index), version.slice(index + 1)];
45
+ if (metadata === '') throwError(version);
46
+
47
+ // Set pre-release versions if exists
48
+ index = vWithoutMetadata.indexOf(PRERELEASE_DELIMITER);
49
+ if (index === -1) {
50
+ this._isStable = true;
51
+ this._preRelease = [];
52
+ } else {
53
+ this._isStable = false;
54
+ this._preRelease = vWithoutMetadata.slice(index + 1).split(VALUE_DELIMITER).map((value) => {
55
+ if (!value) throwError(version);
56
+ return NUMERIC_IDENTIFIER_REGEX.test(value) ?
57
+ sanitizeNumericIdentifier(value) :
58
+ value;
59
+ });
60
+ vWithoutMetadata = vWithoutMetadata.slice(0, index);
61
+ }
62
+
63
+ // Set major, minor, and patch versions
64
+ const vParts = vWithoutMetadata.split(VALUE_DELIMITER).map((value) => {
65
+ if (!value || !NUMERIC_IDENTIFIER_REGEX.test(value)) throwError(version);
66
+ return sanitizeNumericIdentifier(value);
67
+ });
68
+
69
+ if (vParts.length !== 3) throwError(version);
70
+ this._major = vParts[0];
71
+ this._minor = vParts[1];
72
+ this._patch = vParts[2];
73
+
74
+ // Set version string
75
+ this.version = vParts.join(VALUE_DELIMITER);
76
+ if (this._preRelease.length) this.version += PRERELEASE_DELIMITER + this._preRelease.join(VALUE_DELIMITER);
77
+ if (metadata) this.version += METADATA_DELIMITER + metadata;
78
+ }
79
+
80
+ /**
81
+ * Precedence comparision between 2 Semver objects.
82
+ *
83
+ * @return `0` if `this === toCompare`, `-1` if `this < toCompare`, and `1` if `this > toCompare`
84
+ */
85
+ public compare(toCompare: Semver): number {
86
+ if (this.version === toCompare.version) return 0;
87
+
88
+ let result = compareStrings(this._major, toCompare._major);
89
+ if (result !== 0) return result;
90
+
91
+ result = compareStrings(this._minor, toCompare._minor);
92
+ if (result !== 0) return result;
93
+
94
+ result = compareStrings(this._patch, toCompare._patch);
95
+ if (result !== 0) return result;
96
+
97
+ if (!this._isStable && toCompare._isStable) return -1;
98
+ if (this._isStable && !toCompare._isStable) return 1;
99
+
100
+ for (let i = 0, length = Math.min(this._preRelease.length, toCompare._preRelease.length); i < length; i++) {
101
+ const result = compareStrings(this._preRelease[i], toCompare._preRelease[i]);
102
+ if (result !== 0) return result;
103
+ }
104
+
105
+ return this._preRelease.length - toCompare._preRelease.length;
106
+ }
107
+ }
@@ -104,5 +104,3 @@ 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';
@@ -4,7 +4,7 @@ import { LogLevel } from '../../../types';
4
4
  import { getLogLevel } from './commons';
5
5
 
6
6
  function isLogger(log: any): log is ILogger {
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
 
10
10
  // By default it starts disabled.
@@ -11,6 +11,10 @@ export interface IBetweenMatcherData {
11
11
  start: number;
12
12
  end: number;
13
13
  }
14
+ export interface IBetweenStringMatcherData {
15
+ start: string;
16
+ end: string;
17
+ }
14
18
  export interface IWhitelistMatcherData {
15
19
  whitelist: string[];
16
20
  }
@@ -35,6 +39,7 @@ interface ISplitMatcherBase {
35
39
  dependencyMatcherData?: null | IDependencyMatcherData;
36
40
  booleanMatcherData?: null | boolean;
37
41
  stringMatcherData?: null | string;
42
+ betweenStringMatcherData?: null | IBetweenStringMatcherData;
38
43
  }
39
44
  interface IAllKeysMatcher extends ISplitMatcherBase {
40
45
  matcherType: 'ALL_KEYS';
@@ -103,7 +108,27 @@ interface IMatchesStringMatcher extends ISplitMatcherBase {
103
108
  matcherType: 'MATCHES_STRING';
104
109
  stringMatcherData: string;
105
110
  }
106
- export declare type ISplitMatcher = IAllKeysMatcher | IInSegmentMatcher | IWhitelistMatcher | IEqualToMatcher | IGreaterThanOrEqualToMatcher | ILessThanOrEqualToMatcher | IBetweenMatcher | IEqualToSetMatcher | IContainsAnyOfSetMatcher | IContainsAllOfSetMatcher | IPartOfSetMatcher | IStartsWithMatcher | IEndsWithMatcher | IContainsStringMatcher | IInSplitTreatmentMatcher | IEqualToBooleanMatcher | IMatchesStringMatcher;
111
+ interface IEqualToSemverMatcher extends ISplitMatcherBase {
112
+ matcherType: 'EQUAL_TO_SEMVER';
113
+ stringMatcherData: string;
114
+ }
115
+ interface IGreaterThanOrEqualToSemverMatcher extends ISplitMatcherBase {
116
+ matcherType: 'GREATER_THAN_OR_EQUAL_TO_SEMVER';
117
+ stringMatcherData: string;
118
+ }
119
+ interface ILessThanOrEqualToSemverMatcher extends ISplitMatcherBase {
120
+ matcherType: 'LESS_THAN_OR_EQUAL_TO_SEMVER';
121
+ stringMatcherData: string;
122
+ }
123
+ interface IBetweenSemverMatcher extends ISplitMatcherBase {
124
+ matcherType: 'BETWEEN_SEMVER';
125
+ betweenStringMatcherData: IBetweenStringMatcherData;
126
+ }
127
+ interface IInListSemverMatcher extends ISplitMatcherBase {
128
+ matcherType: 'IN_LIST_SEMVER';
129
+ whitelistMatcherData: IWhitelistMatcherData;
130
+ }
131
+ export declare type ISplitMatcher = IAllKeysMatcher | IInSegmentMatcher | IWhitelistMatcher | IEqualToMatcher | IGreaterThanOrEqualToMatcher | ILessThanOrEqualToMatcher | IBetweenMatcher | IEqualToSetMatcher | IContainsAnyOfSetMatcher | IContainsAllOfSetMatcher | IPartOfSetMatcher | IStartsWithMatcher | IEndsWithMatcher | IContainsStringMatcher | IInSplitTreatmentMatcher | IEqualToBooleanMatcher | IMatchesStringMatcher | IEqualToSemverMatcher | IGreaterThanOrEqualToSemverMatcher | ILessThanOrEqualToSemverMatcher | IBetweenSemverMatcher | IInListSemverMatcher;
107
132
  /** Split object */
108
133
  export interface ISplitPartition {
109
134
  treatment: string;
@@ -1,3 +1,3 @@
1
- import { IBetweenMatcherData } from '../../dtos/types';
1
+ import { IBetweenStringMatcherData } from '../../dtos/types';
2
2
  import { ILogger } from '../../logger/types';
3
- export declare function betweenSemverMatcherContext(log: ILogger, ruleVO: IBetweenMatcherData): (key: string) => boolean;
3
+ export declare function betweenSemverMatcherContext(log: ILogger, ruleAttr: IBetweenStringMatcherData): (key: string) => boolean;
@@ -0,0 +1,3 @@
1
+ import { ISet } from '../../utils/lang/sets';
2
+ import { ILogger } from '../../logger/types';
3
+ export declare function inListSemverMatcherContext(log: ILogger, ruleAttr: ISet<string>): (runtimeAttr: string) => boolean;
@@ -1,4 +1,4 @@
1
- import { IBetweenMatcherData, IDependencyMatcherData, MaybeThenable } from '../dtos/types';
1
+ import { IBetweenMatcherData, IBetweenStringMatcherData, IDependencyMatcherData, MaybeThenable } from '../dtos/types';
2
2
  import { IStorageAsync, IStorageSync } from '../storages/types';
3
3
  import { ISet } from '../utils/lang/sets';
4
4
  import { SplitIO } from '../types';
@@ -9,7 +9,7 @@ export interface IDependencyMatcherValue {
9
9
  }
10
10
  export interface IMatcherDto {
11
11
  type: number;
12
- value?: string | number | boolean | string[] | IDependencyMatcherData | ISet<string> | IBetweenMatcherData | null;
12
+ value?: string | number | boolean | string[] | IDependencyMatcherData | ISet<string> | IBetweenMatcherData | IBetweenStringMatcherData | null;
13
13
  attribute: string | null;
14
14
  negate: boolean;
15
15
  dataType: string;
@@ -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, the feature flags filter query, and version of SplitChanges API.
19
+ * Generates a murmur32 hash based on the authorization key and the feature flags filter query.
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,4 +78,3 @@ 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";
@@ -1,22 +1,15 @@
1
1
  export declare class Semver {
2
- private _major;
3
- private _minor;
4
- private _patch;
5
- private _preRelease;
6
- private _isStable;
7
- private _oVersion;
2
+ private readonly _major;
3
+ private readonly _minor;
4
+ private readonly _patch;
5
+ private readonly _preRelease;
6
+ private readonly _isStable;
7
+ readonly version: string;
8
8
  constructor(version: string);
9
- private removeMetadataIfExists;
10
- isEqualTo(toCompare: Semver): boolean;
11
- isGreaterThanOrEqualTo(toCompare: Semver): boolean;
12
- isLessThanOrEqualTo(toCompare: Semver): boolean;
13
- isBetween(start: Semver, end: Semver): boolean;
14
9
  /**
15
10
  * Precedence comparision between 2 Semver objects.
16
11
  *
17
- * @return the value {@code 0} if {@code this == toCompare};
18
- * a value less than {@code 0} if {@code this < toCompare}; and
19
- * a value greater than {@code 0} if {@code this > toCompare}
12
+ * @return `0` if `this === toCompare`, `-1` if `this < toCompare`, and `1` if `this > toCompare`
20
13
  */
21
- private compare;
14
+ compare(toCompare: Semver): number;
22
15
  }