@splitsoftware/splitio-commons 3.0.1-rc.3 → 3.1.1-rc.0

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.
package/CHANGES.txt CHANGED
@@ -1,5 +1,10 @@
1
- 3.1.0 (July XX, 2026)
1
+ 3.2.0 (July 31, 2026)
2
+ - Added support for AI configs.
3
+ - Removed Configs SDK-related types and modules and moved them to the Configs SDK repository.
4
+
5
+ 3.1.0 (July 23, 2026)
2
6
  - Added support for rule-based segments in /api/v1/configs endpoint.
7
+ - Updated polling flow to fetch new referenced segments immediately.
3
8
 
4
9
  3.0.0 (June 26, 2026)
5
10
  - Extracted SDK lifecycle methods (`init`, `flush`, and `destroy`) into a reusable `sdkLifecycle` module.
@@ -33,13 +33,13 @@ function evaluateFeature(log, key, definitionName, attributes, storage, options)
33
33
  // Exception on sync storage. Not possible ATM with InMemory and InLocal storages.
34
34
  return EVALUATION_EXCEPTION;
35
35
  }
36
- if ((0, thenable_1.thenable)(definition)) {
37
- return definition.then(function (definition) { return getEvaluation(log, key, definition, attributes, storage, options); }).catch(
36
+ return (0, thenable_1.thenable)(definition) ?
37
+ definition.then(function (definition) { return getEvaluation(log, key, definition, attributes, storage, options); })
38
+ .catch(
38
39
  // Exception on async storage. For example, when the storage is redis or
39
40
  // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
40
- function () { return EVALUATION_EXCEPTION; });
41
- }
42
- return getEvaluation(log, key, definition, attributes, storage, options);
41
+ function () { return EVALUATION_EXCEPTION; }) :
42
+ getEvaluation(log, key, definition, attributes, storage, options);
43
43
  }
44
44
  exports.evaluateFeature = evaluateFeature;
45
45
  function evaluateFeatures(log, key, definitionNames, attributes, storage, options) {
@@ -53,11 +53,10 @@ function evaluateFeatures(log, key, definitionNames, attributes, storage, option
53
53
  }
54
54
  return (0, thenable_1.thenable)(definitions) ?
55
55
  definitions.then(function (definitions) { return getEvaluations(log, key, definitionNames, definitions, attributes, storage, options); })
56
- .catch(function () {
57
- // Exception on async storage. For example, when the storage is redis or
58
- // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
59
- return treatmentsException(definitionNames);
60
- }) :
56
+ .catch(
57
+ // Exception on async storage. For example, when the storage is redis or
58
+ // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
59
+ function () { return treatmentsException(definitionNames); }) :
61
60
  getEvaluations(log, key, definitionNames, definitions, attributes, storage, options);
62
61
  }
63
62
  exports.evaluateFeatures = evaluateFeatures;
@@ -89,33 +88,26 @@ function evaluateFeaturesByFlagSets(log, key, flagSets, attributes, storage, met
89
88
  // evaluate related features
90
89
  return (0, thenable_1.thenable)(storedFlagNames) ?
91
90
  storedFlagNames.then(function (storedFlagNames) { return evaluate(storedFlagNames); })
92
- .catch(function () {
93
- return {};
94
- }) :
91
+ .catch(function () { return ({}); }) :
95
92
  evaluate(storedFlagNames);
96
93
  }
97
94
  exports.evaluateFeaturesByFlagSets = evaluateFeaturesByFlagSets;
95
+ function setEvaluationDataFromDefinition(evaluation, definition, options) {
96
+ evaluation.changeNumber = definition.changeNumber;
97
+ evaluation.config = definition.configurations && definition.configurations[evaluation.treatment] || null;
98
+ evaluation.type = definition.type;
99
+ evaluation.subtype = definition.subtype;
100
+ // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
101
+ evaluation.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
102
+ return evaluation;
103
+ }
98
104
  function getEvaluation(log, key, definition, attributes, storage, options) {
99
105
  if (definition) {
100
106
  var split = (0, Engine_1.engineParser)(log, definition, storage);
101
107
  var evaluation = split.getTreatment(key, attributes, evaluateFeature);
102
- // If the storage is async and the evaluated definition uses segments or dependencies, evaluation is thenable
103
- if ((0, thenable_1.thenable)(evaluation)) {
104
- return evaluation.then(function (result) {
105
- result.changeNumber = definition.changeNumber;
106
- result.config = definition.configurations && definition.configurations[result.treatment] || null;
107
- // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
108
- result.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
109
- return result;
110
- });
111
- }
112
- else {
113
- evaluation.changeNumber = definition.changeNumber;
114
- evaluation.config = definition.configurations && definition.configurations[evaluation.treatment] || null;
115
- // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
116
- evaluation.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
117
- }
118
- return evaluation;
108
+ return (0, thenable_1.thenable)(evaluation) ?
109
+ evaluation.then(function (result) { return setEvaluationDataFromDefinition(result, definition, options); }) :
110
+ setEvaluationDataFromDefinition(evaluation, definition, options);
119
111
  }
120
112
  return EVALUATION_DEFINITION_NOT_FOUND;
121
113
  }
@@ -124,14 +116,11 @@ function getEvaluations(log, key, definitionNames, definitions, attributes, stor
124
116
  var thenables = [];
125
117
  definitionNames.forEach(function (definitionName) {
126
118
  var evaluation = getEvaluation(log, key, definitions[definitionName], attributes, storage, options);
127
- if ((0, thenable_1.thenable)(evaluation)) {
119
+ (0, thenable_1.thenable)(evaluation) ?
128
120
  thenables.push(evaluation.then(function (res) {
129
121
  result[definitionName] = res;
130
- }));
131
- }
132
- else {
122
+ })) :
133
123
  result[definitionName] = evaluation;
134
- }
135
124
  });
136
125
  return thenables.length > 0 ? Promise.all(thenables).then(function () { return result; }) : result;
137
126
  }
@@ -150,12 +139,10 @@ function evaluateDefaultTreatment(definitionName, storage) {
150
139
  exports.evaluateDefaultTreatment = evaluateDefaultTreatment;
151
140
  function getDefaultTreatment(definition) {
152
141
  if (definition) {
153
- return {
142
+ return setEvaluationDataFromDefinition({
154
143
  treatment: definition.defaultTreatment,
155
- label: labels_1.NO_CONDITION_MATCH,
156
- config: definition.configurations && definition.configurations[definition.defaultTreatment] || null,
157
- changeNumber: definition.changeNumber
158
- };
144
+ label: labels_1.NO_CONDITION_MATCH // "default rule"
145
+ }, definition);
159
146
  }
160
147
  return EVALUATION_DEFINITION_NOT_FOUND;
161
148
  }
@@ -14,14 +14,14 @@ var EVENT_NAME = 'for visibilitychange and pagehide events.';
14
14
  */
15
15
  var BrowserSignalListener = /** @class */ (function () {
16
16
  function BrowserSignalListener(_a) {
17
- var syncManager = _a.syncManager, settings = _a.settings, storage = _a.storage, serviceApi = _a.serviceApi, entityType = _a.entityType;
17
+ var syncManager = _a.syncManager, settings = _a.settings, storage = _a.storage, serviceApi = _a.serviceApi;
18
18
  this.syncManager = syncManager;
19
19
  this.settings = settings;
20
20
  this.storage = storage;
21
21
  this.serviceApi = serviceApi;
22
22
  this.flushData = this.flushData.bind(this);
23
23
  this.flushDataIfHidden = this.flushDataIfHidden.bind(this);
24
- this.fromImpressionsCollector = impressionsSubmitter_1.fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled, entityType);
24
+ this.fromImpressionsCollector = impressionsSubmitter_1.fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled);
25
25
  }
26
26
  /**
27
27
  * start method.
@@ -16,7 +16,7 @@ function isExpired(credential) {
16
16
  function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
17
17
  var urls = settings.urls, log = settings.log;
18
18
  function fetchAuth() {
19
- var url = "".concat(urls.auth, "/api/v3/auth?capabilities=config");
19
+ var url = "".concat(urls.auth, "/api/v3/auth?capabilities=config,aiconfig");
20
20
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(constants_2.TOKEN), false, true);
21
21
  }
22
22
  var authenticate = (0, AuthClient_1.authenticateFactory)(fetchAuth);
@@ -28,6 +28,7 @@ function impressionsToJSON(impressions, metadata) {
28
28
  m: impression.time,
29
29
  pt: impression.pt,
30
30
  properties: impression.properties
31
+ // @TODO set entityType
31
32
  }
32
33
  };
33
34
  return JSON.stringify(impressionWithMetadata);
@@ -7,7 +7,7 @@ var constants_1 = require("../../logger/constants");
7
7
  /**
8
8
  * Converts `impressions` data from cache into request payload.
9
9
  */
10
- function fromImpressionsCollector(sendLabels, entityType, data) {
10
+ function fromImpressionsCollector(sendLabels, data) {
11
11
  var groupedByFeature = (0, lang_1.groupBy)(data, 'feature');
12
12
  var dto = [];
13
13
  (0, lang_1.forOwn)(groupedByFeature, function (value, name) {
@@ -23,7 +23,8 @@ function fromImpressionsCollector(sendLabels, entityType, data) {
23
23
  b: entry.bucketingKey,
24
24
  pt: entry.pt,
25
25
  properties: entry.properties,
26
- et: entityType, // Definition type
26
+ // @ts-expect-error - entityType is not yet public. @TODO: add to SplitIO.ImpressionDTO type
27
+ et: entry.entityType, // Definition type
27
28
  };
28
29
  })
29
30
  });
@@ -35,9 +36,9 @@ exports.fromImpressionsCollector = fromImpressionsCollector;
35
36
  * Submitter that periodically posts impressions data
36
37
  */
37
38
  function impressionsSubmitterFactory(params) {
38
- var _a = params.settings, log = _a.log, impressionsRefreshRate = _a.scheduler.impressionsRefreshRate, labelsEnabled = _a.core.labelsEnabled, postTestImpressionsBulk = params.serviceApi.postTestImpressionsBulk, impressions = params.storage.impressions, entityType = params.entityType;
39
+ var _a = params.settings, log = _a.log, impressionsRefreshRate = _a.scheduler.impressionsRefreshRate, labelsEnabled = _a.core.labelsEnabled, postTestImpressionsBulk = params.serviceApi.postTestImpressionsBulk, impressions = params.storage.impressions;
39
40
  // retry impressions only once.
40
- var syncTask = (0, submitter_1.submitterFactory)(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled, entityType), 1);
41
+ var syncTask = (0, submitter_1.submitterFactory)(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled), 1);
41
42
  // register impressions submitter to be executed when impressions cache is full
42
43
  impressions.setOnFullQueueCb(function () {
43
44
  if (syncTask.isRunning()) {
@@ -30,13 +30,13 @@ export function evaluateFeature(log, key, definitionName, attributes, storage, o
30
30
  // Exception on sync storage. Not possible ATM with InMemory and InLocal storages.
31
31
  return EVALUATION_EXCEPTION;
32
32
  }
33
- if (thenable(definition)) {
34
- return definition.then(function (definition) { return getEvaluation(log, key, definition, attributes, storage, options); }).catch(
33
+ return thenable(definition) ?
34
+ definition.then(function (definition) { return getEvaluation(log, key, definition, attributes, storage, options); })
35
+ .catch(
35
36
  // Exception on async storage. For example, when the storage is redis or
36
37
  // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
37
- function () { return EVALUATION_EXCEPTION; });
38
- }
39
- return getEvaluation(log, key, definition, attributes, storage, options);
38
+ function () { return EVALUATION_EXCEPTION; }) :
39
+ getEvaluation(log, key, definition, attributes, storage, options);
40
40
  }
41
41
  export function evaluateFeatures(log, key, definitionNames, attributes, storage, options) {
42
42
  var definitions;
@@ -49,11 +49,10 @@ export function evaluateFeatures(log, key, definitionNames, attributes, storage,
49
49
  }
50
50
  return thenable(definitions) ?
51
51
  definitions.then(function (definitions) { return getEvaluations(log, key, definitionNames, definitions, attributes, storage, options); })
52
- .catch(function () {
53
- // Exception on async storage. For example, when the storage is redis or
54
- // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
55
- return treatmentsException(definitionNames);
56
- }) :
52
+ .catch(
53
+ // Exception on async storage. For example, when the storage is redis or
54
+ // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
55
+ function () { return treatmentsException(definitionNames); }) :
57
56
  getEvaluations(log, key, definitionNames, definitions, attributes, storage, options);
58
57
  }
59
58
  export function evaluateFeaturesByFlagSets(log, key, flagSets, attributes, storage, method, options) {
@@ -84,32 +83,25 @@ export function evaluateFeaturesByFlagSets(log, key, flagSets, attributes, stora
84
83
  // evaluate related features
85
84
  return thenable(storedFlagNames) ?
86
85
  storedFlagNames.then(function (storedFlagNames) { return evaluate(storedFlagNames); })
87
- .catch(function () {
88
- return {};
89
- }) :
86
+ .catch(function () { return ({}); }) :
90
87
  evaluate(storedFlagNames);
91
88
  }
89
+ function setEvaluationDataFromDefinition(evaluation, definition, options) {
90
+ evaluation.changeNumber = definition.changeNumber;
91
+ evaluation.config = definition.configurations && definition.configurations[evaluation.treatment] || null;
92
+ evaluation.type = definition.type;
93
+ evaluation.subtype = definition.subtype;
94
+ // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
95
+ evaluation.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
96
+ return evaluation;
97
+ }
92
98
  function getEvaluation(log, key, definition, attributes, storage, options) {
93
99
  if (definition) {
94
100
  var split = engineParser(log, definition, storage);
95
101
  var evaluation = split.getTreatment(key, attributes, evaluateFeature);
96
- // If the storage is async and the evaluated definition uses segments or dependencies, evaluation is thenable
97
- if (thenable(evaluation)) {
98
- return evaluation.then(function (result) {
99
- result.changeNumber = definition.changeNumber;
100
- result.config = definition.configurations && definition.configurations[result.treatment] || null;
101
- // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
102
- result.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
103
- return result;
104
- });
105
- }
106
- else {
107
- evaluation.changeNumber = definition.changeNumber;
108
- evaluation.config = definition.configurations && definition.configurations[evaluation.treatment] || null;
109
- // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
110
- evaluation.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
111
- }
112
- return evaluation;
102
+ return thenable(evaluation) ?
103
+ evaluation.then(function (result) { return setEvaluationDataFromDefinition(result, definition, options); }) :
104
+ setEvaluationDataFromDefinition(evaluation, definition, options);
113
105
  }
114
106
  return EVALUATION_DEFINITION_NOT_FOUND;
115
107
  }
@@ -118,14 +110,11 @@ function getEvaluations(log, key, definitionNames, definitions, attributes, stor
118
110
  var thenables = [];
119
111
  definitionNames.forEach(function (definitionName) {
120
112
  var evaluation = getEvaluation(log, key, definitions[definitionName], attributes, storage, options);
121
- if (thenable(evaluation)) {
113
+ thenable(evaluation) ?
122
114
  thenables.push(evaluation.then(function (res) {
123
115
  result[definitionName] = res;
124
- }));
125
- }
126
- else {
116
+ })) :
127
117
  result[definitionName] = evaluation;
128
- }
129
118
  });
130
119
  return thenables.length > 0 ? Promise.all(thenables).then(function () { return result; }) : result;
131
120
  }
@@ -143,12 +132,10 @@ export function evaluateDefaultTreatment(definitionName, storage) {
143
132
  }
144
133
  function getDefaultTreatment(definition) {
145
134
  if (definition) {
146
- return {
135
+ return setEvaluationDataFromDefinition({
147
136
  treatment: definition.defaultTreatment,
148
- label: NO_CONDITION_MATCH,
149
- config: definition.configurations && definition.configurations[definition.defaultTreatment] || null,
150
- changeNumber: definition.changeNumber
151
- };
137
+ label: NO_CONDITION_MATCH // "default rule"
138
+ }, definition);
152
139
  }
153
140
  return EVALUATION_DEFINITION_NOT_FOUND;
154
141
  }
@@ -11,14 +11,14 @@ var EVENT_NAME = 'for visibilitychange and pagehide events.';
11
11
  */
12
12
  var BrowserSignalListener = /** @class */ (function () {
13
13
  function BrowserSignalListener(_a) {
14
- var syncManager = _a.syncManager, settings = _a.settings, storage = _a.storage, serviceApi = _a.serviceApi, entityType = _a.entityType;
14
+ var syncManager = _a.syncManager, settings = _a.settings, storage = _a.storage, serviceApi = _a.serviceApi;
15
15
  this.syncManager = syncManager;
16
16
  this.settings = settings;
17
17
  this.storage = storage;
18
18
  this.serviceApi = serviceApi;
19
19
  this.flushData = this.flushData.bind(this);
20
20
  this.flushDataIfHidden = this.flushDataIfHidden.bind(this);
21
- this.fromImpressionsCollector = fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled, entityType);
21
+ this.fromImpressionsCollector = fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled);
22
22
  }
23
23
  /**
24
24
  * start method.
@@ -13,7 +13,7 @@ function isExpired(credential) {
13
13
  export function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
14
14
  var urls = settings.urls, log = settings.log;
15
15
  function fetchAuth() {
16
- var url = "".concat(urls.auth, "/api/v3/auth?capabilities=config");
16
+ var url = "".concat(urls.auth, "/api/v3/auth?capabilities=config,aiconfig");
17
17
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN), false, true);
18
18
  }
19
19
  var authenticate = authenticateFactory(fetchAuth);
@@ -24,6 +24,7 @@ export function impressionsToJSON(impressions, metadata) {
24
24
  m: impression.time,
25
25
  pt: impression.pt,
26
26
  properties: impression.properties
27
+ // @TODO set entityType
27
28
  }
28
29
  };
29
30
  return JSON.stringify(impressionWithMetadata);
@@ -4,7 +4,7 @@ import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
4
4
  /**
5
5
  * Converts `impressions` data from cache into request payload.
6
6
  */
7
- export function fromImpressionsCollector(sendLabels, entityType, data) {
7
+ export function fromImpressionsCollector(sendLabels, data) {
8
8
  var groupedByFeature = groupBy(data, 'feature');
9
9
  var dto = [];
10
10
  forOwn(groupedByFeature, function (value, name) {
@@ -20,7 +20,8 @@ export function fromImpressionsCollector(sendLabels, entityType, data) {
20
20
  b: entry.bucketingKey,
21
21
  pt: entry.pt,
22
22
  properties: entry.properties,
23
- et: entityType, // Definition type
23
+ // @ts-expect-error - entityType is not yet public. @TODO: add to SplitIO.ImpressionDTO type
24
+ et: entry.entityType, // Definition type
24
25
  };
25
26
  })
26
27
  });
@@ -31,9 +32,9 @@ export function fromImpressionsCollector(sendLabels, entityType, data) {
31
32
  * Submitter that periodically posts impressions data
32
33
  */
33
34
  export function impressionsSubmitterFactory(params) {
34
- var _a = params.settings, log = _a.log, impressionsRefreshRate = _a.scheduler.impressionsRefreshRate, labelsEnabled = _a.core.labelsEnabled, postTestImpressionsBulk = params.serviceApi.postTestImpressionsBulk, impressions = params.storage.impressions, entityType = params.entityType;
35
+ var _a = params.settings, log = _a.log, impressionsRefreshRate = _a.scheduler.impressionsRefreshRate, labelsEnabled = _a.core.labelsEnabled, postTestImpressionsBulk = params.serviceApi.postTestImpressionsBulk, impressions = params.storage.impressions;
35
36
  // retry impressions only once.
36
- var syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled, entityType), 1);
37
+ var syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled), 1);
37
38
  // register impressions submitter to be executed when impressions cache is full
38
39
  impressions.setOnFullQueueCb(function () {
39
40
  if (syncTask.isRunning()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "3.0.1-rc.3",
3
+ "version": "3.1.1-rc.0",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
package/src/dtos/types.ts CHANGED
@@ -215,6 +215,9 @@ export interface IRBSegment extends TargetingEntity {
215
215
  } | null
216
216
  }
217
217
 
218
+ export type ConfigType = 'standard' | 'ai';
219
+ export type ConfigSubtype = 'llm_call';
220
+
218
221
  export interface IDefinition extends TargetingEntity {
219
222
  trafficTypeName: string;
220
223
  sets?: string[] | null;
@@ -231,6 +234,10 @@ export interface IDefinition extends TargetingEntity {
231
234
  configurations?: {
232
235
  [treatmentName: string]: string | SplitIO.JsonObject
233
236
  } | null;
237
+ /** Definition classification. Absent means a feature flag. */
238
+ type?: ConfigType;
239
+ /** Only meaningful when `type === 'AI'`. */
240
+ subtype?: ConfigSubtype;
234
241
  }
235
242
 
236
243
  /** Interface of the parsed JSON response of `/splitChanges` */
@@ -47,29 +47,14 @@ export function evaluateFeature(
47
47
  return EVALUATION_EXCEPTION;
48
48
  }
49
49
 
50
- if (thenable(definition)) {
51
- return definition.then((definition) => getEvaluation(
52
- log,
53
- key,
54
- definition,
55
- attributes,
56
- storage,
57
- options,
58
- )).catch(
59
- // Exception on async storage. For example, when the storage is redis or
60
- // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
61
- () => EVALUATION_EXCEPTION
62
- );
63
- }
64
-
65
- return getEvaluation(
66
- log,
67
- key,
68
- definition,
69
- attributes,
70
- storage,
71
- options,
72
- );
50
+ return thenable(definition) ?
51
+ definition.then((definition) => getEvaluation(log, key, definition, attributes, storage, options))
52
+ .catch(
53
+ // Exception on async storage. For example, when the storage is redis or
54
+ // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
55
+ () => EVALUATION_EXCEPTION
56
+ ) :
57
+ getEvaluation(log, key, definition, attributes, storage, options);
73
58
  }
74
59
 
75
60
  export function evaluateFeatures(
@@ -91,11 +76,11 @@ export function evaluateFeatures(
91
76
 
92
77
  return thenable(definitions) ?
93
78
  definitions.then(definitions => getEvaluations(log, key, definitionNames, definitions, attributes, storage, options))
94
- .catch(() => {
79
+ .catch(
95
80
  // Exception on async storage. For example, when the storage is redis or
96
81
  // pluggable and there is a connection issue and we can't retrieve the split to be evaluated
97
- return treatmentsException(definitionNames);
98
- }) :
82
+ () => treatmentsException(definitionNames)
83
+ ) :
99
84
  getEvaluations(log, key, definitionNames, definitions, attributes, storage, options);
100
85
  }
101
86
 
@@ -137,12 +122,21 @@ export function evaluateFeaturesByFlagSets(
137
122
  // evaluate related features
138
123
  return thenable(storedFlagNames) ?
139
124
  storedFlagNames.then((storedFlagNames) => evaluate(storedFlagNames))
140
- .catch(() => {
141
- return {};
142
- }) :
125
+ .catch(() => ({})) :
143
126
  evaluate(storedFlagNames);
144
127
  }
145
128
 
129
+ function setEvaluationDataFromDefinition(evaluation: IEvaluationResult, definition: IDefinition, options?: SplitIO.EvaluationOptions): IEvaluationResult {
130
+ evaluation.changeNumber = definition.changeNumber;
131
+ evaluation.config = definition.configurations && definition.configurations[evaluation.treatment] || null;
132
+ evaluation.type = definition.type;
133
+ evaluation.subtype = definition.subtype;
134
+ // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
135
+ evaluation.impressionsDisabled = options?.impressionsDisabled || definition.impressionsDisabled;
136
+
137
+ return evaluation;
138
+ }
139
+
146
140
  function getEvaluation(
147
141
  log: ILogger,
148
142
  key: SplitIO.SplitKey,
@@ -156,24 +150,9 @@ function getEvaluation(
156
150
  const split = engineParser(log, definition, storage);
157
151
  const evaluation = split.getTreatment(key, attributes, evaluateFeature);
158
152
 
159
- // If the storage is async and the evaluated definition uses segments or dependencies, evaluation is thenable
160
- if (thenable(evaluation)) {
161
- return evaluation.then(result => {
162
- result.changeNumber = definition.changeNumber;
163
- result.config = definition.configurations && definition.configurations[result.treatment] || null;
164
- // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
165
- result.impressionsDisabled = options?.impressionsDisabled || definition.impressionsDisabled;
166
-
167
- return result;
168
- });
169
- } else {
170
- evaluation.changeNumber = definition.changeNumber;
171
- evaluation.config = definition.configurations && definition.configurations[evaluation.treatment] || null;
172
- // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
173
- evaluation.impressionsDisabled = options?.impressionsDisabled || definition.impressionsDisabled;
174
- }
175
-
176
- return evaluation;
153
+ return thenable(evaluation) ?
154
+ evaluation.then(result => setEvaluationDataFromDefinition(result, definition, options)) :
155
+ setEvaluationDataFromDefinition(evaluation, definition, options);
177
156
  }
178
157
 
179
158
  return EVALUATION_DEFINITION_NOT_FOUND;
@@ -191,21 +170,12 @@ function getEvaluations(
191
170
  const result: Record<string, IEvaluationResult> = {};
192
171
  const thenables: Promise<void>[] = [];
193
172
  definitionNames.forEach(definitionName => {
194
- const evaluation = getEvaluation(
195
- log,
196
- key,
197
- definitions[definitionName],
198
- attributes,
199
- storage,
200
- options
201
- );
202
- if (thenable(evaluation)) {
173
+ const evaluation = getEvaluation(log, key, definitions[definitionName], attributes, storage, options);
174
+ thenable(evaluation) ?
203
175
  thenables.push(evaluation.then(res => {
204
176
  result[definitionName] = res;
205
- }));
206
- } else {
177
+ })) :
207
178
  result[definitionName] = evaluation;
208
- }
209
179
  });
210
180
 
211
181
  return thenables.length > 0 ? Promise.all(thenables).then(() => result) : result;
@@ -232,12 +202,10 @@ function getDefaultTreatment(
232
202
  definition: IDefinition | null,
233
203
  ): MaybeThenable<IEvaluationResult> {
234
204
  if (definition) {
235
- return {
205
+ return setEvaluationDataFromDefinition({
236
206
  treatment: definition.defaultTreatment,
237
- label: NO_CONDITION_MATCH, // "default rule"
238
- config: definition.configurations && definition.configurations[definition.defaultTreatment] || null,
239
- changeNumber: definition.changeNumber
240
- };
207
+ label: NO_CONDITION_MATCH // "default rule"
208
+ }, definition);
241
209
  }
242
210
 
243
211
  return EVALUATION_DEFINITION_NOT_FOUND;
@@ -1,4 +1,4 @@
1
- import { IBetweenMatcherData, IBetweenStringMatcherData, IDependencyMatcherData, MaybeThenable } from '../dtos/types';
1
+ import { IBetweenMatcherData, IBetweenStringMatcherData, IDefinition, IDependencyMatcherData, MaybeThenable } from '../dtos/types';
2
2
  import { IStorageAsync, IStorageSync } from '../storages/types';
3
3
  import SplitIO from '../../types/splitio';
4
4
  import { ILogger } from '../logger/types';
@@ -23,6 +23,8 @@ export interface IEvaluation {
23
23
  label: string,
24
24
  changeNumber?: number,
25
25
  config?: string | null | SplitIO.JsonObject
26
+ type?: IDefinition['type']
27
+ subtype?: IDefinition['subtype']
26
28
  }
27
29
 
28
30
  export type IEvaluationResult = IEvaluation & { treatment: string; impressionsDisabled?: boolean }
@@ -28,14 +28,14 @@ export class BrowserSignalListener implements ISignalListener {
28
28
  private serviceApi: IServiceApi;
29
29
  private fromImpressionsCollector: (data: SplitIO.ImpressionDTO[]) => ImpressionsPayload;
30
30
 
31
- constructor({ syncManager, settings, storage, serviceApi, entityType }: ISdkFactoryContextSync) {
31
+ constructor({ syncManager, settings, storage, serviceApi }: ISdkFactoryContextSync) {
32
32
  this.syncManager = syncManager;
33
33
  this.settings = settings;
34
34
  this.storage = storage;
35
35
  this.serviceApi = serviceApi;
36
36
  this.flushData = this.flushData.bind(this);
37
37
  this.flushDataIfHidden = this.flushDataIfHidden.bind(this);
38
- this.fromImpressionsCollector = fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled, entityType);
38
+ this.fromImpressionsCollector = fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled);
39
39
  }
40
40
 
41
41
  /**
@@ -43,9 +43,6 @@ export interface IPlatform {
43
43
  SignalListener?: new (params: ISdkFactoryContext) => ISignalListener, // Used by BrowserSignalListener
44
44
  }
45
45
 
46
- // Definition type
47
- export type EntityType = 'config' | 'flag';
48
-
49
46
  export interface ISdkFactoryContext {
50
47
  platform: IPlatform,
51
48
  sdkReadinessManager: ISdkReadinessManager,
@@ -59,7 +56,6 @@ export interface ISdkFactoryContext {
59
56
  syncManager?: ISyncManager,
60
57
  clients: Record<string, SplitIO.IBasicClient>,
61
58
  fallbackCalculator: IFallbackCalculator,
62
- entityType?: EntityType
63
59
  }
64
60
 
65
61
  export interface ISdkFactoryContextSync extends ISdkFactoryContext {
@@ -28,7 +28,7 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
28
28
  const { urls, log } = settings;
29
29
 
30
30
  function fetchAuth() {
31
- let url = `${urls.auth}/api/v3/auth?capabilities=config`;
31
+ let url = `${urls.auth}/api/v3/auth?capabilities=config,aiconfig`;
32
32
  return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN), false, true);
33
33
  }
34
34
 
@@ -31,6 +31,7 @@ export function impressionsToJSON(impressions: SplitIO.ImpressionDTO[], metadata
31
31
  m: impression.time,
32
32
  pt: impression.pt,
33
33
  properties: impression.properties
34
+ // @TODO set entityType
34
35
  }
35
36
  };
36
37
 
@@ -3,12 +3,12 @@ import SplitIO from '../../../types/splitio';
3
3
  import { submitterFactory } from './submitter';
4
4
  import { ImpressionsPayload } from './types';
5
5
  import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
6
- import { EntityType, ISdkFactoryContextSync } from '../../sdkFactory/types';
6
+ import { ISdkFactoryContextSync } from '../../sdkFactory/types';
7
7
 
8
8
  /**
9
9
  * Converts `impressions` data from cache into request payload.
10
10
  */
11
- export function fromImpressionsCollector(sendLabels: boolean, entityType: EntityType | undefined, data: SplitIO.ImpressionDTO[]): ImpressionsPayload {
11
+ export function fromImpressionsCollector(sendLabels: boolean, data: SplitIO.ImpressionDTO[]): ImpressionsPayload {
12
12
  let groupedByFeature = groupBy(data, 'feature');
13
13
  let dto: ImpressionsPayload = [];
14
14
 
@@ -25,7 +25,8 @@ export function fromImpressionsCollector(sendLabels: boolean, entityType: Entity
25
25
  b: entry.bucketingKey, // Bucketing Key
26
26
  pt: entry.pt, // Previous time
27
27
  properties: entry.properties, // Properties
28
- et: entityType, // Definition type
28
+ // @ts-expect-error - entityType is not yet public. @TODO: add to SplitIO.ImpressionDTO type
29
+ et: entry.entityType, // Definition type
29
30
  };
30
31
  })
31
32
  });
@@ -42,12 +43,11 @@ export function impressionsSubmitterFactory(params: ISdkFactoryContextSync) {
42
43
  const {
43
44
  settings: { log, scheduler: { impressionsRefreshRate }, core: { labelsEnabled } },
44
45
  serviceApi: { postTestImpressionsBulk },
45
- storage: { impressions },
46
- entityType
46
+ storage: { impressions }
47
47
  } = params;
48
48
 
49
49
  // retry impressions only once.
50
- const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled, entityType), 1);
50
+ const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled), 1);
51
51
 
52
52
  // register impressions submitter to be executed when impressions cache is full
53
53
  impressions.setOnFullQueueCb(() => {
@@ -2,7 +2,8 @@
2
2
  import { IMetadata } from '../../dtos/types';
3
3
  import SplitIO from '../../../types/splitio';
4
4
  import { ISyncTask } from '../types';
5
- import { EntityType } from '../../sdkFactory/types';
5
+
6
+ type EntityType = 'config' | 'flag' | 'ai-config';
6
7
 
7
8
  type ImpressionPayload = {
8
9
  /** Matching Key */
@@ -2290,133 +2290,9 @@ declare namespace SplitIO {
2290
2290
  split(featureFlagName: string): SplitViewAsync;
2291
2291
  }
2292
2292
 
2293
- /**
2294
- * Fallback configuration objects returned by the `client.getConfig` method when the SDK is not ready or the provided config name is not found.
2295
- */
2296
- type FallbackConfigs = {
2297
- /**
2298
- * Fallback config for all config names.
2299
- */
2300
- global?: Config;
2301
- /**
2302
- * Fallback configs for specific config names. It takes precedence over the global fallback config.
2303
- */
2304
- byName?: {
2305
- [configName: string]: Config;
2306
- };
2307
- }
2308
-
2309
- /**
2310
- * Configs SDK settings.
2311
- */
2312
- interface ConfigsClientSettings {
2313
- /**
2314
- * SDK key used to authenticate with Harness services.
2315
- *
2316
- * @see {@link https://developer.harness.io/docs/feature-management-experimentation/management-and-administration/account-settings/api-keys/}
2317
- */
2318
- sdkKey: string;
2319
- /**
2320
- * Log level for SDK logging.
2321
- * - `'none'`: No logging
2322
- * - `'error'`: Log errors only
2323
- * - `'warn'`: Log warnings and errors
2324
- * - `'info'`: Log info, warnings, and errors
2325
- * - `'debug'`: Log debug info and above
2326
- * @defaultValue `'none'`
2327
- */
2328
- logLevel?: 'none' | 'error' | 'warn' | 'info' | 'debug';
2329
- /**
2330
- * Synchronization configuration.
2331
- */
2332
- sync?: {
2333
- /**
2334
- * Polling rate for configs and segments refresh, in seconds. Minimum value: 5.
2335
- *
2336
- * @defaultValue `60`
2337
- */
2338
- pollingRate?: number;
2339
- /**
2340
- * Push rate for events and impressions, in seconds. Minimum value: 60.
2341
- *
2342
- * @defaultValue `60`
2343
- */
2344
- pushRate?: number;
2345
- /**
2346
- * Maximum queue size for events and impressions. When the queue reaches this size, a flush is triggered. Minimum value: 1000.
2347
- *
2348
- * @defaultValue `10000`
2349
- */
2350
- queueSize?: number;
2351
- /**
2352
- * Time in seconds before emitting the `SDK_READY_TIMED_OUT` event.
2353
- * A value of `-1` disables the timeout and thus the event is never emitted.
2354
- *
2355
- * @defaultValue `10`
2356
- */
2357
- readyTimeout?: number;
2358
- /**
2359
- * Base URLs used by the SDK for different services.
2360
- */
2361
- serviceEndpoints?: {
2362
- /**
2363
- * String property to override the base URL where the SDK will get JWT authentication credentials.
2364
- *
2365
- * @defaultValue `'https://auth.split.io'`
2366
- */
2367
- auth?: string;
2368
- /**
2369
- * String property to override the base URL where the SDK will get rollout plan related data, like configs and segments definitions.
2370
- *
2371
- * @defaultValue `'https://configs.split.io'`
2372
- */
2373
- configs?: string;
2374
- /**
2375
- * String property to override the base URL where the SDK will post event-related information like impressions.
2376
- *
2377
- * @defaultValue `'https://events.split.io'`
2378
- */
2379
- events?: string;
2380
- };
2381
- };
2382
- /**
2383
- * Fallback configuration objects returned by the `client.getConfig` method when the SDK is not ready or the provided config name is not found.
2384
- */
2385
- fallbackConfigs?: FallbackConfigs;
2386
- /**
2387
- * Custom options object for HTTP(S) requests.
2388
- * If provided, this object is merged with the options object passed by the SDK for EventSource and Fetch calls.
2389
- */
2390
- requestOptions?: {
2391
- /**
2392
- * Custom Node.js HTTP(S) Agent used by the SDK for HTTP(S) requests.
2393
- *
2394
- * You can use it, for example, for certificate pinning or setting a network proxy:
2395
- *
2396
- * ```
2397
- * const { ConfigsClient } = require('@splitsoftware/configs');
2398
- * const { HttpsProxyAgent } = require('https-proxy-agent');
2399
- *
2400
- * const proxyAgent = new HttpsProxyAgent(process.env.HTTPS_PROXY || 'http://10.10.1.10:1080');
2401
- *
2402
- * const client = ConfigsClient({
2403
- * ...
2404
- * requestOptions: {
2405
- * agent: proxyAgent
2406
- * }
2407
- * })
2408
- * ```
2409
- *
2410
- * @see {@link https://nodejs.org/api/https.html#class-httpsagent}
2411
- *
2412
- * @defaultValue `undefined`
2413
- */
2414
- agent?: RequestOptions['agent'];
2415
- };
2416
- }
2417
2293
 
2418
2294
  /**
2419
- * Target for a config evaluation.
2295
+ * Target for an evaluation.
2420
2296
  */
2421
2297
  interface Target {
2422
2298
  /**
@@ -2434,50 +2310,4 @@ declare namespace SplitIO {
2434
2310
  type JsonValue = string | number | boolean | null | JsonObject | JsonArray;
2435
2311
  type JsonArray = JsonValue[];
2436
2312
  type JsonObject = { [key: string]: JsonValue; };
2437
-
2438
- /**
2439
- * Config object returned by getConfig.
2440
- */
2441
- type Config = {
2442
- /**
2443
- * The name of the variant.
2444
- */
2445
- variant: string;
2446
- /**
2447
- * The config value, a raw JSON object.
2448
- */
2449
- value: JsonObject;
2450
- }
2451
-
2452
- /**
2453
- * Configs SDK client interface.
2454
- */
2455
- interface ConfigsClient extends Omit<IStatusInterface, 'ready' | 'whenReadyFromCache'> {
2456
- /**
2457
- * Destroys the client.
2458
- *
2459
- * @returns A promise that resolves once all clients are destroyed.
2460
- */
2461
- destroy(): Promise<void>;
2462
- /**
2463
- * Gets the config object for a given config name and optional target. If no target is provided, the default variant of the config is returned.
2464
- *
2465
- * @param configName - The name of the config we want to get.
2466
- * @param target - The target of the config evaluation.
2467
- * @param options - An object of type EvaluationOptions for advanced evaluation options.
2468
- * @returns The config object.
2469
- */
2470
- getConfig(configName: string, target?: Target, options?: EvaluationOptions): Config;
2471
- /**
2472
- * Tracks an event to be fed to the results product on Harness FME user interface.
2473
- *
2474
- * @param trafficKey - The key that identifies the entity related to this event.
2475
- * @param trafficType - The traffic type of the entity related to this event. See {@link https://developer.harness.io/docs/feature-management-experimentation/management-and-administration/fme-settings/traffic-types/}
2476
- * @param eventType - The event type corresponding to this event.
2477
- * @param value - The value of this event.
2478
- * @param properties - The properties of this event. Values can be string, number, boolean or null.
2479
- * @returns Whether the event was added to the queue successfully or not.
2480
- */
2481
- track(trafficKey: SplitKey, trafficType: string, eventType: string, value?: number, properties?: Properties): boolean;
2482
- }
2483
2313
  }
@@ -1,70 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sanitizeFallbacks = exports.isValidConfig = exports.isValidConfigName = void 0;
4
- var lang_1 = require("../../../utils/lang");
5
- var CONFIG_NAME_DISCARD_REASON = 'Invalid config name (max 100 chars, no spaces)';
6
- var VARIANT_DISCARD_REASON = 'Invalid variant (max 100 chars and must match pattern)';
7
- var VALUE_DISCARD_REASON = 'Invalid value (must be an object)';
8
- var VARIANT_PATTERN = /^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$/;
9
- function isValidConfigName(name) {
10
- return name.length <= 100 && !name.includes(' ');
11
- }
12
- exports.isValidConfigName = isValidConfigName;
13
- function isValidConfig(config) {
14
- if (!(0, lang_1.isObject)(config))
15
- return false;
16
- if (!(0, lang_1.isString)(config.variant) || config.variant.length > 100 || !VARIANT_PATTERN.test(config.variant))
17
- return false;
18
- if (!(0, lang_1.isObject)(config.value))
19
- return false;
20
- return true;
21
- }
22
- exports.isValidConfig = isValidConfig;
23
- function sanitizeGlobal(logger, config) {
24
- if (config === undefined)
25
- return undefined;
26
- if (!isValidConfig(config)) {
27
- if (!(0, lang_1.isObject)(config) || !(0, lang_1.isString)(config.variant) || config.variant.length > 100 || !VARIANT_PATTERN.test(config.variant)) {
28
- logger.error("Fallback configs - Discarded fallback: ".concat(VARIANT_DISCARD_REASON));
29
- }
30
- else {
31
- logger.error("Fallback configs - Discarded fallback: ".concat(VALUE_DISCARD_REASON));
32
- }
33
- return undefined;
34
- }
35
- return config;
36
- }
37
- function sanitizeByName(logger, byNameFallbacks) {
38
- var sanitizedByName = {};
39
- if (!(0, lang_1.isObject)(byNameFallbacks))
40
- return sanitizedByName;
41
- Object.keys(byNameFallbacks).forEach(function (configName) {
42
- var config = byNameFallbacks[configName];
43
- if (!isValidConfigName(configName)) {
44
- logger.error("Fallback configs - Discarded config '".concat(configName, "': ").concat(CONFIG_NAME_DISCARD_REASON));
45
- return;
46
- }
47
- if (!isValidConfig(config)) {
48
- if (!(0, lang_1.isObject)(config) || !(0, lang_1.isString)(config.variant) || config.variant.length > 100 || !VARIANT_PATTERN.test(config.variant)) {
49
- logger.error("Fallback configs - Discarded config '".concat(configName, "': ").concat(VARIANT_DISCARD_REASON));
50
- }
51
- else {
52
- logger.error("Fallback configs - Discarded config '".concat(configName, "': ").concat(VALUE_DISCARD_REASON));
53
- }
54
- return;
55
- }
56
- sanitizedByName[configName] = config;
57
- });
58
- return sanitizedByName;
59
- }
60
- function sanitizeFallbacks(logger, fallbacks) {
61
- if (!(0, lang_1.isObject)(fallbacks)) {
62
- logger.error('Fallback configs - Discarded configuration: it must be an object with optional `global` and `byName` properties');
63
- return;
64
- }
65
- return {
66
- global: sanitizeGlobal(logger, fallbacks.global),
67
- byName: sanitizeByName(logger, fallbacks.byName)
68
- };
69
- }
70
- exports.sanitizeFallbacks = sanitizeFallbacks;
@@ -1,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FallbackConfigsCalculator = exports.FALLBACK_PREFIX = void 0;
4
- var constants_1 = require("../../utils/constants");
5
- exports.FALLBACK_PREFIX = 'fallback - ';
6
- function FallbackConfigsCalculator(fallbacks) {
7
- if (fallbacks === void 0) { fallbacks = {}; }
8
- return function (configName, label) {
9
- var _a;
10
- if (label === void 0) { label = ''; }
11
- var fallback = ((_a = fallbacks.byName) === null || _a === void 0 ? void 0 : _a[configName]) || fallbacks.global;
12
- return fallback ?
13
- {
14
- treatment: fallback.variant,
15
- config: fallback.value,
16
- label: "".concat(exports.FALLBACK_PREFIX).concat(label),
17
- } :
18
- {
19
- treatment: constants_1.CONTROL,
20
- config: null,
21
- label: label,
22
- };
23
- };
24
- }
25
- exports.FallbackConfigsCalculator = FallbackConfigsCalculator;
@@ -1,64 +0,0 @@
1
- import { isObject, isString } from '../../../utils/lang';
2
- var CONFIG_NAME_DISCARD_REASON = 'Invalid config name (max 100 chars, no spaces)';
3
- var VARIANT_DISCARD_REASON = 'Invalid variant (max 100 chars and must match pattern)';
4
- var VALUE_DISCARD_REASON = 'Invalid value (must be an object)';
5
- var VARIANT_PATTERN = /^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$/;
6
- export function isValidConfigName(name) {
7
- return name.length <= 100 && !name.includes(' ');
8
- }
9
- export function isValidConfig(config) {
10
- if (!isObject(config))
11
- return false;
12
- if (!isString(config.variant) || config.variant.length > 100 || !VARIANT_PATTERN.test(config.variant))
13
- return false;
14
- if (!isObject(config.value))
15
- return false;
16
- return true;
17
- }
18
- function sanitizeGlobal(logger, config) {
19
- if (config === undefined)
20
- return undefined;
21
- if (!isValidConfig(config)) {
22
- if (!isObject(config) || !isString(config.variant) || config.variant.length > 100 || !VARIANT_PATTERN.test(config.variant)) {
23
- logger.error("Fallback configs - Discarded fallback: ".concat(VARIANT_DISCARD_REASON));
24
- }
25
- else {
26
- logger.error("Fallback configs - Discarded fallback: ".concat(VALUE_DISCARD_REASON));
27
- }
28
- return undefined;
29
- }
30
- return config;
31
- }
32
- function sanitizeByName(logger, byNameFallbacks) {
33
- var sanitizedByName = {};
34
- if (!isObject(byNameFallbacks))
35
- return sanitizedByName;
36
- Object.keys(byNameFallbacks).forEach(function (configName) {
37
- var config = byNameFallbacks[configName];
38
- if (!isValidConfigName(configName)) {
39
- logger.error("Fallback configs - Discarded config '".concat(configName, "': ").concat(CONFIG_NAME_DISCARD_REASON));
40
- return;
41
- }
42
- if (!isValidConfig(config)) {
43
- if (!isObject(config) || !isString(config.variant) || config.variant.length > 100 || !VARIANT_PATTERN.test(config.variant)) {
44
- logger.error("Fallback configs - Discarded config '".concat(configName, "': ").concat(VARIANT_DISCARD_REASON));
45
- }
46
- else {
47
- logger.error("Fallback configs - Discarded config '".concat(configName, "': ").concat(VALUE_DISCARD_REASON));
48
- }
49
- return;
50
- }
51
- sanitizedByName[configName] = config;
52
- });
53
- return sanitizedByName;
54
- }
55
- export function sanitizeFallbacks(logger, fallbacks) {
56
- if (!isObject(fallbacks)) {
57
- logger.error('Fallback configs - Discarded configuration: it must be an object with optional `global` and `byName` properties');
58
- return;
59
- }
60
- return {
61
- global: sanitizeGlobal(logger, fallbacks.global),
62
- byName: sanitizeByName(logger, fallbacks.byName)
63
- };
64
- }
@@ -1,21 +0,0 @@
1
- import { CONTROL } from '../../utils/constants';
2
- export var FALLBACK_PREFIX = 'fallback - ';
3
- export function FallbackConfigsCalculator(fallbacks) {
4
- if (fallbacks === void 0) { fallbacks = {}; }
5
- return function (configName, label) {
6
- var _a;
7
- if (label === void 0) { label = ''; }
8
- var fallback = ((_a = fallbacks.byName) === null || _a === void 0 ? void 0 : _a[configName]) || fallbacks.global;
9
- return fallback ?
10
- {
11
- treatment: fallback.variant,
12
- config: fallback.value,
13
- label: "".concat(FALLBACK_PREFIX).concat(label),
14
- } :
15
- {
16
- treatment: CONTROL,
17
- config: null,
18
- label: label,
19
- };
20
- };
21
- }
@@ -1,76 +0,0 @@
1
- import SplitIO from '../../../../types/splitio';
2
- import { ILogger } from '../../../logger/types';
3
- import { isObject, isString } from '../../../utils/lang';
4
-
5
- const CONFIG_NAME_DISCARD_REASON = 'Invalid config name (max 100 chars, no spaces)';
6
- const VARIANT_DISCARD_REASON = 'Invalid variant (max 100 chars and must match pattern)';
7
- const VALUE_DISCARD_REASON = 'Invalid value (must be an object)';
8
-
9
- const VARIANT_PATTERN = /^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$/;
10
-
11
- export function isValidConfigName(name: string): boolean {
12
- return name.length <= 100 && !name.includes(' ');
13
- }
14
-
15
- export function isValidConfig(config?: SplitIO.Config): boolean {
16
- if (!isObject(config)) return false;
17
- if (!isString(config!.variant) || config!.variant.length > 100 || !VARIANT_PATTERN.test(config!.variant)) return false;
18
- if (!isObject(config!.value)) return false;
19
- return true;
20
- }
21
-
22
- function sanitizeGlobal(logger: ILogger, config?: SplitIO.Config): SplitIO.Config | undefined {
23
- if (config === undefined) return undefined;
24
- if (!isValidConfig(config)) {
25
- if (!isObject(config) || !isString(config!.variant) || config!.variant.length > 100 || !VARIANT_PATTERN.test(config!.variant)) {
26
- logger.error(`Fallback configs - Discarded fallback: ${VARIANT_DISCARD_REASON}`);
27
- } else {
28
- logger.error(`Fallback configs - Discarded fallback: ${VALUE_DISCARD_REASON}`);
29
- }
30
- return undefined;
31
- }
32
- return config;
33
- }
34
-
35
- function sanitizeByName(
36
- logger: ILogger,
37
- byNameFallbacks?: Record<string, SplitIO.Config>
38
- ): Record<string, SplitIO.Config> {
39
- const sanitizedByName: Record<string, SplitIO.Config> = {};
40
-
41
- if (!isObject(byNameFallbacks)) return sanitizedByName;
42
-
43
- Object.keys(byNameFallbacks!).forEach((configName) => {
44
- const config = byNameFallbacks![configName];
45
-
46
- if (!isValidConfigName(configName)) {
47
- logger.error(`Fallback configs - Discarded config '${configName}': ${CONFIG_NAME_DISCARD_REASON}`);
48
- return;
49
- }
50
-
51
- if (!isValidConfig(config)) {
52
- if (!isObject(config) || !isString(config.variant) || config.variant.length > 100 || !VARIANT_PATTERN.test(config.variant)) {
53
- logger.error(`Fallback configs - Discarded config '${configName}': ${VARIANT_DISCARD_REASON}`);
54
- } else {
55
- logger.error(`Fallback configs - Discarded config '${configName}': ${VALUE_DISCARD_REASON}`);
56
- }
57
- return;
58
- }
59
-
60
- sanitizedByName[configName] = config;
61
- });
62
-
63
- return sanitizedByName;
64
- }
65
-
66
- export function sanitizeFallbacks(logger: ILogger, fallbacks: SplitIO.FallbackConfigs): SplitIO.FallbackConfigs | undefined {
67
- if (!isObject(fallbacks)) {
68
- logger.error('Fallback configs - Discarded configuration: it must be an object with optional `global` and `byName` properties');
69
- return;
70
- }
71
-
72
- return {
73
- global: sanitizeGlobal(logger, fallbacks.global),
74
- byName: sanitizeByName(logger, fallbacks.byName)
75
- };
76
- }
@@ -1,24 +0,0 @@
1
- import { IFallbackCalculator } from '../fallbackTreatmentsCalculator/index';
2
- import { CONTROL } from '../../utils/constants';
3
- import SplitIO from '../../../types/splitio';
4
-
5
- export const FALLBACK_PREFIX = 'fallback - ';
6
-
7
- export function FallbackConfigsCalculator(fallbacks: SplitIO.FallbackConfigs = {}): IFallbackCalculator {
8
-
9
- return (configName: string, label = '') => {
10
- const fallback = fallbacks.byName?.[configName] || fallbacks.global;
11
-
12
- return fallback ?
13
- {
14
- treatment: fallback.variant,
15
- config: fallback.value,
16
- label: `${FALLBACK_PREFIX}${label}`,
17
- } :
18
- {
19
- treatment: CONTROL,
20
- config: null,
21
- label,
22
- };
23
- };
24
- }