@splitsoftware/splitio-commons 2.2.1-rc.5 → 2.3.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,8 +1,12 @@
1
- 2.3.0 (May 12, 2025)
1
+ 2.4.0 (May 20, 2025)
2
2
  - Added support for targeting rules based on rule-based segments.
3
+ - Added support for feature flag prerequisites.
4
+
5
+ 2.3.0 (May 16, 2025)
3
6
  - Updated the Redis storage to:
4
7
  - Avoid lazy require of the `ioredis` dependency when the SDK is initialized, and
5
8
  - Flag the SDK as ready from cache immediately to allow queueing feature flag evaluations before SDK_READY event is emitted (Reverted in v1.7.0).
9
+ - Bugfix - Enhanced HTTP client module to implement timeouts for failing requests that might otherwise remain pending indefinitely on some Fetch API implementations.
6
10
 
7
11
  2.2.0 (March 28, 2025)
8
12
  - Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs.
@@ -15,21 +19,21 @@
15
19
  - Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on `SplitView` type objects. Read more in our docs.
16
20
 
17
21
  2.0.3 (January 9, 2025)
18
- - Bugfixing - Properly handle rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
22
+ - Bugfix - Properly handle rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
19
23
 
20
24
  2.0.2 (December 3, 2024)
21
25
  - Updated the factory `init` and `destroy` methods to support re-initialization after destruction. This update ensures compatibility of the React SDK with React Strict Mode, where the factory's `init` and `destroy` effects are executed an extra time to validate proper resource cleanup.
22
- - Bugfixing - Sanitize the `SplitSDKMachineName` header value to avoid exceptions on HTTP/S requests when it contains non ISO-8859-1 characters (Related to issue https://github.com/splitio/javascript-client/issues/847).
26
+ - Bugfix - Sanitize the `SplitSDKMachineName` header value to avoid exceptions on HTTP/S requests when it contains non ISO-8859-1 characters (Related to issue https://github.com/splitio/javascript-client/issues/847).
23
27
 
24
28
  2.0.1 (November 25, 2024)
25
- - Bugfixing - Fixed an issue with the SDK_UPDATE event on server-side, where it was not being emitted if there was an empty segment and the SDK received a feature flag update notification.
29
+ - Bugfix - Fixed an issue with the SDK_UPDATE event on server-side, where it was not being emitted if there was an empty segment and the SDK received a feature flag update notification.
26
30
 
27
31
  2.0.0 (November 1, 2024)
28
32
  - Added support for targeting rules based on large segments.
29
33
  - Added `factory.destroy()` method, which invokes the `destroy` method on all SDK clients created by the factory.
30
34
  - Added `SplitIO` namespace with the public TypeScript definitions to be reused by the SDKs.
31
35
  - Updated the handling of timers and async operations inside an `init` factory method to enable lazy initialization of the SDK in standalone mode. This update is intended for the React SDK.
32
- - Bugfixing - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
36
+ - Bugfix - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
33
37
  - BREAKING CHANGES:
34
38
  - Updated default flag spec version to 1.2, which requires Split Proxy v5.9.0 or higher.
35
39
  - Removed `/mySegments` endpoint from SplitAPI module, as it is replaced by `/memberships` endpoint.
@@ -48,8 +52,8 @@
48
52
  1.16.0 (June 13, 2024)
49
53
  - Added the `getOptions` method to the `IPlatform` interface to allow the SDK to pass request options to the `fetch` function and `EventSource` constructor when fetching data from the Split servers. The method is optional and, if provided, it is called twice: first for the `fetch` options and then for the `EventSource` options. Useful for advanced use cases like configuring a proxy or validating HTTPS certificates in Node.js.
50
54
  - Updated the Redis storage to lazily import the `ioredis` dependency when the storage is created. This prevents errors when the SDK is imported or bundled in a .mjs file, as `ioredis` is a CommonJS module.
51
- - Bugfixing - Restored some input validation error logs that were removed in version 1.12.0. The logs inform the user when the `getTreatment(s)` methods are called with an invalid value as feature flag name or flag set name.
52
- - Bugfixing - Fixed localhost mode to emit SDK_UPDATE when mocked feature flags are updated in the `features` object map of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119).
55
+ - Bugfix - Restored some input validation error logs that were removed in version 1.12.0. The logs inform the user when the `getTreatment(s)` methods are called with an invalid value as feature flag name or flag set name.
56
+ - Bugfix - Fixed localhost mode to emit SDK_UPDATE when mocked feature flags are updated in the `features` object map of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119).
53
57
 
54
58
  1.15.0 (May 13, 2024)
55
59
  - Added an optional settings validation parameter to let overwrite the default flag spec version, used by the JS Synchronizer.
@@ -67,13 +71,13 @@
67
71
 
68
72
  1.12.1 (December 12, 2023)
69
73
  - Updated PluggableStorage, for producer mode, and LocalStorage, for standalone mode, to clear the storage before initiating the synchronization process if it was previously synchronized with a different SDK key (i.e., a different environment) or different Split Filter criteria.
70
- - Bugfixing - Fixed an issue when tracking telemetry latencies for the new `getTreatmentsByFlagSet(s)` methods in Redis and Pluggable storages, which was causing the SDK to not track those stats.
74
+ - Bugfix - Fixed an issue when tracking telemetry latencies for the new `getTreatmentsByFlagSet(s)` methods in Redis and Pluggable storages, which was causing the SDK to not track those stats.
71
75
 
72
76
  1.12.0 (December 4, 2023)
73
77
  - Added support for Flag Sets in "consumer" and "partial consumer" modes for Pluggable and Redis storages.
74
78
  - Updated evaluation flow to log a warning when using flag sets that don't contain cached feature flags.
75
79
  - Updated Redis adapter to handle timeouts and queueing of some missing commands: 'hincrby', 'popNRaw', and 'pipeline.exec'.
76
- - Bugfixing - Fixed manager methods in consumer modes to return results in a promise when the SDK is not operational (not ready or destroyed).
80
+ - Bugfix - Fixed manager methods in consumer modes to return results in a promise when the SDK is not operational (not ready or destroyed).
77
81
 
78
82
  1.11.0 (November 3, 2023)
79
83
  - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation):
@@ -83,7 +87,7 @@
83
87
  - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload.
84
88
  - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init.
85
89
  - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views.
86
- - Bugfixing - Fixed SDK key validation in Node.js to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768).
90
+ - Bugfix - Fixed SDK key validation in Node.js to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768).
87
91
 
88
92
  1.10.0 (October 20, 2023)
89
93
  - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225).
@@ -102,7 +106,7 @@
102
106
  1.8.3 (June 29, 2023)
103
107
  - Updated some transitive dependencies for vulnerability fixes.
104
108
  - Updated SDK_READY_TIMED_OUT event to be emitted immediately when a connection error occurs using pluggable storage (i.e., when the wrapper `connect` promise is rejected) in consumer and partial consumer modes.
105
- - Bugfixing - The `destroy` method has been updated to immediately flag the SDK client as destroyed, to prevent unexpected behaviours when `getTreatment` and `track` methods are called synchronously after `destroy` method is called.
109
+ - Bugfix - The `destroy` method has been updated to immediately flag the SDK client as destroyed, to prevent unexpected behaviours when `getTreatment` and `track` methods are called synchronously after `destroy` method is called.
106
110
 
107
111
  1.8.2 (May 15, 2023)
108
112
  - Updated terminology on the SDKs codebase to be more aligned with current standard without causing a breaking change. The core change is the term split for feature flag on things like logs and IntelliSense comments.
@@ -118,10 +122,10 @@
118
122
  1.7.3 (December 16, 2022)
119
123
  - Updated unique keys cache for Redis and Pluggable storages to optimize the usage of the underlying storage.
120
124
  - Updated some transitive dependencies for vulnerability fixes.
121
- - Bugfixing - Updated events and impressions cache in localhost mode in order to avoid memory leaks (Related to issue https://github.com/splitio/javascript-commons/issues/181).
125
+ - Bugfix - Updated events and impressions cache in localhost mode in order to avoid memory leaks (Related to issue https://github.com/splitio/javascript-commons/issues/181).
122
126
 
123
127
  1.7.2 (October 14, 2022)
124
- - Bugfixing - Handle `Navigator.sendBeacon` API exceptions in the browser, and fallback to regular Fetch/XHR transport in case of error.
128
+ - Bugfix - Handle `Navigator.sendBeacon` API exceptions in the browser, and fallback to regular Fetch/XHR transport in case of error.
125
129
 
126
130
  1.7.1 (October 5, 2022)
127
131
  - Updated default value of `scheduler.featuresRefreshRate` config parameter to 60 seconds.
@@ -139,7 +143,7 @@
139
143
  - Added `autoRequire` configuration option to the Google Analytics to Split integration, which takes care of requiring the splitTracker plugin on trackers dynamically created by Google tag managers (See https://help.split.io/hc/en-us/articles/360040838752#set-up-with-gtm-and-gtag.js).
140
144
  - Updated browser listener to push remaining impressions and events on 'visibilitychange' and 'pagehide' DOM events, instead of 'unload', which is not reliable in modern mobile and desktop Web browsers.
141
145
  - Updated the synchronization flow to be more reliable in the event of an edge case generating delay in cache purge propagation, keeping the SDK cache properly synced.
142
- - Bugfixing - Removed js-yaml dependency to avoid resolution to an incompatible version on certain npm versions when installing third-party dependencies that also define js-yaml as transitive dependency (Related to issue https://github.com/splitio/javascript-client/issues/662).
146
+ - Bugfix - Removed js-yaml dependency to avoid resolution to an incompatible version on certain npm versions when installing third-party dependencies that also define js-yaml as transitive dependency (Related to issue https://github.com/splitio/javascript-client/issues/662).
143
147
 
144
148
  1.5.0 (June 29, 2022)
145
149
  - Added a new config option to control the tasks that listen or poll for updates on feature flags and segments, via the new config `sync.enabled`. Running online, Split SDK will always pull the most recent updates upon initialization, this only affects updates fetching on a running instance. Useful when a consistent session experience is a must or to save resources when updates are not being used.
@@ -147,16 +151,16 @@
147
151
  - Updated submitters logic, to avoid duplicating the post of impressions to Split cloud when the SDK is destroyed while its periodic post of impressions is running.
148
152
 
149
153
  1.4.1 (June 13, 2022)
150
- - Bugfixing - Updated submitters logic, to avoid dropping impressions and events that are being tracked while POST request is pending.
154
+ - Bugfix - Updated submitters logic, to avoid dropping impressions and events that are being tracked while POST request is pending.
151
155
 
152
156
  1.4.0 (May 24, 2022)
153
157
  - Added `scheduler.telemetryRefreshRate` property to SDK configuration, and deprecated `scheduler.metricsRefreshRate` property.
154
158
  - Updated SDK telemetry storage, metrics and updater to be more effective and send less often.
155
- - Bugfixing - Updated default values for `scheduler.impressionsRefreshRate` config parameter: 300s for OPTIMIZED impression mode and 60s for DEBUG impression mode.
159
+ - Bugfix - Updated default values for `scheduler.impressionsRefreshRate` config parameter: 300s for OPTIMIZED impression mode and 60s for DEBUG impression mode.
156
160
 
157
161
  1.3.1 (April 19, 2022)
158
- - Bugfixing - Added peer dependencies to avoid issues when requiring some third-party dependencies used by modules of the package (Related to issue https://github.com/splitio/javascript-client/issues/662).
159
- - Bugfixing - Updated `ready` method to rejects the promise with an Error object instead of a string value (Related to issue https://github.com/splitio/javascript-client/issues/654).
162
+ - Bugfix - Added peer dependencies to avoid issues when requiring some third-party dependencies used by modules of the package (Related to issue https://github.com/splitio/javascript-client/issues/662).
163
+ - Bugfix - Updated `ready` method to rejects the promise with an Error object instead of a string value (Related to issue https://github.com/splitio/javascript-client/issues/654).
160
164
 
161
165
  1.3.0 (April 6, 2022)
162
166
  - Added user consent feature to allow delaying or disabling the data tracking from SDK until user consent is explicitly granted or declined. Read more in our docs.
@@ -165,12 +169,12 @@
165
169
  - Updated format for MySegments keys in LocalStorage, keeping backwards compatibility (issue https://github.com/splitio/javascript-client/issues/638).
166
170
  - Updated some modules due to general polishing and refactors, including updates in some log messages.
167
171
  - Updated some dependencies for vulnerability fixes.
168
- - Bugfixing - Updated internal isObject utility function, to avoid unexpected behaviors on frameworks and libraries that uses multiple VM contexts, like NuxtJS dev server.
169
- - Bugfixing - Fixed validation of `core.key` SDK configuration param, to parse it into a string and log a warning when passing a number (Related to issue https://github.com/splitio/react-native-client/issues/19).
170
- - Bugfixing - Fixed validation of `sync.impressionsMode` SDK configuration param, to avoid an exception on SplitFactory instantiation when passing a non-string value.
171
- - Bugfixing - Fixed an issue with `connectionTimeout` options params of Redis storage, that was being ignored and not passed down to the underlying ioredis client.
172
- - Bugfixing - Fixed streaming synchronization issue with multiple clients.
173
- - Bugfixing - Fixed issue with internal Map ponyfill that results in logger not working properly on IE11 browser.
172
+ - Bugfix - Updated internal isObject utility function, to avoid unexpected behaviors on frameworks and libraries that uses multiple VM contexts, like NuxtJS dev server.
173
+ - Bugfix - Fixed validation of `core.key` SDK configuration param, to parse it into a string and log a warning when passing a number (Related to issue https://github.com/splitio/react-native-client/issues/19).
174
+ - Bugfix - Fixed validation of `sync.impressionsMode` SDK configuration param, to avoid an exception on SplitFactory instantiation when passing a non-string value.
175
+ - Bugfix - Fixed an issue with `connectionTimeout` options params of Redis storage, that was being ignored and not passed down to the underlying ioredis client.
176
+ - Bugfix - Fixed streaming synchronization issue with multiple clients.
177
+ - Bugfix - Fixed issue with internal Map ponyfill that results in logger not working properly on IE11 browser.
174
178
 
175
179
  1.2.0 (January 19, 2022)
176
180
  - Added support to SDK clients on browser to optionally bind attributes to the client, keeping these loaded within the SDK along with the user ID, for easier usage when requesting flag.
@@ -180,7 +184,7 @@
180
184
  customers to implement this caching with any storage technology of choice and connect it to the SDK instance to be used instead of its default in-memory storage.
181
185
  - Updated multiple modules due to general polishing and improvements, including the replacement of default exports with named exports, to avoid runtime errors with some particular configurations of Webpack projects.
182
186
  - Updated ioredis dependency for vulnerability fixes.
183
- - Bugfixing - Fixed issue returning dynamic configs if treatment name contains a dot (".").
187
+ - Bugfix - Fixed issue returning dynamic configs if treatment name contains a dot (".").
184
188
 
185
189
  1.0.0 (October 20, 2021)
186
190
  - BREAKING CHANGE on multiple modules due to general polishing, improvements and bug fixes. In most cases the change is to use named exports. This affected mostly modules related with synchronization and storages.
@@ -1,79 +1,58 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Engine = void 0;
3
+ exports.engineParser = void 0;
4
4
  var lang_1 = require("../utils/lang");
5
5
  var parser_1 = require("./parser");
6
6
  var key_1 = require("../utils/key");
7
7
  var thenable_1 = require("../utils/promise/thenable");
8
8
  var labels_1 = require("../utils/labels");
9
9
  var constants_1 = require("../utils/constants");
10
+ var constants_2 = require("../logger/constants");
11
+ var prerequisites_1 = require("./matchers/prerequisites");
10
12
  function evaluationResult(result, defaultTreatment) {
11
13
  return {
12
14
  treatment: (0, lang_1.get)(result, 'treatment', defaultTreatment),
13
15
  label: (0, lang_1.get)(result, 'label', labels_1.NO_CONDITION_MATCH)
14
16
  };
15
17
  }
16
- var Engine = /** @class */ (function () {
17
- function Engine(baseInfo, evaluator) {
18
- this.baseInfo = baseInfo;
19
- this.evaluator = evaluator;
20
- // in case we don't have a default treatment in the instantiation, use 'control'
21
- if (typeof this.baseInfo.defaultTreatment !== 'string') {
22
- this.baseInfo.defaultTreatment = constants_1.CONTROL;
23
- }
24
- }
25
- Engine.parse = function (log, splitFlatStructure, storage) {
26
- var conditions = splitFlatStructure.conditions;
27
- var evaluator = (0, parser_1.parser)(log, conditions, storage);
28
- return new Engine(splitFlatStructure, evaluator);
29
- };
30
- Engine.prototype.getKey = function () {
31
- return this.baseInfo.name;
32
- };
33
- Engine.prototype.getTreatment = function (key, attributes, splitEvaluator) {
34
- var _a = this.baseInfo, killed = _a.killed, seed = _a.seed, defaultTreatment = _a.defaultTreatment, trafficAllocation = _a.trafficAllocation, trafficAllocationSeed = _a.trafficAllocationSeed;
35
- var parsedKey;
36
- var treatment;
37
- var label;
38
- try {
39
- parsedKey = (0, key_1.keyParser)(key);
40
- }
41
- catch (err) {
42
- return {
43
- treatment: constants_1.CONTROL,
44
- label: labels_1.EXCEPTION
45
- };
46
- }
47
- if (this.isGarbage()) {
48
- treatment = constants_1.CONTROL;
49
- label = labels_1.SPLIT_ARCHIVED;
50
- }
51
- else if (killed) {
52
- treatment = defaultTreatment;
53
- label = labels_1.SPLIT_KILLED;
54
- }
55
- else {
56
- var evaluation = this.evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator);
57
- // Evaluation could be async, so we should handle that case checking for a
58
- // thenable object
59
- if ((0, thenable_1.thenable)(evaluation)) {
60
- return evaluation.then(function (result) { return evaluationResult(result, defaultTreatment); });
18
+ function engineParser(log, split, storage) {
19
+ var killed = split.killed, seed = split.seed, trafficAllocation = split.trafficAllocation, trafficAllocationSeed = split.trafficAllocationSeed, status = split.status, conditions = split.conditions, prerequisites = split.prerequisites;
20
+ var defaultTreatment = (0, lang_1.isString)(split.defaultTreatment) ? split.defaultTreatment : constants_1.CONTROL;
21
+ var evaluator = (0, parser_1.parser)(log, conditions, storage);
22
+ var prerequisiteMatcher = (0, prerequisites_1.prerequisitesMatcherContext)(prerequisites, storage, log);
23
+ return {
24
+ getTreatment: function (key, attributes, splitEvaluator) {
25
+ var parsedKey = (0, key_1.keyParser)(key);
26
+ function evaluate(prerequisitesMet) {
27
+ if (!prerequisitesMet) {
28
+ log.debug(constants_2.ENGINE_DEFAULT, ['Prerequisite not met']);
29
+ return {
30
+ treatment: defaultTreatment,
31
+ label: labels_1.PREREQUISITES_NOT_MET
32
+ };
33
+ }
34
+ var evaluation = evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator);
35
+ return (0, thenable_1.thenable)(evaluation) ?
36
+ evaluation.then(function (result) { return evaluationResult(result, defaultTreatment); }) :
37
+ evaluationResult(evaluation, defaultTreatment);
61
38
  }
62
- else {
63
- return evaluationResult(evaluation, defaultTreatment);
39
+ if (status === 'ARCHIVED')
40
+ return {
41
+ treatment: constants_1.CONTROL,
42
+ label: labels_1.SPLIT_ARCHIVED
43
+ };
44
+ if (killed) {
45
+ log.debug(constants_2.ENGINE_DEFAULT, ['Flag is killed']);
46
+ return {
47
+ treatment: defaultTreatment,
48
+ label: labels_1.SPLIT_KILLED
49
+ };
64
50
  }
51
+ var prerequisitesMet = prerequisiteMatcher({ key: key, attributes: attributes }, splitEvaluator);
52
+ return (0, thenable_1.thenable)(prerequisitesMet) ?
53
+ prerequisitesMet.then(evaluate) :
54
+ evaluate(prerequisitesMet);
65
55
  }
66
- return {
67
- treatment: treatment,
68
- label: label
69
- };
70
- };
71
- Engine.prototype.isGarbage = function () {
72
- return this.baseInfo.status === 'ARCHIVED';
73
56
  };
74
- Engine.prototype.getChangeNumber = function () {
75
- return this.baseInfo.changeNumber;
76
- };
77
- return Engine;
78
- }());
79
- exports.Engine = Engine;
57
+ }
58
+ exports.engineParser = engineParser;
@@ -97,19 +97,19 @@ function getEvaluation(log, key, splitJSON, attributes, storage) {
97
97
  config: null
98
98
  };
99
99
  if (splitJSON) {
100
- var split_1 = Engine_1.Engine.parse(log, splitJSON, storage);
101
- evaluation = split_1.getTreatment(key, attributes, evaluateFeature);
100
+ var split = (0, Engine_1.engineParser)(log, splitJSON, storage);
101
+ evaluation = split.getTreatment(key, attributes, evaluateFeature);
102
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
- result.changeNumber = split_1.getChangeNumber();
105
+ result.changeNumber = splitJSON.changeNumber;
106
106
  result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
107
107
  result.impressionsDisabled = splitJSON.impressionsDisabled;
108
108
  return result;
109
109
  });
110
110
  }
111
111
  else {
112
- evaluation.changeNumber = split_1.getChangeNumber(); // Always sync and optional
112
+ evaluation.changeNumber = splitJSON.changeNumber;
113
113
  evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
114
114
  evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
115
115
  }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prerequisitesMatcherContext = void 0;
4
+ var thenable_1 = require("../../utils/promise/thenable");
5
+ function prerequisitesMatcherContext(prerequisites, storage, log) {
6
+ if (prerequisites === void 0) { prerequisites = []; }
7
+ return function prerequisitesMatcher(_a, splitEvaluator) {
8
+ var key = _a.key, attributes = _a.attributes;
9
+ function evaluatePrerequisite(prerequisite) {
10
+ var evaluation = splitEvaluator(log, key, prerequisite.n, attributes, storage);
11
+ return (0, thenable_1.thenable)(evaluation) ?
12
+ evaluation.then(function (evaluation) { return prerequisite.ts.indexOf(evaluation.treatment) !== -1; }) :
13
+ prerequisite.ts.indexOf(evaluation.treatment) !== -1;
14
+ }
15
+ return prerequisites.reduce(function (prerequisitesMet, prerequisite) {
16
+ return (0, thenable_1.thenable)(prerequisitesMet) ?
17
+ prerequisitesMet.then(function (prerequisitesMet) { return prerequisitesMet ? evaluatePrerequisite(prerequisite) : false; }) :
18
+ prerequisitesMet ? evaluatePrerequisite(prerequisite) : false;
19
+ }, true);
20
+ };
21
+ }
22
+ exports.prerequisitesMatcherContext = prerequisitesMatcherContext;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ENGINE_VALUE_NO_ATTRIBUTES = exports.ENGINE_VALUE_INVALID = exports.USER_CONSENT_INITIAL = exports.USER_CONSENT_NOT_UPDATED = exports.USER_CONSENT_UPDATED = exports.IMPRESSIONS_TRACKER_SUCCESS = exports.EVENTS_TRACKER_SUCCESS = exports.SYNC_STOP_POLLING = exports.SYNC_CONTINUE_POLLING = exports.SYNC_START_POLLING = exports.SUBMITTERS_PUSH = exports.SUBMITTERS_PUSH_FULL_QUEUE = exports.STREAMING_DISCONNECTING = exports.STREAMING_DISABLED = exports.STREAMING_CONNECTING = exports.STREAMING_RECONNECT = exports.STREAMING_REFRESH_TOKEN = exports.SYNC_SPLITS_FETCH_RETRY = exports.POLLING_STOP = exports.POLLING_START = exports.POLLING_SMART_PAUSING = exports.NEW_FACTORY = exports.NEW_SHARED_CLIENT = exports.IMPRESSION_QUEUEING = exports.IMPRESSION = exports.CLIENT_READY = exports.CLIENT_READY_FROM_CACHE = exports.ENGINE_MATCHER_RESULT = exports.SETTINGS_SPLITS_FILTER = exports.SYNC_TASK_STOP = exports.SYNC_TASK_EXECUTE = exports.SYNC_TASK_START = exports.STREAMING_NEW_MESSAGE = exports.SYNC_RBS_UPDATE = exports.SYNC_SPLITS_UPDATE = exports.SYNC_SPLITS_FETCH = exports.SYNC_OFFLINE_DATA = exports.RETRIEVE_MANAGER = exports.RETRIEVE_CLIENT_EXISTING = exports.RETRIEVE_CLIENT_DEFAULT = exports.CLEANUP_DEREGISTERING = exports.CLEANUP_REGISTERING = exports.ENGINE_SANITIZE = exports.ENGINE_VALUE = exports.ENGINE_MATCHER_DEPENDENCY_PRE = exports.ENGINE_MATCHER_DEPENDENCY = exports.ENGINE_BUCKET = exports.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT = exports.ENGINE_COMBINER_IFELSEIF = exports.ENGINE_COMBINER_AND = void 0;
4
- exports.ERROR_HTTP = exports.ERROR_INVALID_CONFIG_PARAM = exports.ERROR_EMPTY_ARRAY = exports.ERROR_EMPTY = exports.ERROR_INVALID = exports.ERROR_INVALID_KEY_OBJECT = exports.ERROR_TOO_LONG = exports.ERROR_NULL = exports.ERROR_CLIENT_DESTROYED = exports.ERROR_NOT_FINITE = exports.ERROR_SIZE_EXCEEDED = exports.ERROR_NOT_PLAIN_OBJECT = exports.ERROR_EVENT_TYPE_FORMAT = exports.ERROR_EVENTS_TRACKER = exports.ERROR_IMPRESSIONS_LISTENER = exports.ERROR_IMPRESSIONS_TRACKER = exports.ERROR_STREAMING_AUTH = exports.ERROR_STREAMING_SSE = exports.ERROR_SYNC_OFFLINE_LOADING = exports.ERROR_CLIENT_CANNOT_GET_READY = exports.ERROR_CLIENT_LISTENER = exports.ERROR_LOGLEVEL_INVALID = exports.ERROR_ENGINE_COMBINER_IFELSEIF = exports.WARN_FLAGSET_WITHOUT_FLAGS = exports.WARN_FLAGSET_NOT_CONFIGURED = exports.WARN_LOWERCASE_FLAGSET = exports.WARN_INVALID_FLAGSET = exports.STREAMING_PARSING_SPLIT_UPDATE = exports.STREAMING_PARSING_MEMBERSHIPS_UPDATE = exports.WARN_SDK_KEY = exports.WARN_SPLITS_FILTER_EMPTY = exports.WARN_SPLITS_FILTER_INVALID = exports.WARN_SPLITS_FILTER_IGNORED = exports.WARN_INTEGRATION_INVALID = exports.WARN_NOT_EXISTENT_TT = exports.WARN_LOWERCASE_TRAFFIC_TYPE = exports.WARN_NOT_EXISTENT_SPLIT = exports.WARN_TRIMMING = exports.WARN_CONVERTING = exports.WARN_TRIMMING_PROPERTIES = exports.WARN_SETTING_NULL = exports.SUBMITTERS_PUSH_RETRY = exports.SUBMITTERS_PUSH_FAILS = exports.STREAMING_FALLBACK = exports.STREAMING_PARSING_MESSAGE_FAILS = exports.STREAMING_PARSING_ERROR_FAILS = exports.SYNC_SPLITS_FETCH_FAILS = exports.SYNC_MYSEGMENTS_FETCH_RETRY = exports.CLIENT_NOT_READY = exports.CLIENT_NO_LISTENER = void 0;
5
- exports.LOG_PREFIX_CLEANUP = exports.LOG_PREFIX_UNIQUE_KEYS_TRACKER = exports.LOG_PREFIX_EVENTS_TRACKER = exports.LOG_PREFIX_IMPRESSIONS_TRACKER = exports.LOG_PREFIX_SYNC_SUBMITTERS = exports.LOG_PREFIX_SYNC_POLLING = exports.LOG_PREFIX_SYNC_MYSEGMENTS = exports.LOG_PREFIX_SYNC_SEGMENTS = exports.LOG_PREFIX_SYNC_SPLITS = exports.LOG_PREFIX_SYNC_STREAMING = exports.LOG_PREFIX_SYNC_OFFLINE = exports.LOG_PREFIX_SYNC_MANAGER = exports.LOG_PREFIX_SYNC = exports.LOG_PREFIX_ENGINE_VALUE = exports.LOG_PREFIX_ENGINE_MATCHER = exports.LOG_PREFIX_ENGINE_COMBINER = exports.LOG_PREFIX_ENGINE = exports.LOG_PREFIX_CLIENT_INSTANTIATION = exports.LOG_PREFIX_INSTANTIATION = exports.LOG_PREFIX_SETTINGS = exports.ENGINE_MATCHER_ERROR = exports.ERROR_SETS_FILTER_EXCLUSIVE = exports.ERROR_TOO_MANY_SETS = exports.ERROR_MIN_CONFIG_PARAM = exports.ERROR_NOT_BOOLEAN = exports.ERROR_STORAGE_INVALID = void 0;
3
+ exports.ENGINE_VALUE_INVALID = exports.USER_CONSENT_INITIAL = exports.USER_CONSENT_NOT_UPDATED = exports.USER_CONSENT_UPDATED = exports.IMPRESSIONS_TRACKER_SUCCESS = exports.EVENTS_TRACKER_SUCCESS = exports.SYNC_STOP_POLLING = exports.SYNC_CONTINUE_POLLING = exports.SYNC_START_POLLING = exports.SUBMITTERS_PUSH = exports.SUBMITTERS_PUSH_FULL_QUEUE = exports.STREAMING_DISCONNECTING = exports.STREAMING_DISABLED = exports.STREAMING_CONNECTING = exports.STREAMING_RECONNECT = exports.STREAMING_REFRESH_TOKEN = exports.SYNC_SPLITS_FETCH_RETRY = exports.POLLING_STOP = exports.POLLING_START = exports.POLLING_SMART_PAUSING = exports.NEW_FACTORY = exports.NEW_SHARED_CLIENT = exports.IMPRESSION_QUEUEING = exports.IMPRESSION = exports.CLIENT_READY = exports.CLIENT_READY_FROM_CACHE = exports.ENGINE_DEFAULT = exports.ENGINE_MATCHER_RESULT = exports.SETTINGS_SPLITS_FILTER = exports.SYNC_TASK_STOP = exports.SYNC_TASK_EXECUTE = exports.SYNC_TASK_START = exports.STREAMING_NEW_MESSAGE = exports.SYNC_RBS_UPDATE = exports.SYNC_SPLITS_UPDATE = exports.SYNC_SPLITS_FETCH = exports.SYNC_OFFLINE_DATA = exports.RETRIEVE_MANAGER = exports.RETRIEVE_CLIENT_EXISTING = exports.RETRIEVE_CLIENT_DEFAULT = exports.CLEANUP_DEREGISTERING = exports.CLEANUP_REGISTERING = exports.ENGINE_SANITIZE = exports.ENGINE_VALUE = exports.ENGINE_MATCHER_DEPENDENCY_PRE = exports.ENGINE_MATCHER_DEPENDENCY = exports.ENGINE_BUCKET = exports.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT = exports.ENGINE_COMBINER_IFELSEIF = exports.ENGINE_COMBINER_AND = void 0;
4
+ exports.ERROR_INVALID_CONFIG_PARAM = exports.ERROR_EMPTY_ARRAY = exports.ERROR_EMPTY = exports.ERROR_INVALID = exports.ERROR_INVALID_KEY_OBJECT = exports.ERROR_TOO_LONG = exports.ERROR_NULL = exports.ERROR_CLIENT_DESTROYED = exports.ERROR_NOT_FINITE = exports.ERROR_SIZE_EXCEEDED = exports.ERROR_NOT_PLAIN_OBJECT = exports.ERROR_EVENT_TYPE_FORMAT = exports.ERROR_EVENTS_TRACKER = exports.ERROR_IMPRESSIONS_LISTENER = exports.ERROR_IMPRESSIONS_TRACKER = exports.ERROR_STREAMING_AUTH = exports.ERROR_STREAMING_SSE = exports.ERROR_SYNC_OFFLINE_LOADING = exports.ERROR_CLIENT_CANNOT_GET_READY = exports.ERROR_CLIENT_LISTENER = exports.ERROR_LOGLEVEL_INVALID = exports.ERROR_ENGINE_COMBINER_IFELSEIF = exports.WARN_FLAGSET_WITHOUT_FLAGS = exports.WARN_FLAGSET_NOT_CONFIGURED = exports.WARN_LOWERCASE_FLAGSET = exports.WARN_INVALID_FLAGSET = exports.STREAMING_PARSING_SPLIT_UPDATE = exports.STREAMING_PARSING_MEMBERSHIPS_UPDATE = exports.WARN_SDK_KEY = exports.WARN_SPLITS_FILTER_EMPTY = exports.WARN_SPLITS_FILTER_INVALID = exports.WARN_SPLITS_FILTER_IGNORED = exports.WARN_INTEGRATION_INVALID = exports.WARN_NOT_EXISTENT_TT = exports.WARN_LOWERCASE_TRAFFIC_TYPE = exports.WARN_NOT_EXISTENT_SPLIT = exports.WARN_TRIMMING = exports.WARN_CONVERTING = exports.WARN_TRIMMING_PROPERTIES = exports.WARN_SETTING_NULL = exports.SUBMITTERS_PUSH_RETRY = exports.SUBMITTERS_PUSH_FAILS = exports.STREAMING_FALLBACK = exports.STREAMING_PARSING_MESSAGE_FAILS = exports.STREAMING_PARSING_ERROR_FAILS = exports.SYNC_SPLITS_FETCH_FAILS = exports.SYNC_MYSEGMENTS_FETCH_RETRY = exports.CLIENT_NOT_READY = exports.CLIENT_NO_LISTENER = exports.ENGINE_VALUE_NO_ATTRIBUTES = void 0;
5
+ exports.LOG_PREFIX_CLEANUP = exports.LOG_PREFIX_UNIQUE_KEYS_TRACKER = exports.LOG_PREFIX_EVENTS_TRACKER = exports.LOG_PREFIX_IMPRESSIONS_TRACKER = exports.LOG_PREFIX_SYNC_SUBMITTERS = exports.LOG_PREFIX_SYNC_POLLING = exports.LOG_PREFIX_SYNC_MYSEGMENTS = exports.LOG_PREFIX_SYNC_SEGMENTS = exports.LOG_PREFIX_SYNC_SPLITS = exports.LOG_PREFIX_SYNC_STREAMING = exports.LOG_PREFIX_SYNC_OFFLINE = exports.LOG_PREFIX_SYNC_MANAGER = exports.LOG_PREFIX_SYNC = exports.LOG_PREFIX_ENGINE_VALUE = exports.LOG_PREFIX_ENGINE_MATCHER = exports.LOG_PREFIX_ENGINE_COMBINER = exports.LOG_PREFIX_ENGINE = exports.LOG_PREFIX_CLIENT_INSTANTIATION = exports.LOG_PREFIX_INSTANTIATION = exports.LOG_PREFIX_SETTINGS = exports.ENGINE_MATCHER_ERROR = exports.ERROR_SETS_FILTER_EXCLUSIVE = exports.ERROR_TOO_MANY_SETS = exports.ERROR_MIN_CONFIG_PARAM = exports.ERROR_NOT_BOOLEAN = exports.ERROR_STORAGE_INVALID = exports.ERROR_HTTP = void 0;
6
6
  /**
7
7
  * Message codes used to trim string log messages from commons and client-side API modules,
8
8
  * in order to reduce the minimal SDK size for Browser and eventually other client-side environments.
@@ -33,6 +33,7 @@ exports.SYNC_TASK_EXECUTE = 37;
33
33
  exports.SYNC_TASK_STOP = 38;
34
34
  exports.SETTINGS_SPLITS_FILTER = 39;
35
35
  exports.ENGINE_MATCHER_RESULT = 40;
36
+ exports.ENGINE_DEFAULT = 41;
36
37
  exports.CLIENT_READY_FROM_CACHE = 100;
37
38
  exports.CLIENT_READY = 101;
38
39
  exports.IMPRESSION = 102;
@@ -15,6 +15,7 @@ exports.codesDebug = info_1.codesInfo.concat([
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'],
18
+ [c.ENGINE_DEFAULT, c.LOG_PREFIX_ENGINE + 'Evaluates to default treatment. %s'],
18
19
  // SDK
19
20
  [c.CLEANUP_REGISTERING, c.LOG_PREFIX_CLEANUP + 'Registering cleanup handler %s'],
20
21
  [c.CLEANUP_DEREGISTERING, c.LOG_PREFIX_CLEANUP + 'Deregistering cleanup handler %s'],
@@ -24,8 +25,8 @@ exports.codesDebug = info_1.codesInfo.concat([
24
25
  // synchronizer
25
26
  [c.SYNC_OFFLINE_DATA, c.LOG_PREFIX_SYNC_OFFLINE + 'Feature flags data: \n%s'],
26
27
  [c.SYNC_SPLITS_FETCH, c.LOG_PREFIX_SYNC_SPLITS + 'Spin up feature flags update using since = %s and rbSince = %s.'],
27
- [c.SYNC_SPLITS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New feature flags %s. Removed feature flags %s.'],
28
- [c.SYNC_RBS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New rule-based segments %s. Removed rule-based segments %s.'],
28
+ [c.SYNC_SPLITS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New feature flags: %s. Removed feature flags: %s.'],
29
+ [c.SYNC_RBS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New rule-based segments: %s. Removed rule-based segments: %s.'],
29
30
  [c.STREAMING_NEW_MESSAGE, c.LOG_PREFIX_SYNC_STREAMING + 'New SSE message received, with data: %s.'],
30
31
  [c.SYNC_TASK_START, c.LOG_PREFIX_SYNC + ': Starting %s. Running each %s millis'],
31
32
  [c.SYNC_TASK_EXECUTE, c.LOG_PREFIX_SYNC + ': Running %s'],
@@ -29,7 +29,8 @@ function objectToView(splitObject) {
29
29
  configs: splitObject.configurations || {},
30
30
  sets: splitObject.sets || [],
31
31
  defaultTreatment: splitObject.defaultTreatment,
32
- impressionsDisabled: splitObject.impressionsDisabled === true
32
+ impressionsDisabled: splitObject.impressionsDisabled === true,
33
+ prerequisites: (splitObject.prerequisites || []).map(function (p) { return ({ flagName: p.n, treatments: p.ts }); }),
33
34
  };
34
35
  }
35
36
  function objectsToViews(splitObjects) {
@@ -5,6 +5,7 @@ var objectAssign_1 = require("../utils/lang/objectAssign");
5
5
  var constants_1 = require("../logger/constants");
6
6
  var decorateHeaders_1 = require("./decorateHeaders");
7
7
  var timeout_1 = require("../utils/promise/timeout");
8
+ var PENDING_FETCH_ERROR_TIMEOUT = 100;
8
9
  var messageNoFetch = 'Global fetch API is not available.';
9
10
  /**
10
11
  * Factory of Split HTTP clients, which are HTTP clients with predefined headers for Split endpoints.
@@ -44,8 +45,8 @@ function splitHttpClientFactory(settings, _a) {
44
45
  // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
45
46
  .then(function (response) {
46
47
  if (!response.ok) {
47
- // timeout after 100ms because `text()` promise doesn't settle in some implementations and cases (e.g. no content)
48
- return (0, timeout_1.timeout)(100, response.text()).then(function (message) { return Promise.reject({ response: response, message: message }); }, function () { return Promise.reject({ response: response }); });
48
+ // timeout since `text()` promise might not settle in some fetch implementations and cases (e.g. no content)
49
+ return (0, timeout_1.timeout)(PENDING_FETCH_ERROR_TIMEOUT, response.text()).then(function (message) { return Promise.reject({ response: response, message: message }); }, function () { return Promise.reject({ response: response }); });
49
50
  }
50
51
  latencyTracker();
51
52
  return response;
@@ -41,7 +41,7 @@ function splitChangesFetcherFactory(fetchSplitChanges, settings, storage) {
41
41
  return splitsPromise
42
42
  .then(function (resp) { return resp.json(); })
43
43
  .then(function (data) {
44
- // Using flag spec version 1.2
44
+ // Using flag spec version 1.2 or below
45
45
  if (data.splits) {
46
46
  return {
47
47
  ff: {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UNSUPPORTED_MATCHER_TYPE = exports.NOT_IN_SPLIT = exports.SPLIT_ARCHIVED = exports.EXCEPTION = exports.SDK_NOT_READY = exports.SPLIT_NOT_FOUND = exports.NO_CONDITION_MATCH = exports.SPLIT_KILLED = void 0;
3
+ exports.PREREQUISITES_NOT_MET = exports.UNSUPPORTED_MATCHER_TYPE = exports.NOT_IN_SPLIT = exports.SPLIT_ARCHIVED = exports.EXCEPTION = exports.SDK_NOT_READY = exports.SPLIT_NOT_FOUND = exports.NO_CONDITION_MATCH = exports.SPLIT_KILLED = void 0;
4
4
  exports.SPLIT_KILLED = 'killed';
5
5
  exports.NO_CONDITION_MATCH = 'default rule';
6
6
  exports.SPLIT_NOT_FOUND = 'definition not found';
@@ -9,3 +9,4 @@ exports.EXCEPTION = 'exception';
9
9
  exports.SPLIT_ARCHIVED = 'archived';
10
10
  exports.NOT_IN_SPLIT = 'not in split';
11
11
  exports.UNSUPPORTED_MATCHER_TYPE = 'targeting rule type unsupported by sdk';
12
+ exports.PREREQUISITES_NOT_MET = 'prerequisites not met';
@@ -1,76 +1,54 @@
1
- import { get } from '../utils/lang';
1
+ import { get, isString } from '../utils/lang';
2
2
  import { parser } from './parser';
3
3
  import { keyParser } from '../utils/key';
4
4
  import { thenable } from '../utils/promise/thenable';
5
- import { EXCEPTION, NO_CONDITION_MATCH, SPLIT_ARCHIVED, SPLIT_KILLED } from '../utils/labels';
5
+ import { NO_CONDITION_MATCH, SPLIT_ARCHIVED, SPLIT_KILLED, PREREQUISITES_NOT_MET } from '../utils/labels';
6
6
  import { CONTROL } from '../utils/constants';
7
+ import { ENGINE_DEFAULT } from '../logger/constants';
8
+ import { prerequisitesMatcherContext } from './matchers/prerequisites';
7
9
  function evaluationResult(result, defaultTreatment) {
8
10
  return {
9
11
  treatment: get(result, 'treatment', defaultTreatment),
10
12
  label: get(result, 'label', NO_CONDITION_MATCH)
11
13
  };
12
14
  }
13
- var Engine = /** @class */ (function () {
14
- function Engine(baseInfo, evaluator) {
15
- this.baseInfo = baseInfo;
16
- this.evaluator = evaluator;
17
- // in case we don't have a default treatment in the instantiation, use 'control'
18
- if (typeof this.baseInfo.defaultTreatment !== 'string') {
19
- this.baseInfo.defaultTreatment = CONTROL;
20
- }
21
- }
22
- Engine.parse = function (log, splitFlatStructure, storage) {
23
- var conditions = splitFlatStructure.conditions;
24
- var evaluator = parser(log, conditions, storage);
25
- return new Engine(splitFlatStructure, evaluator);
26
- };
27
- Engine.prototype.getKey = function () {
28
- return this.baseInfo.name;
29
- };
30
- Engine.prototype.getTreatment = function (key, attributes, splitEvaluator) {
31
- var _a = this.baseInfo, killed = _a.killed, seed = _a.seed, defaultTreatment = _a.defaultTreatment, trafficAllocation = _a.trafficAllocation, trafficAllocationSeed = _a.trafficAllocationSeed;
32
- var parsedKey;
33
- var treatment;
34
- var label;
35
- try {
36
- parsedKey = keyParser(key);
37
- }
38
- catch (err) {
39
- return {
40
- treatment: CONTROL,
41
- label: EXCEPTION
42
- };
43
- }
44
- if (this.isGarbage()) {
45
- treatment = CONTROL;
46
- label = SPLIT_ARCHIVED;
47
- }
48
- else if (killed) {
49
- treatment = defaultTreatment;
50
- label = SPLIT_KILLED;
51
- }
52
- else {
53
- var evaluation = this.evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator);
54
- // Evaluation could be async, so we should handle that case checking for a
55
- // thenable object
56
- if (thenable(evaluation)) {
57
- return evaluation.then(function (result) { return evaluationResult(result, defaultTreatment); });
15
+ export function engineParser(log, split, storage) {
16
+ var killed = split.killed, seed = split.seed, trafficAllocation = split.trafficAllocation, trafficAllocationSeed = split.trafficAllocationSeed, status = split.status, conditions = split.conditions, prerequisites = split.prerequisites;
17
+ var defaultTreatment = isString(split.defaultTreatment) ? split.defaultTreatment : CONTROL;
18
+ var evaluator = parser(log, conditions, storage);
19
+ var prerequisiteMatcher = prerequisitesMatcherContext(prerequisites, storage, log);
20
+ return {
21
+ getTreatment: function (key, attributes, splitEvaluator) {
22
+ var parsedKey = keyParser(key);
23
+ function evaluate(prerequisitesMet) {
24
+ if (!prerequisitesMet) {
25
+ log.debug(ENGINE_DEFAULT, ['Prerequisite not met']);
26
+ return {
27
+ treatment: defaultTreatment,
28
+ label: PREREQUISITES_NOT_MET
29
+ };
30
+ }
31
+ var evaluation = evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator);
32
+ return thenable(evaluation) ?
33
+ evaluation.then(function (result) { return evaluationResult(result, defaultTreatment); }) :
34
+ evaluationResult(evaluation, defaultTreatment);
58
35
  }
59
- else {
60
- return evaluationResult(evaluation, defaultTreatment);
36
+ if (status === 'ARCHIVED')
37
+ return {
38
+ treatment: CONTROL,
39
+ label: SPLIT_ARCHIVED
40
+ };
41
+ if (killed) {
42
+ log.debug(ENGINE_DEFAULT, ['Flag is killed']);
43
+ return {
44
+ treatment: defaultTreatment,
45
+ label: SPLIT_KILLED
46
+ };
61
47
  }
48
+ var prerequisitesMet = prerequisiteMatcher({ key: key, attributes: attributes }, splitEvaluator);
49
+ return thenable(prerequisitesMet) ?
50
+ prerequisitesMet.then(evaluate) :
51
+ evaluate(prerequisitesMet);
62
52
  }
63
- return {
64
- treatment: treatment,
65
- label: label
66
- };
67
- };
68
- Engine.prototype.isGarbage = function () {
69
- return this.baseInfo.status === 'ARCHIVED';
70
53
  };
71
- Engine.prototype.getChangeNumber = function () {
72
- return this.baseInfo.changeNumber;
73
- };
74
- return Engine;
75
- }());
76
- export { Engine };
54
+ }
@@ -1,4 +1,4 @@
1
- import { Engine } from './Engine';
1
+ import { engineParser } from './Engine';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { EXCEPTION, SPLIT_NOT_FOUND } from '../utils/labels';
4
4
  import { CONTROL } from '../utils/constants';
@@ -91,19 +91,19 @@ function getEvaluation(log, key, splitJSON, attributes, storage) {
91
91
  config: null
92
92
  };
93
93
  if (splitJSON) {
94
- var split_1 = Engine.parse(log, splitJSON, storage);
95
- evaluation = split_1.getTreatment(key, attributes, evaluateFeature);
94
+ var split = engineParser(log, splitJSON, storage);
95
+ evaluation = split.getTreatment(key, attributes, evaluateFeature);
96
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
- result.changeNumber = split_1.getChangeNumber();
99
+ result.changeNumber = splitJSON.changeNumber;
100
100
  result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
101
101
  result.impressionsDisabled = splitJSON.impressionsDisabled;
102
102
  return result;
103
103
  });
104
104
  }
105
105
  else {
106
- evaluation.changeNumber = split_1.getChangeNumber(); // Always sync and optional
106
+ evaluation.changeNumber = splitJSON.changeNumber;
107
107
  evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
108
108
  evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
109
109
  }
@@ -0,0 +1,18 @@
1
+ import { thenable } from '../../utils/promise/thenable';
2
+ export function prerequisitesMatcherContext(prerequisites, storage, log) {
3
+ if (prerequisites === void 0) { prerequisites = []; }
4
+ return function prerequisitesMatcher(_a, splitEvaluator) {
5
+ var key = _a.key, attributes = _a.attributes;
6
+ function evaluatePrerequisite(prerequisite) {
7
+ var evaluation = splitEvaluator(log, key, prerequisite.n, attributes, storage);
8
+ return thenable(evaluation) ?
9
+ evaluation.then(function (evaluation) { return prerequisite.ts.indexOf(evaluation.treatment) !== -1; }) :
10
+ prerequisite.ts.indexOf(evaluation.treatment) !== -1;
11
+ }
12
+ return prerequisites.reduce(function (prerequisitesMet, prerequisite) {
13
+ return thenable(prerequisitesMet) ?
14
+ prerequisitesMet.then(function (prerequisitesMet) { return prerequisitesMet ? evaluatePrerequisite(prerequisite) : false; }) :
15
+ prerequisitesMet ? evaluatePrerequisite(prerequisite) : false;
16
+ }, true);
17
+ };
18
+ }
@@ -28,6 +28,7 @@ export var SYNC_TASK_EXECUTE = 37;
28
28
  export var SYNC_TASK_STOP = 38;
29
29
  export var SETTINGS_SPLITS_FILTER = 39;
30
30
  export var ENGINE_MATCHER_RESULT = 40;
31
+ export var ENGINE_DEFAULT = 41;
31
32
  export var CLIENT_READY_FROM_CACHE = 100;
32
33
  export var CLIENT_READY = 101;
33
34
  export var IMPRESSION = 102;
@@ -11,6 +11,7 @@ export var codesDebug = codesInfo.concat([
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'],
14
+ [c.ENGINE_DEFAULT, c.LOG_PREFIX_ENGINE + 'Evaluates to default treatment. %s'],
14
15
  // SDK
15
16
  [c.CLEANUP_REGISTERING, c.LOG_PREFIX_CLEANUP + 'Registering cleanup handler %s'],
16
17
  [c.CLEANUP_DEREGISTERING, c.LOG_PREFIX_CLEANUP + 'Deregistering cleanup handler %s'],
@@ -20,8 +21,8 @@ export var codesDebug = codesInfo.concat([
20
21
  // synchronizer
21
22
  [c.SYNC_OFFLINE_DATA, c.LOG_PREFIX_SYNC_OFFLINE + 'Feature flags data: \n%s'],
22
23
  [c.SYNC_SPLITS_FETCH, c.LOG_PREFIX_SYNC_SPLITS + 'Spin up feature flags update using since = %s and rbSince = %s.'],
23
- [c.SYNC_SPLITS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New feature flags %s. Removed feature flags %s.'],
24
- [c.SYNC_RBS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New rule-based segments %s. Removed rule-based segments %s.'],
24
+ [c.SYNC_SPLITS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New feature flags: %s. Removed feature flags: %s.'],
25
+ [c.SYNC_RBS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New rule-based segments: %s. Removed rule-based segments: %s.'],
25
26
  [c.STREAMING_NEW_MESSAGE, c.LOG_PREFIX_SYNC_STREAMING + 'New SSE message received, with data: %s.'],
26
27
  [c.SYNC_TASK_START, c.LOG_PREFIX_SYNC + ': Starting %s. Running each %s millis'],
27
28
  [c.SYNC_TASK_EXECUTE, c.LOG_PREFIX_SYNC + ': Running %s'],
@@ -26,7 +26,8 @@ function objectToView(splitObject) {
26
26
  configs: splitObject.configurations || {},
27
27
  sets: splitObject.sets || [],
28
28
  defaultTreatment: splitObject.defaultTreatment,
29
- impressionsDisabled: splitObject.impressionsDisabled === true
29
+ impressionsDisabled: splitObject.impressionsDisabled === true,
30
+ prerequisites: (splitObject.prerequisites || []).map(function (p) { return ({ flagName: p.n, treatments: p.ts }); }),
30
31
  };
31
32
  }
32
33
  function objectsToViews(splitObjects) {
@@ -2,6 +2,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { ERROR_HTTP, ERROR_CLIENT_CANNOT_GET_READY } from '../logger/constants';
3
3
  import { decorateHeaders, removeNonISO88591 } from './decorateHeaders';
4
4
  import { timeout } from '../utils/promise/timeout';
5
+ var PENDING_FETCH_ERROR_TIMEOUT = 100;
5
6
  var messageNoFetch = 'Global fetch API is not available.';
6
7
  /**
7
8
  * Factory of Split HTTP clients, which are HTTP clients with predefined headers for Split endpoints.
@@ -41,8 +42,8 @@ export function splitHttpClientFactory(settings, _a) {
41
42
  // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
42
43
  .then(function (response) {
43
44
  if (!response.ok) {
44
- // timeout after 100ms because `text()` promise doesn't settle in some implementations and cases (e.g. no content)
45
- return timeout(100, response.text()).then(function (message) { return Promise.reject({ response: response, message: message }); }, function () { return Promise.reject({ response: response }); });
45
+ // timeout since `text()` promise might not settle in some fetch implementations and cases (e.g. no content)
46
+ return timeout(PENDING_FETCH_ERROR_TIMEOUT, response.text()).then(function (message) { return Promise.reject({ response: response, message: message }); }, function () { return Promise.reject({ response: response }); });
46
47
  }
47
48
  latencyTracker();
48
49
  return response;
@@ -38,7 +38,7 @@ export function splitChangesFetcherFactory(fetchSplitChanges, settings, storage)
38
38
  return splitsPromise
39
39
  .then(function (resp) { return resp.json(); })
40
40
  .then(function (data) {
41
- // Using flag spec version 1.2
41
+ // Using flag spec version 1.2 or below
42
42
  if (data.splits) {
43
43
  return {
44
44
  ff: {
@@ -6,3 +6,4 @@ export var EXCEPTION = 'exception';
6
6
  export var SPLIT_ARCHIVED = 'archived';
7
7
  export var NOT_IN_SPLIT = 'not in split';
8
8
  export var UNSUPPORTED_MATCHER_TYPE = 'targeting rule type unsupported by sdk';
9
+ export var PREREQUISITES_NOT_MET = 'prerequisites not met';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "2.2.1-rc.5",
3
+ "version": "2.3.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
@@ -220,6 +220,10 @@ export interface ISplit {
220
220
  changeNumber: number,
221
221
  status: 'ACTIVE' | 'ARCHIVED',
222
222
  conditions: ISplitCondition[],
223
+ prerequisites?: {
224
+ n: string,
225
+ ts: string[]
226
+ }[]
223
227
  killed: boolean,
224
228
  defaultTreatment: string,
225
229
  trafficTypeName: string,
@@ -1,14 +1,16 @@
1
- import { get } from '../utils/lang';
1
+ import { get, isString } from '../utils/lang';
2
2
  import { parser } from './parser';
3
3
  import { keyParser } from '../utils/key';
4
4
  import { thenable } from '../utils/promise/thenable';
5
- import { EXCEPTION, NO_CONDITION_MATCH, SPLIT_ARCHIVED, SPLIT_KILLED } from '../utils/labels';
5
+ import { NO_CONDITION_MATCH, SPLIT_ARCHIVED, SPLIT_KILLED, PREREQUISITES_NOT_MET } from '../utils/labels';
6
6
  import { CONTROL } from '../utils/constants';
7
7
  import { ISplit, MaybeThenable } from '../dtos/types';
8
8
  import SplitIO from '../../types/splitio';
9
9
  import { IStorageAsync, IStorageSync } from '../storages/types';
10
- import { IEvaluation, IEvaluationResult, IEvaluator, ISplitEvaluator } from './types';
10
+ import { IEvaluation, IEvaluationResult, ISplitEvaluator } from './types';
11
11
  import { ILogger } from '../logger/types';
12
+ import { ENGINE_DEFAULT } from '../logger/constants';
13
+ import { prerequisitesMatcherContext } from './matchers/prerequisites';
12
14
 
13
15
  function evaluationResult(result: IEvaluation | undefined, defaultTreatment: string): IEvaluationResult {
14
16
  return {
@@ -17,84 +19,55 @@ function evaluationResult(result: IEvaluation | undefined, defaultTreatment: str
17
19
  };
18
20
  }
19
21
 
20
- export class Engine {
22
+ export function engineParser(log: ILogger, split: ISplit, storage: IStorageSync | IStorageAsync) {
23
+ const { killed, seed, trafficAllocation, trafficAllocationSeed, status, conditions, prerequisites } = split;
21
24
 
22
- constructor(private baseInfo: ISplit, private evaluator: IEvaluator) {
25
+ const defaultTreatment = isString(split.defaultTreatment) ? split.defaultTreatment : CONTROL;
23
26
 
24
- // in case we don't have a default treatment in the instantiation, use 'control'
25
- if (typeof this.baseInfo.defaultTreatment !== 'string') {
26
- this.baseInfo.defaultTreatment = CONTROL;
27
- }
28
- }
27
+ const evaluator = parser(log, conditions, storage);
28
+ const prerequisiteMatcher = prerequisitesMatcherContext(prerequisites, storage, log);
29
+
30
+ return {
29
31
 
30
- static parse(log: ILogger, splitFlatStructure: ISplit, storage: IStorageSync | IStorageAsync) {
31
- const conditions = splitFlatStructure.conditions;
32
- const evaluator = parser(log, conditions, storage);
32
+ getTreatment(key: SplitIO.SplitKey, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator): MaybeThenable<IEvaluationResult> {
33
33
 
34
- return new Engine(splitFlatStructure, evaluator);
35
- }
34
+ const parsedKey = keyParser(key);
36
35
 
37
- getKey() {
38
- return this.baseInfo.name;
39
- }
36
+ function evaluate(prerequisitesMet: boolean) {
37
+ if (!prerequisitesMet) {
38
+ log.debug(ENGINE_DEFAULT, ['Prerequisite not met']);
39
+ return {
40
+ treatment: defaultTreatment,
41
+ label: PREREQUISITES_NOT_MET
42
+ };
43
+ }
40
44
 
41
- getTreatment(key: SplitIO.SplitKey, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator): MaybeThenable<IEvaluationResult> {
42
- const {
43
- killed,
44
- seed,
45
- defaultTreatment,
46
- trafficAllocation,
47
- trafficAllocationSeed
48
- } = this.baseInfo;
49
- let parsedKey;
50
- let treatment;
51
- let label;
45
+ const evaluation = evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator) as MaybeThenable<IEvaluation>;
46
+
47
+ return thenable(evaluation) ?
48
+ evaluation.then(result => evaluationResult(result, defaultTreatment)) :
49
+ evaluationResult(evaluation, defaultTreatment);
50
+ }
52
51
 
53
- try {
54
- parsedKey = keyParser(key);
55
- } catch (err) {
56
- return {
52
+ if (status === 'ARCHIVED') return {
57
53
  treatment: CONTROL,
58
- label: EXCEPTION
54
+ label: SPLIT_ARCHIVED
59
55
  };
60
- }
61
-
62
- if (this.isGarbage()) {
63
- treatment = CONTROL;
64
- label = SPLIT_ARCHIVED;
65
- } else if (killed) {
66
- treatment = defaultTreatment;
67
- label = SPLIT_KILLED;
68
- } else {
69
- const evaluation = this.evaluator(
70
- parsedKey,
71
- seed,
72
- trafficAllocation,
73
- trafficAllocationSeed,
74
- attributes,
75
- splitEvaluator
76
- ) as MaybeThenable<IEvaluation>;
77
56
 
78
- // Evaluation could be async, so we should handle that case checking for a
79
- // thenable object
80
- if (thenable(evaluation)) {
81
- return evaluation.then(result => evaluationResult(result, defaultTreatment));
82
- } else {
83
- return evaluationResult(evaluation, defaultTreatment);
57
+ if (killed) {
58
+ log.debug(ENGINE_DEFAULT, ['Flag is killed']);
59
+ return {
60
+ treatment: defaultTreatment,
61
+ label: SPLIT_KILLED
62
+ };
84
63
  }
85
- }
86
64
 
87
- return {
88
- treatment,
89
- label
90
- };
91
- }
65
+ const prerequisitesMet = prerequisiteMatcher({ key, attributes }, splitEvaluator);
92
66
 
93
- isGarbage() {
94
- return this.baseInfo.status === 'ARCHIVED';
95
- }
67
+ return thenable(prerequisitesMet) ?
68
+ prerequisitesMet.then(evaluate) :
69
+ evaluate(prerequisitesMet);
70
+ }
71
+ };
96
72
 
97
- getChangeNumber() {
98
- return this.baseInfo.changeNumber;
99
- }
100
73
  }
@@ -1,4 +1,4 @@
1
- import { Engine } from './Engine';
1
+ import { engineParser } from './Engine';
2
2
  import { thenable } from '../utils/promise/thenable';
3
3
  import { EXCEPTION, SPLIT_NOT_FOUND } from '../utils/labels';
4
4
  import { CONTROL } from '../utils/constants';
@@ -99,9 +99,7 @@ export function evaluateFeaturesByFlagSets(
99
99
  ): MaybeThenable<Record<string, IEvaluationResult>> {
100
100
  let storedFlagNames: MaybeThenable<Set<string>[]>;
101
101
 
102
- function evaluate(
103
- featureFlagsByFlagSets: Set<string>[],
104
- ) {
102
+ function evaluate(featureFlagsByFlagSets: Set<string>[]) {
105
103
  let featureFlags = new Set<string>();
106
104
  for (let i = 0; i < flagSets.length; i++) {
107
105
  const featureFlagByFlagSet = featureFlagsByFlagSets[i];
@@ -148,20 +146,20 @@ function getEvaluation(
148
146
  };
149
147
 
150
148
  if (splitJSON) {
151
- const split = Engine.parse(log, splitJSON, storage);
149
+ const split = engineParser(log, splitJSON, storage);
152
150
  evaluation = split.getTreatment(key, attributes, evaluateFeature);
153
151
 
154
152
  // If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
155
153
  if (thenable(evaluation)) {
156
154
  return evaluation.then(result => {
157
- result.changeNumber = split.getChangeNumber();
155
+ result.changeNumber = splitJSON.changeNumber;
158
156
  result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
159
157
  result.impressionsDisabled = splitJSON.impressionsDisabled;
160
158
 
161
159
  return result;
162
160
  });
163
161
  } else {
164
- evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional
162
+ evaluation.changeNumber = splitJSON.changeNumber;
165
163
  evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
166
164
  evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
167
165
  }
@@ -0,0 +1,24 @@
1
+ import { ISplit, MaybeThenable } from '../../dtos/types';
2
+ import { IStorageAsync, IStorageSync } from '../../storages/types';
3
+ import { ILogger } from '../../logger/types';
4
+ import { thenable } from '../../utils/promise/thenable';
5
+ import { IDependencyMatcherValue, ISplitEvaluator } from '../types';
6
+
7
+ export function prerequisitesMatcherContext(prerequisites: ISplit['prerequisites'] = [], storage: IStorageSync | IStorageAsync, log: ILogger) {
8
+
9
+ return function prerequisitesMatcher({ key, attributes }: IDependencyMatcherValue, splitEvaluator: ISplitEvaluator): MaybeThenable<boolean> {
10
+
11
+ function evaluatePrerequisite(prerequisite: { n: string; ts: string[] }): MaybeThenable<boolean> {
12
+ const evaluation = splitEvaluator(log, key, prerequisite.n, attributes, storage);
13
+ return thenable(evaluation) ?
14
+ evaluation.then(evaluation => prerequisite.ts.indexOf(evaluation.treatment!) !== -1) :
15
+ prerequisite.ts.indexOf(evaluation.treatment!) !== -1;
16
+ }
17
+
18
+ return prerequisites.reduce<MaybeThenable<boolean>>((prerequisitesMet, prerequisite) => {
19
+ return thenable(prerequisitesMet) ?
20
+ prerequisitesMet.then(prerequisitesMet => prerequisitesMet ? evaluatePrerequisite(prerequisite) : false) :
21
+ prerequisitesMet ? evaluatePrerequisite(prerequisite) : false;
22
+ }, true);
23
+ };
24
+ }
@@ -28,6 +28,7 @@ export const SYNC_TASK_EXECUTE = 37;
28
28
  export const SYNC_TASK_STOP = 38;
29
29
  export const SETTINGS_SPLITS_FILTER = 39;
30
30
  export const ENGINE_MATCHER_RESULT = 40;
31
+ export const ENGINE_DEFAULT = 41;
31
32
 
32
33
  export const CLIENT_READY_FROM_CACHE = 100;
33
34
  export const CLIENT_READY = 101;
@@ -12,6 +12,7 @@ export const codesDebug: [number, string][] = codesInfo.concat([
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'],
15
+ [c.ENGINE_DEFAULT, c.LOG_PREFIX_ENGINE + 'Evaluates to default treatment. %s'],
15
16
  // SDK
16
17
  [c.CLEANUP_REGISTERING, c.LOG_PREFIX_CLEANUP + 'Registering cleanup handler %s'],
17
18
  [c.CLEANUP_DEREGISTERING, c.LOG_PREFIX_CLEANUP + 'Deregistering cleanup handler %s'],
@@ -21,8 +22,8 @@ export const codesDebug: [number, string][] = codesInfo.concat([
21
22
  // synchronizer
22
23
  [c.SYNC_OFFLINE_DATA, c.LOG_PREFIX_SYNC_OFFLINE + 'Feature flags data: \n%s'],
23
24
  [c.SYNC_SPLITS_FETCH, c.LOG_PREFIX_SYNC_SPLITS + 'Spin up feature flags update using since = %s and rbSince = %s.'],
24
- [c.SYNC_SPLITS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New feature flags %s. Removed feature flags %s.'],
25
- [c.SYNC_RBS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New rule-based segments %s. Removed rule-based segments %s.'],
25
+ [c.SYNC_SPLITS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New feature flags: %s. Removed feature flags: %s.'],
26
+ [c.SYNC_RBS_UPDATE, c.LOG_PREFIX_SYNC_SPLITS + 'New rule-based segments: %s. Removed rule-based segments: %s.'],
26
27
  [c.STREAMING_NEW_MESSAGE, c.LOG_PREFIX_SYNC_STREAMING + 'New SSE message received, with data: %s.'],
27
28
  [c.SYNC_TASK_START, c.LOG_PREFIX_SYNC + ': Starting %s. Running each %s millis'],
28
29
  [c.SYNC_TASK_EXECUTE, c.LOG_PREFIX_SYNC + ': Running %s'],
@@ -22,7 +22,7 @@ export interface IPlatform {
22
22
  /**
23
23
  * If provided, it is used to pass additional options to fetch and eventsource calls.
24
24
  */
25
- getOptions?: (settings: ISettings) => object
25
+ getOptions?: (settings: ISettings) => (object | undefined)
26
26
  /**
27
27
  * If provided, it is used to retrieve the EventSource constructor for streaming support.
28
28
  */
@@ -32,7 +32,8 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
32
32
  configs: splitObject.configurations || {},
33
33
  sets: splitObject.sets || [],
34
34
  defaultTreatment: splitObject.defaultTreatment,
35
- impressionsDisabled: splitObject.impressionsDisabled === true
35
+ impressionsDisabled: splitObject.impressionsDisabled === true,
36
+ prerequisites: (splitObject.prerequisites || []).map(p => ({ flagName: p.n, treatments: p.ts })),
36
37
  };
37
38
  }
38
39
 
@@ -6,6 +6,7 @@ import { IPlatform } from '../sdkFactory/types';
6
6
  import { decorateHeaders, removeNonISO88591 } from './decorateHeaders';
7
7
  import { timeout } from '../utils/promise/timeout';
8
8
 
9
+ const PENDING_FETCH_ERROR_TIMEOUT = 100;
9
10
  const messageNoFetch = 'Global fetch API is not available.';
10
11
 
11
12
  /**
@@ -46,8 +47,8 @@ export function splitHttpClientFactory(settings: ISettings, { getOptions, getFet
46
47
  // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
47
48
  .then(response => {
48
49
  if (!response.ok) {
49
- // timeout after 100ms because `text()` promise doesn't settle in some implementations and cases (e.g. no content)
50
- return timeout(100, response.text()).then(message => Promise.reject({ response, message }), () => Promise.reject({ response }));
50
+ // timeout since `text()` promise might not settle in some fetch implementations and cases (e.g. no content)
51
+ return timeout(PENDING_FETCH_ERROR_TIMEOUT, response.text()).then(message => Promise.reject({ response, message }), () => Promise.reject({ response }));
51
52
  }
52
53
  latencyTracker();
53
54
  return response;
@@ -56,7 +56,7 @@ export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges
56
56
  return splitsPromise
57
57
  .then(resp => resp.json())
58
58
  .then(data => {
59
- // Using flag spec version 1.2
59
+ // Using flag spec version 1.2 or below
60
60
  if (data.splits) {
61
61
  return {
62
62
  ff: {
@@ -6,3 +6,4 @@ export const EXCEPTION = 'exception';
6
6
  export const SPLIT_ARCHIVED = 'archived';
7
7
  export const NOT_IN_SPLIT = 'not in split';
8
8
  export const UNSUPPORTED_MATCHER_TYPE = 'targeting rule type unsupported by sdk';
9
+ export const PREREQUISITES_NOT_MET = 'prerequisites not met';
@@ -879,7 +879,7 @@ declare namespace SplitIO {
879
879
  /**
880
880
  * The list of treatments available for the feature flag.
881
881
  */
882
- treatments: Array<string>;
882
+ treatments: string[];
883
883
  /**
884
884
  * Current change number of the feature flag.
885
885
  */
@@ -903,6 +903,10 @@ declare namespace SplitIO {
903
903
  * Whether the feature flag has impressions tracking disabled or not.
904
904
  */
905
905
  impressionsDisabled: boolean;
906
+ /**
907
+ * Prerequisites for the feature flag.
908
+ */
909
+ prerequisites: Array<{ flagName: string, treatments: string[] }>;
906
910
  };
907
911
  /**
908
912
  * A promise that resolves to a feature flag view or null if the feature flag is not found.