@splitsoftware/splitio-commons 2.11.1-rc.2 → 2.12.1
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 +6 -2
- package/cjs/evaluator/condition/engineUtils.js +2 -2
- package/cjs/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +3 -3
- package/cjs/evaluator/fallbackTreatmentsCalculator/index.js +21 -32
- package/cjs/evaluator/matchers/whitelist.js +1 -1
- package/cjs/integrations/pluggable.js +1 -1
- package/cjs/listeners/node.js +2 -2
- package/cjs/logger/constants.js +3 -4
- package/cjs/logger/index.js +1 -1
- package/cjs/logger/messages/debug.js +1 -1
- package/cjs/logger/messages/error.js +1 -1
- package/cjs/logger/messages/info.js +2 -3
- package/cjs/logger/messages/warn.js +4 -4
- package/cjs/sdkClient/client.js +2 -3
- package/cjs/sdkClient/clientAttributesDecoration.js +3 -3
- package/cjs/sdkClient/clientInputValidation.js +36 -68
- package/cjs/sdkClient/identity.js +1 -1
- package/cjs/sdkClient/sdkClient.js +19 -7
- package/cjs/sdkFactory/index.js +8 -41
- package/cjs/services/splitApi.js +13 -13
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/KeyBuilder.js +12 -12
- package/cjs/storages/KeyBuilderCS.js +13 -13
- package/cjs/storages/KeyBuilderSS.js +15 -15
- package/cjs/storages/getRolloutPlan.js +1 -1
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +1 -1
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -1
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +1 -1
- package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +5 -5
- package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/RedisAdapter.js +8 -8
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +6 -6
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +7 -7
- package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +3 -3
- package/cjs/storages/pluggable/EventsCachePluggable.js +1 -1
- package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +6 -6
- package/cjs/storages/pluggable/SplitsCachePluggable.js +4 -4
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +7 -7
- package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +2 -2
- package/cjs/storages/pluggable/index.js +3 -3
- package/cjs/storages/pluggable/wrapperAdapter.js +1 -1
- package/cjs/storages/setRolloutPlan.js +3 -3
- package/cjs/storages/utils.js +6 -6
- package/cjs/sync/offline/splitsParser/parseCondition.js +1 -1
- package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
- package/cjs/sync/polling/pollingManagerSS.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +6 -6
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +12 -10
- package/cjs/sync/streaming/SSEClient/index.js +2 -2
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +2 -2
- package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -2
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +2 -2
- package/cjs/sync/streaming/parseUtils.js +1 -1
- package/cjs/sync/submitters/submitter.js +1 -1
- package/cjs/trackers/eventTracker.js +10 -12
- package/cjs/trackers/impressionObserver/buildKey.js +1 -1
- package/cjs/trackers/impressionsTracker.js +37 -20
- package/cjs/trackers/uniqueKeysTracker.js +1 -1
- package/cjs/utils/inputValidation/apiKey.js +1 -1
- package/cjs/utils/inputValidation/attribute.js +2 -2
- package/cjs/utils/inputValidation/eventProperties.js +1 -1
- package/cjs/utils/inputValidation/isOperational.js +1 -1
- package/cjs/utils/lang/index.js +1 -1
- package/cjs/utils/lang/sets.js +4 -4
- package/cjs/utils/promise/timeout.js +1 -1
- package/cjs/utils/settingsValidation/splitFilters.js +2 -2
- package/cjs/utils/settingsValidation/url.js +5 -5
- package/esm/evaluator/condition/engineUtils.js +2 -2
- package/esm/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +3 -3
- package/esm/evaluator/fallbackTreatmentsCalculator/index.js +21 -33
- package/esm/evaluator/matchers/whitelist.js +1 -1
- package/esm/integrations/pluggable.js +1 -1
- package/esm/listeners/node.js +2 -2
- package/esm/logger/constants.js +0 -1
- package/esm/logger/index.js +1 -1
- package/esm/logger/messages/info.js +1 -2
- package/esm/logger/messages/warn.js +3 -3
- package/esm/sdkClient/client.js +3 -4
- package/esm/sdkClient/clientAttributesDecoration.js +3 -3
- package/esm/sdkClient/clientInputValidation.js +36 -68
- package/esm/sdkClient/identity.js +1 -1
- package/esm/sdkClient/sdkClient.js +20 -8
- package/esm/sdkFactory/index.js +8 -41
- package/esm/services/splitApi.js +13 -13
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/KeyBuilder.js +12 -12
- package/esm/storages/KeyBuilderCS.js +12 -12
- package/esm/storages/KeyBuilderSS.js +14 -14
- package/esm/storages/getRolloutPlan.js +1 -1
- package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
- package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +4 -4
- package/esm/storages/inRedis/RedisAdapter.js +7 -7
- package/esm/storages/inRedis/SplitsCacheInRedis.js +4 -4
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +7 -7
- package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +1 -1
- package/esm/storages/pluggable/EventsCachePluggable.js +1 -1
- package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +5 -5
- package/esm/storages/pluggable/SplitsCachePluggable.js +2 -2
- package/esm/storages/pluggable/TelemetryCachePluggable.js +7 -7
- package/esm/storages/pluggable/UniqueKeysCachePluggable.js +1 -1
- package/esm/storages/pluggable/index.js +2 -2
- package/esm/storages/pluggable/wrapperAdapter.js +1 -1
- package/esm/storages/setRolloutPlan.js +3 -3
- package/esm/storages/utils.js +6 -6
- package/esm/sync/offline/splitsParser/parseCondition.js +1 -1
- package/esm/sync/polling/pollingManagerSS.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +6 -6
- package/esm/sync/polling/updaters/splitChangesUpdater.js +12 -10
- package/esm/sync/streaming/SSEClient/index.js +2 -2
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +2 -2
- package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -2
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +2 -2
- package/esm/sync/streaming/parseUtils.js +1 -1
- package/esm/sync/submitters/submitter.js +1 -1
- package/esm/trackers/eventTracker.js +10 -12
- package/esm/trackers/impressionObserver/buildKey.js +1 -1
- package/esm/trackers/impressionsTracker.js +38 -21
- package/esm/trackers/uniqueKeysTracker.js +1 -1
- package/esm/utils/inputValidation/apiKey.js +1 -1
- package/esm/utils/inputValidation/attribute.js +2 -2
- package/esm/utils/inputValidation/isOperational.js +1 -1
- package/esm/utils/lang/sets.js +4 -4
- package/esm/utils/promise/timeout.js +1 -1
- package/esm/utils/settingsValidation/splitFilters.js +2 -2
- package/esm/utils/settingsValidation/url.js +5 -5
- package/package.json +5 -5
- package/src/dtos/types.ts +15 -15
- package/src/evaluator/condition/engineUtils.ts +2 -2
- package/src/evaluator/fallbackTreatmentsCalculator/index.ts +23 -41
- package/src/evaluator/matchers/whitelist.ts +2 -2
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +1 -1
- package/src/integrations/pluggable.ts +1 -1
- package/src/logger/constants.ts +0 -1
- package/src/logger/messages/info.ts +1 -2
- package/src/logger/messages/warn.ts +3 -3
- package/src/sdkClient/client.ts +3 -5
- package/src/sdkClient/clientInputValidation.ts +38 -61
- package/src/sdkClient/sdkClient.ts +20 -5
- package/src/sdkFactory/index.ts +10 -47
- package/src/sdkFactory/types.ts +6 -3
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +1 -1
- package/src/storages/inMemory/SplitsCacheInMemory.ts +1 -1
- package/src/storages/inRedis/SplitsCacheInRedis.ts +1 -1
- package/src/storages/pluggable/SplitsCachePluggable.ts +1 -1
- package/src/storages/setRolloutPlan.ts +2 -2
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/polling/updaters/splitChangesUpdater.ts +11 -9
- package/src/trackers/eventTracker.ts +7 -10
- package/src/trackers/impressionsTracker.ts +42 -26
- package/src/trackers/types.ts +3 -1
- package/src/utils/lang/sets.ts +4 -2
- package/types/splitio.d.ts +22 -18
|
@@ -37,7 +37,7 @@ export function SplitsUpdateWorker(log, storage, splitsSyncTask, splitsEventEmit
|
|
|
37
37
|
segmentsSyncTask.execute(true);
|
|
38
38
|
var attempts = backoff.attempts + 1;
|
|
39
39
|
if (ff.isSync() && rbs.isSync()) {
|
|
40
|
-
log.debug("Refresh completed"
|
|
40
|
+
log.debug("Refresh completed".concat(cdnBypass ? ' bypassing the CDN' : '', " in ").concat(attempts, " attempts."));
|
|
41
41
|
isHandlingEvent = false;
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
@@ -46,7 +46,7 @@ export function SplitsUpdateWorker(log, storage, splitsSyncTask, splitsEventEmit
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
if (cdnBypass) {
|
|
49
|
-
log.debug("No changes fetched after "
|
|
49
|
+
log.debug("No changes fetched after ".concat(attempts, " attempts with CDN bypassed."));
|
|
50
50
|
isHandlingEvent = false;
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
@@ -27,7 +27,7 @@ function decompress(data, compression) {
|
|
|
27
27
|
return algorithms.gunzipSync(binData);
|
|
28
28
|
if (compression === ZLIB)
|
|
29
29
|
return algorithms.unzlibSync(binData);
|
|
30
|
-
throw new Error("Invalid compression algorithm #"
|
|
30
|
+
throw new Error("Invalid compression algorithm #".concat(compression));
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Decode, decompress and parse the provided 'data' into a KeyList object
|
|
@@ -13,7 +13,7 @@ export function submitterFactory(log, postClient, sourceCache, postRate, fromCac
|
|
|
13
13
|
if (sourceCache.isEmpty() && !data)
|
|
14
14
|
return Promise.resolve();
|
|
15
15
|
data = sourceCache.pop(data);
|
|
16
|
-
var dataCountMessage = typeof data.length === 'number' ? data.length
|
|
16
|
+
var dataCountMessage = typeof data.length === 'number' ? "".concat(data.length, " ").concat(dataName) : dataName;
|
|
17
17
|
log[debugLogs ? 'debug' : 'info'](SUBMITTERS_PUSH, [dataCountMessage]);
|
|
18
18
|
var jsonPayload = JSON.stringify(fromCacheToPayload ? fromCacheToPayload(data) : data);
|
|
19
19
|
if (!maxRetries)
|
|
@@ -9,26 +9,24 @@ import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
|
9
9
|
* @param eventsCache - cache to save events
|
|
10
10
|
* @param integrationsManager - optional event handler used for integrations
|
|
11
11
|
*/
|
|
12
|
-
export function eventTrackerFactory(settings, eventsCache,
|
|
12
|
+
export function eventTrackerFactory(settings, eventsCache, integrationsManager, telemetryCache) {
|
|
13
13
|
var log = settings.log, mode = settings.mode;
|
|
14
14
|
var isAsync = isConsumerMode(mode);
|
|
15
15
|
function queueEventsCallback(eventData, tracked) {
|
|
16
16
|
var eventTypeId = eventData.eventTypeId, trafficTypeName = eventData.trafficTypeName, key = eventData.key, value = eventData.value, timestamp = eventData.timestamp, properties = eventData.properties;
|
|
17
17
|
// Logging every prop would be too much.
|
|
18
|
-
var msg = "event of type \""
|
|
18
|
+
var msg = "event of type \"".concat(eventTypeId, "\" for traffic type \"").concat(trafficTypeName, "\". Key: ").concat(key, ". Value: ").concat(value, ". Timestamp: ").concat(timestamp, ". ").concat(properties ? 'With properties.' : 'With no properties.');
|
|
19
19
|
if (tracked) {
|
|
20
20
|
log.info(EVENTS_TRACKER_SUCCESS, [msg]);
|
|
21
21
|
if (integrationsManager) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
integrationsManager.handleEvent(eventDataCopy);
|
|
31
|
-
});
|
|
22
|
+
// Wrap in a timeout because we don't want it to be blocking.
|
|
23
|
+
setTimeout(function () {
|
|
24
|
+
// copy of event, to avoid unexpected behavior if modified by integrations
|
|
25
|
+
var eventDataCopy = objectAssign({}, eventData);
|
|
26
|
+
if (properties)
|
|
27
|
+
eventDataCopy.properties = objectAssign({}, properties);
|
|
28
|
+
// integrationsManager does not throw errors (they are internally handled by each integration module)
|
|
29
|
+
integrationsManager.handleEvent(eventDataCopy);
|
|
32
30
|
});
|
|
33
31
|
}
|
|
34
32
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export function buildKey(impression) {
|
|
2
|
-
return impression.keyName
|
|
2
|
+
return "".concat(impression.keyName, ":").concat(impression.feature, ":").concat(impression.treatment, ":").concat(impression.label, ":").concat(impression.changeNumber);
|
|
3
3
|
}
|
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIONS_LISTENER } from '../logger/constants';
|
|
4
|
-
import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
|
|
4
|
+
import { CONSENT_DECLINED, DEBUG, DEDUPED, OPTIMIZED, QUEUED } from '../utils/constants';
|
|
5
|
+
import { strategyDebugFactory } from './strategy/strategyDebug';
|
|
6
|
+
import { strategyNoneFactory } from './strategy/strategyNone';
|
|
7
|
+
import { strategyOptimizedFactory } from './strategy/strategyOptimized';
|
|
8
|
+
import { uniqueKeysTrackerFactory } from './uniqueKeysTracker';
|
|
5
9
|
/**
|
|
6
10
|
* Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
|
|
7
11
|
*/
|
|
8
|
-
export function impressionsTrackerFactory(
|
|
9
|
-
var
|
|
12
|
+
export function impressionsTrackerFactory(params, storage, integrationsManager) {
|
|
13
|
+
var settings = params.settings, impressionsObserverFactory = params.impressionsObserverFactory, filterAdapterFactory = params.filterAdapterFactory;
|
|
14
|
+
var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version, impressionsMode = settings.sync.impressionsMode;
|
|
15
|
+
var observer = impressionsObserverFactory();
|
|
16
|
+
var uniqueKeysTracker = uniqueKeysTrackerFactory(log, storage.uniqueKeys, filterAdapterFactory && filterAdapterFactory());
|
|
17
|
+
var noneStrategy = strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker);
|
|
18
|
+
var strategy = impressionsMode === OPTIMIZED ?
|
|
19
|
+
strategyOptimizedFactory(observer, storage.impressionCounts) :
|
|
20
|
+
impressionsMode === DEBUG ?
|
|
21
|
+
strategyDebugFactory(observer) :
|
|
22
|
+
noneStrategy;
|
|
10
23
|
return {
|
|
24
|
+
start: function () {
|
|
25
|
+
uniqueKeysTracker.start();
|
|
26
|
+
},
|
|
27
|
+
stop: function () {
|
|
28
|
+
uniqueKeysTracker.stop();
|
|
29
|
+
},
|
|
11
30
|
track: function (impressions, attributes) {
|
|
12
31
|
if (settings.userConsent === CONSENT_DECLINED)
|
|
13
32
|
return;
|
|
@@ -20,7 +39,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
|
|
|
20
39
|
var impressionsLength = impressions.length;
|
|
21
40
|
var impressionsToStoreLength = impressionsToStore.length;
|
|
22
41
|
if (impressionsToStoreLength) {
|
|
23
|
-
var res =
|
|
42
|
+
var res = storage.impressions.track(impressionsToStore.map(function (item) { return item.imp; }));
|
|
24
43
|
// If we're on an async storage, handle error and log it.
|
|
25
44
|
if (thenable(res)) {
|
|
26
45
|
res.then(function () {
|
|
@@ -32,9 +51,9 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
|
|
|
32
51
|
else {
|
|
33
52
|
// Record when impressionsCache is sync only (standalone mode)
|
|
34
53
|
// @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
if (storage.telemetry) {
|
|
55
|
+
storage.telemetry.recordImpressionStats(QUEUED, impressionsToStoreLength);
|
|
56
|
+
storage.telemetry.recordImpressionStats(DEDUPED, impressionsLength - impressionsToStoreLength);
|
|
38
57
|
}
|
|
39
58
|
}
|
|
40
59
|
}
|
|
@@ -49,20 +68,18 @@ export function impressionsTrackerFactory(settings, impressionsCache, noneStrate
|
|
|
49
68
|
hostname: hostname,
|
|
50
69
|
sdkLanguageVersion: version
|
|
51
70
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
});
|
|
71
|
+
// Wrap in a timeout because we don't want it to be blocking.
|
|
72
|
+
setTimeout(function () {
|
|
73
|
+
// integrationsManager.handleImpression does not throw errors
|
|
74
|
+
if (integrationsManager)
|
|
75
|
+
integrationsManager.handleImpression(impressionData);
|
|
76
|
+
try { // @ts-ignore. An exception on the listeners should not break the SDK.
|
|
77
|
+
if (impressionListener)
|
|
78
|
+
impressionListener.logImpression(impressionData);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
log.error(ERROR_IMPRESSIONS_LISTENER, [err]);
|
|
82
|
+
}
|
|
66
83
|
});
|
|
67
84
|
};
|
|
68
85
|
for (var i = 0; i < impressionsLength; i++) {
|
|
@@ -19,7 +19,7 @@ export function uniqueKeysTrackerFactory(log, uniqueKeysCache, filterAdapter) {
|
|
|
19
19
|
return {
|
|
20
20
|
track: function (key, featureName) {
|
|
21
21
|
if (!filterAdapter.add(key, featureName)) {
|
|
22
|
-
log.debug(LOG_PREFIX_UNIQUE_KEYS_TRACKER
|
|
22
|
+
log.debug("".concat(LOG_PREFIX_UNIQUE_KEYS_TRACKER, "The feature ").concat(featureName, " and key ").concat(key, " exist in the filter"));
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
uniqueKeysCache.track(key, featureName);
|
|
@@ -34,7 +34,7 @@ export function validateAndTrackApiKey(log, maybeSdkKey) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
|
-
log.warn(WARN_SDK_KEY, [usedKeysMap[sdkKey]
|
|
37
|
+
log.warn(WARN_SDK_KEY, ["".concat(usedKeysMap[sdkKey], " factory/ies with this SDK Key")]);
|
|
38
38
|
usedKeysMap[sdkKey]++;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isString, isFiniteNumber, isBoolean } from '../../utils/lang';
|
|
2
2
|
export function validateAttribute(log, attributeKey, attributeValue, method) {
|
|
3
3
|
if (!isString(attributeKey) || attributeKey.length === 0) {
|
|
4
|
-
log.warn(method
|
|
4
|
+
log.warn("".concat(method, ": you passed an invalid attribute name, attribute name must be a non-empty string."));
|
|
5
5
|
return false;
|
|
6
6
|
}
|
|
7
7
|
var isStringVal = isString(attributeValue);
|
|
@@ -9,7 +9,7 @@ export function validateAttribute(log, attributeKey, attributeValue, method) {
|
|
|
9
9
|
var isBoolVal = isBoolean(attributeValue);
|
|
10
10
|
var isArrayVal = Array.isArray(attributeValue);
|
|
11
11
|
if (!(isStringVal || isFiniteVal || isBoolVal || isArrayVal)) { // If it's not of valid type.
|
|
12
|
-
log.warn(method
|
|
12
|
+
log.warn("".concat(method, ": you passed an invalid attribute value for ").concat(attributeKey, ". Acceptable types are: string, number, boolean and array of strings."));
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
15
|
return true;
|
|
@@ -8,7 +8,7 @@ export function validateIfNotDestroyed(log, readinessManager, method) {
|
|
|
8
8
|
export function validateIfReadyFromCache(log, readinessManager, method, featureFlagNameOrNames) {
|
|
9
9
|
if (readinessManager.isReadyFromCache())
|
|
10
10
|
return true;
|
|
11
|
-
log.warn(CLIENT_NOT_READY_FROM_CACHE, [method, featureFlagNameOrNames ? " for feature flag "
|
|
11
|
+
log.warn(CLIENT_NOT_READY_FROM_CACHE, [method, featureFlagNameOrNames ? " for feature flag ".concat(featureFlagNameOrNames.toString()) : '']);
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
14
|
// Operational means that the SDK is ready to evaluate (not destroyed and ready from cache)
|
package/esm/utils/lang/sets.js
CHANGED
|
@@ -10,8 +10,8 @@ export function setToArray(set) {
|
|
|
10
10
|
export function returnSetsUnion(set, set2) {
|
|
11
11
|
return new Set(setToArray(set).concat(setToArray(set2)));
|
|
12
12
|
}
|
|
13
|
-
export function returnDifference(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return
|
|
13
|
+
export function returnDifference(list1, list2) {
|
|
14
|
+
list1 = list1 || [];
|
|
15
|
+
list2 = list2 || [];
|
|
16
|
+
return list1.filter(function (item) { return list2.indexOf(item) === -1; });
|
|
17
17
|
}
|
|
@@ -3,7 +3,7 @@ export function timeout(ms, promise) {
|
|
|
3
3
|
return promise;
|
|
4
4
|
return new Promise(function (resolve, reject) {
|
|
5
5
|
var tid = setTimeout(function () {
|
|
6
|
-
reject(new Error("Operation timed out because it exceeded the configured time limit of "
|
|
6
|
+
reject(new Error("Operation timed out because it exceeded the configured time limit of ".concat(ms, " ms.")));
|
|
7
7
|
}, ms);
|
|
8
8
|
promise.then(function (res) {
|
|
9
9
|
clearTimeout(tid);
|
|
@@ -42,14 +42,14 @@ function validateFilterType(maybeFilterType) {
|
|
|
42
42
|
*/
|
|
43
43
|
function validateSplitFilter(log, type, values, maxLength) {
|
|
44
44
|
// validate and remove invalid and duplicated values
|
|
45
|
-
var result = validateSplits(log, values, LOG_PREFIX_SETTINGS, type
|
|
45
|
+
var result = validateSplits(log, values, LOG_PREFIX_SETTINGS, "".concat(type, " filter"), "".concat(type, " filter value"));
|
|
46
46
|
if (result) {
|
|
47
47
|
if (type === 'bySet') {
|
|
48
48
|
result = sanitizeFlagSets(log, result, LOG_PREFIX_SETTINGS);
|
|
49
49
|
}
|
|
50
50
|
// check max length
|
|
51
51
|
if (result.length > maxLength)
|
|
52
|
-
throw new Error(maxLength
|
|
52
|
+
throw new Error("".concat(maxLength, " unique values can be specified at most for '").concat(type, "' filter. You passed ").concat(result.length, ". Please consider reducing the amount or using other filter."));
|
|
53
53
|
// sort values
|
|
54
54
|
result.sort();
|
|
55
55
|
}
|
|
@@ -12,16 +12,16 @@ var streamingEndpointMatcher = /^\/(sse|event-stream)/;
|
|
|
12
12
|
*/
|
|
13
13
|
export function url(settings, target) {
|
|
14
14
|
if (telemetryEndpointMatcher.test(target)) {
|
|
15
|
-
return ""
|
|
15
|
+
return "".concat(settings.urls.telemetry).concat(target);
|
|
16
16
|
}
|
|
17
17
|
if (eventsEndpointMatcher.test(target)) {
|
|
18
|
-
return ""
|
|
18
|
+
return "".concat(settings.urls.events).concat(target);
|
|
19
19
|
}
|
|
20
20
|
if (authEndpointMatcher.test(target)) {
|
|
21
|
-
return ""
|
|
21
|
+
return "".concat(settings.urls.auth).concat(target);
|
|
22
22
|
}
|
|
23
23
|
if (streamingEndpointMatcher.test(target)) {
|
|
24
|
-
return ""
|
|
24
|
+
return "".concat(settings.urls.streaming).concat(target);
|
|
25
25
|
}
|
|
26
|
-
return ""
|
|
26
|
+
return "".concat(settings.urls.sdk).concat(target);
|
|
27
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.1",
|
|
4
4
|
"description": "Split JavaScript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"@types/ioredis": "^4.28.0",
|
|
61
61
|
"@types/jest": "^27.0.0",
|
|
62
62
|
"@types/lodash": "^4.14.162",
|
|
63
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
64
|
-
"@typescript-eslint/parser": "^
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
64
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
65
65
|
"cross-env": "^7.0.2",
|
|
66
|
-
"eslint": "^8.
|
|
66
|
+
"eslint": "^8.56.0",
|
|
67
67
|
"eslint-plugin-compat": "^6.0.1",
|
|
68
68
|
"eslint-plugin-import": "^2.25.3",
|
|
69
69
|
"eslint-plugin-tsdoc": "^0.3.0",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"redis-server": "1.2.2",
|
|
77
77
|
"rimraf": "^3.0.2",
|
|
78
78
|
"ts-jest": "^27.0.5",
|
|
79
|
-
"typescript": "4.
|
|
79
|
+
"typescript": "4.7.4"
|
|
80
80
|
},
|
|
81
81
|
"sideEffects": false
|
|
82
82
|
}
|
package/src/dtos/types.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface IBetweenStringMatcherData {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export interface IWhitelistMatcherData {
|
|
26
|
-
whitelist
|
|
26
|
+
whitelist?: string[] | null
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export interface IInSegmentMatcherData {
|
|
@@ -41,10 +41,10 @@ export interface IDependencyMatcherData {
|
|
|
41
41
|
|
|
42
42
|
interface ISplitMatcherBase {
|
|
43
43
|
matcherType: string
|
|
44
|
-
negate
|
|
45
|
-
keySelector
|
|
44
|
+
negate?: boolean
|
|
45
|
+
keySelector?: null | {
|
|
46
46
|
trafficType: string,
|
|
47
|
-
attribute
|
|
47
|
+
attribute?: string | null
|
|
48
48
|
}
|
|
49
49
|
userDefinedSegmentMatcherData?: null | IInSegmentMatcherData
|
|
50
50
|
userDefinedLargeSegmentMatcherData?: null | IInLargeSegmentMatcherData
|
|
@@ -207,18 +207,18 @@ export interface IExcludedSegment {
|
|
|
207
207
|
export interface IRBSegment {
|
|
208
208
|
name: string,
|
|
209
209
|
changeNumber: number,
|
|
210
|
-
status
|
|
211
|
-
conditions?: ISplitCondition[],
|
|
210
|
+
status?: 'ACTIVE' | 'ARCHIVED',
|
|
211
|
+
conditions?: ISplitCondition[] | null,
|
|
212
212
|
excluded?: {
|
|
213
213
|
keys?: string[] | null,
|
|
214
214
|
segments?: IExcludedSegment[] | null
|
|
215
|
-
}
|
|
215
|
+
} | null
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
export interface ISplit {
|
|
219
219
|
name: string,
|
|
220
220
|
changeNumber: number,
|
|
221
|
-
status
|
|
221
|
+
status?: 'ACTIVE' | 'ARCHIVED',
|
|
222
222
|
conditions: ISplitCondition[],
|
|
223
223
|
prerequisites?: null | {
|
|
224
224
|
n: string,
|
|
@@ -232,9 +232,9 @@ export interface ISplit {
|
|
|
232
232
|
trafficAllocationSeed?: number
|
|
233
233
|
configurations?: {
|
|
234
234
|
[treatmentName: string]: string
|
|
235
|
-
},
|
|
236
|
-
sets?: string[],
|
|
237
|
-
impressionsDisabled?: boolean
|
|
235
|
+
} | null,
|
|
236
|
+
sets?: string[] | null,
|
|
237
|
+
impressionsDisabled?: boolean | null
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
// Split definition used in offline mode
|
|
@@ -245,13 +245,13 @@ export interface ISplitChangesResponse {
|
|
|
245
245
|
ff?: {
|
|
246
246
|
t: number,
|
|
247
247
|
s?: number,
|
|
248
|
-
d
|
|
249
|
-
},
|
|
248
|
+
d?: ISplit[] | null,
|
|
249
|
+
} | null,
|
|
250
250
|
rbs?: {
|
|
251
251
|
t: number,
|
|
252
252
|
s?: number,
|
|
253
|
-
d
|
|
254
|
-
}
|
|
253
|
+
d?: IRBSegment[] | null,
|
|
254
|
+
} | null,
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
/** Interface of the parsed JSON response of `/segmentChanges/{segmentName}` */
|
|
@@ -18,10 +18,10 @@ export function getTreatment(log: ILogger, key: string, seed: number | undefined
|
|
|
18
18
|
/**
|
|
19
19
|
* Evaluates the traffic allocation to see if we should apply rollout conditions or not.
|
|
20
20
|
*/
|
|
21
|
-
export function shouldApplyRollout(trafficAllocation: number,
|
|
21
|
+
export function shouldApplyRollout(trafficAllocation: number, bucketingKey: string, trafficAllocationSeed: number) {
|
|
22
22
|
// For rollout, if traffic allocation for splits is 100%, we don't need to filter it because everything should evaluate the rollout.
|
|
23
23
|
if (trafficAllocation < 100) {
|
|
24
|
-
const _bucket = bucket(
|
|
24
|
+
const _bucket = bucket(bucketingKey, trafficAllocationSeed);
|
|
25
25
|
|
|
26
26
|
if (_bucket > trafficAllocation) {
|
|
27
27
|
return false;
|
|
@@ -1,50 +1,32 @@
|
|
|
1
|
-
import { FallbackTreatmentConfiguration,
|
|
1
|
+
import { FallbackTreatmentConfiguration, TreatmentWithConfig } from '../../../types/splitio';
|
|
2
2
|
import { CONTROL } from '../../utils/constants';
|
|
3
3
|
import { isString } from '../../utils/lang';
|
|
4
4
|
|
|
5
|
-
export type IFallbackTreatmentsCalculator = {
|
|
6
|
-
resolve(flagName: string, label: string): TreatmentWithConfig & { label: string };
|
|
7
|
-
}
|
|
5
|
+
export type IFallbackTreatmentsCalculator = (flagName: string, label?: string) => TreatmentWithConfig & { label: string };
|
|
8
6
|
|
|
9
7
|
export const FALLBACK_PREFIX = 'fallback - ';
|
|
10
8
|
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
config: null,
|
|
31
|
-
label,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private copyWithLabel(fallback: Treatment | TreatmentWithConfig, label: string): TreatmentWithConfig & { label: string } {
|
|
36
|
-
if (isString(fallback)) {
|
|
37
|
-
return {
|
|
38
|
-
treatment: fallback,
|
|
9
|
+
export function FallbackTreatmentsCalculator(fallbacks: FallbackTreatmentConfiguration = {}): IFallbackTreatmentsCalculator {
|
|
10
|
+
|
|
11
|
+
return (flagName: string, label = '') => {
|
|
12
|
+
const fallback = fallbacks.byFlag?.[flagName] || fallbacks.global;
|
|
13
|
+
|
|
14
|
+
return fallback ?
|
|
15
|
+
isString(fallback) ?
|
|
16
|
+
{
|
|
17
|
+
treatment: fallback,
|
|
18
|
+
config: null,
|
|
19
|
+
label: `${FALLBACK_PREFIX}${label}`,
|
|
20
|
+
} :
|
|
21
|
+
{
|
|
22
|
+
treatment: fallback.treatment,
|
|
23
|
+
config: fallback.config,
|
|
24
|
+
label: `${FALLBACK_PREFIX}${label}`,
|
|
25
|
+
} :
|
|
26
|
+
{
|
|
27
|
+
treatment: CONTROL,
|
|
39
28
|
config: null,
|
|
40
|
-
label
|
|
29
|
+
label,
|
|
41
30
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
treatment: fallback.treatment,
|
|
46
|
-
config: fallback.config,
|
|
47
|
-
label: `${FALLBACK_PREFIX}${label}`,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
31
|
+
};
|
|
50
32
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export function whitelistMatcherContext(ruleAttr
|
|
2
|
-
const whitelistSet = new Set(ruleAttr);
|
|
1
|
+
export function whitelistMatcherContext(ruleAttr?: string[] | null) {
|
|
2
|
+
const whitelistSet = new Set(ruleAttr || []);
|
|
3
3
|
|
|
4
4
|
return function whitelistMatcher(runtimeAttr: string): boolean {
|
|
5
5
|
const isInWhitelist = whitelistSet.has(runtimeAttr);
|
package/src/evaluator/types.ts
CHANGED
|
@@ -13,8 +13,8 @@ export interface IMatcherDto {
|
|
|
13
13
|
name: string
|
|
14
14
|
value?: string | number | boolean | string[] | IDependencyMatcherData | IBetweenMatcherData | IBetweenStringMatcherData | null
|
|
15
15
|
|
|
16
|
-
attribute
|
|
17
|
-
negate
|
|
16
|
+
attribute?: string | null
|
|
17
|
+
negate?: boolean
|
|
18
18
|
dataType: string
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -4,7 +4,7 @@ import { ILogger } from '../../logger/types';
|
|
|
4
4
|
import { sanitize } from './sanitize';
|
|
5
5
|
import { ENGINE_VALUE, ENGINE_VALUE_NO_ATTRIBUTES, ENGINE_VALUE_INVALID } from '../../logger/constants';
|
|
6
6
|
|
|
7
|
-
function parseValue(log: ILogger, key: SplitIO.SplitKey, attributeName
|
|
7
|
+
function parseValue(log: ILogger, key: SplitIO.SplitKey, attributeName?: string | null, attributes?: SplitIO.Attributes) {
|
|
8
8
|
let value = undefined;
|
|
9
9
|
if (attributeName) {
|
|
10
10
|
if (attributes) {
|
|
@@ -21,7 +21,7 @@ export function pluggableIntegrationsManagerFactory(
|
|
|
21
21
|
// No need to check if `settings.integrations` is an array of functions. It was already validated
|
|
22
22
|
integrations.forEach(integrationFactory => {
|
|
23
23
|
let integration = integrationFactory(params);
|
|
24
|
-
if (integration
|
|
24
|
+
if (integration) listeners.push(integration);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
// If `listeners` is empty, not return a integration manager
|
package/src/logger/constants.ts
CHANGED
|
@@ -32,7 +32,6 @@ export const ENGINE_DEFAULT = 41;
|
|
|
32
32
|
|
|
33
33
|
export const CLIENT_READY_FROM_CACHE = 100;
|
|
34
34
|
export const CLIENT_READY = 101;
|
|
35
|
-
export const IMPRESSION = 102;
|
|
36
35
|
export const IMPRESSION_QUEUEING = 103;
|
|
37
36
|
export const NEW_SHARED_CLIENT = 104;
|
|
38
37
|
export const NEW_FACTORY = 105;
|
|
@@ -8,8 +8,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
8
8
|
[c.CLIENT_READY_FROM_CACHE, READY_MSG + ' from cache'],
|
|
9
9
|
[c.CLIENT_READY, READY_MSG],
|
|
10
10
|
// SDK
|
|
11
|
-
[c.
|
|
12
|
-
[c.IMPRESSION_QUEUEING, c.LOG_PREFIX_IMPRESSIONS_TRACKER +'Queueing corresponding impression.'],
|
|
11
|
+
[c.IMPRESSION_QUEUEING, c.LOG_PREFIX_IMPRESSIONS_TRACKER +'Queueing impression. Feature flag: %s. Key: %s. Evaluation: %s. Label: %s'],
|
|
13
12
|
[c.NEW_SHARED_CLIENT, 'New shared client instance created.'],
|
|
14
13
|
[c.NEW_FACTORY, 'New Split SDK instance created. %s'],
|
|
15
14
|
[c.EVENTS_TRACKER_SUCCESS, c.LOG_PREFIX_EVENTS_TRACKER + 'Successfully queued %s'],
|
|
@@ -21,9 +21,9 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
21
21
|
[c.WARN_TRIMMING_PROPERTIES, '%s: more than 300 properties were provided. Some of them will be trimmed when processed.'],
|
|
22
22
|
[c.WARN_CONVERTING, '%s: %s "%s" is not of type string, converting.'],
|
|
23
23
|
[c.WARN_TRIMMING, '%s: %s "%s" has extra whitespace, trimming.'],
|
|
24
|
-
[c.WARN_NOT_EXISTENT_SPLIT, '%s: feature flag "%s" does not exist in this environment. Please double check what feature flags exist in
|
|
24
|
+
[c.WARN_NOT_EXISTENT_SPLIT, '%s: feature flag "%s" does not exist in this environment. Please double check what feature flags exist in Harness FME UI.'],
|
|
25
25
|
[c.WARN_LOWERCASE_TRAFFIC_TYPE, '%s: traffic_type_name should be all lowercase - converting string to lowercase.'],
|
|
26
|
-
[c.WARN_NOT_EXISTENT_TT, '%s: traffic type "%s" does not have any corresponding feature flag in this environment, make sure you\'re tracking your events to a valid traffic type defined in
|
|
26
|
+
[c.WARN_NOT_EXISTENT_TT, '%s: traffic type "%s" does not have any corresponding feature flag in this environment, make sure you\'re tracking your events to a valid traffic type defined in Harness FME UI.'],
|
|
27
27
|
[c.WARN_FLAGSET_NOT_CONFIGURED, '%s: you passed %s which is not part of the configured FlagSetsFilter, ignoring Flag Set.'],
|
|
28
28
|
// initialization / settings validation
|
|
29
29
|
[c.WARN_INTEGRATION_INVALID, c.LOG_PREFIX_SETTINGS + ': %s integration item(s) at settings is invalid. %s'],
|
|
@@ -36,5 +36,5 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
36
36
|
[c.STREAMING_PARSING_SPLIT_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching SplitChanges due to an error processing %s notification: %s'],
|
|
37
37
|
[c.WARN_INVALID_FLAGSET, '%s: you passed %s, flag set must adhere to the regular expressions %s. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. %s was discarded.'],
|
|
38
38
|
[c.WARN_LOWERCASE_FLAGSET, '%s: flag set %s should be all lowercase - converting string to lowercase.'],
|
|
39
|
-
[c.WARN_FLAGSET_WITHOUT_FLAGS, '%s: you passed %s flag set that does not contain cached feature flag names. Please double check what flag sets are in use in
|
|
39
|
+
[c.WARN_FLAGSET_WITHOUT_FLAGS, '%s: you passed %s flag set that does not contain cached feature flag names. Please double check what flag sets are in use in Harness FME UI.'],
|
|
40
40
|
]);
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { SDK_NOT_READY } from '../utils/labels';
|
|
|
7
7
|
import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK, TREATMENTS_WITH_CONFIG_BY_FLAGSETS, TREATMENTS_BY_FLAGSETS, TREATMENTS_BY_FLAGSET, TREATMENTS_WITH_CONFIG_BY_FLAGSET, GET_TREATMENTS_WITH_CONFIG, GET_TREATMENTS_BY_FLAG_SETS, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, GET_TREATMENTS_BY_FLAG_SET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, GET_TREATMENT_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, TRACK_FN_LABEL } from '../utils/constants';
|
|
8
8
|
import { IEvaluationResult } from '../evaluator/types';
|
|
9
9
|
import SplitIO from '../../types/splitio';
|
|
10
|
-
import {
|
|
10
|
+
import { IMPRESSION_QUEUEING } from '../logger/constants';
|
|
11
11
|
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
12
12
|
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
13
13
|
import { Method } from '../sync/submitters/types';
|
|
@@ -147,16 +147,14 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
147
147
|
let { treatment, label, config = null } = evaluation;
|
|
148
148
|
|
|
149
149
|
if (treatment === CONTROL) {
|
|
150
|
-
const fallbackTreatment = fallbackTreatmentsCalculator
|
|
150
|
+
const fallbackTreatment = fallbackTreatmentsCalculator(featureFlagName, label);
|
|
151
151
|
treatment = fallbackTreatment.treatment;
|
|
152
152
|
label = fallbackTreatment.label;
|
|
153
153
|
config = fallbackTreatment.config;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
|
|
157
|
-
|
|
158
156
|
if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
|
|
159
|
-
log.info(IMPRESSION_QUEUEING);
|
|
157
|
+
log.info(IMPRESSION_QUEUEING, [featureFlagName, matchingKey, treatment, label]);
|
|
160
158
|
queue.push({
|
|
161
159
|
imp: {
|
|
162
160
|
feature: featureFlagName,
|