@splitsoftware/splitio-commons 1.2.1-rc.8 → 1.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 +17 -0
- package/cjs/{utils/consent.js → consent/index.js} +1 -1
- package/cjs/consent/sdkUserConsent.js +58 -0
- package/cjs/listeners/browser.js +1 -1
- package/cjs/logger/constants.js +3 -2
- package/cjs/logger/messages/info.js +1 -0
- package/cjs/readiness/sdkReadinessManager.js +5 -3
- package/cjs/sdkClient/client.js +1 -1
- package/cjs/sdkClient/clientCS.js +1 -1
- package/cjs/sdkClient/sdkClient.js +3 -3
- package/cjs/sdkClient/sdkClientMethodCS.js +3 -9
- package/cjs/sdkClient/sdkClientMethodCSWithTT.js +3 -13
- package/cjs/sdkFactory/index.js +3 -2
- package/cjs/services/splitHttpClient.js +12 -11
- package/cjs/storages/inRedis/RedisAdapter.js +9 -2
- package/cjs/sync/syncManagerOnline.js +1 -1
- package/cjs/trackers/impressionsTracker.js +2 -2
- package/cjs/utils/inputValidation/attributes.js +1 -1
- package/cjs/utils/lang/index.js +4 -2
- package/cjs/utils/lang/maps.js +16 -2
- package/cjs/utils/settingsValidation/index.js +21 -3
- package/esm/{utils/consent.js → consent/index.js} +1 -1
- package/esm/consent/sdkUserConsent.js +54 -0
- package/esm/listeners/browser.js +1 -1
- package/esm/logger/constants.js +1 -0
- package/esm/logger/messages/info.js +1 -0
- package/esm/readiness/sdkReadinessManager.js +5 -3
- package/esm/sdkClient/client.js +1 -1
- package/esm/sdkClient/clientCS.js +1 -1
- package/esm/sdkClient/sdkClient.js +3 -3
- package/esm/sdkClient/sdkClientMethodCS.js +3 -9
- package/esm/sdkClient/sdkClientMethodCSWithTT.js +3 -13
- package/esm/sdkFactory/index.js +3 -2
- package/esm/services/splitHttpClient.js +12 -11
- package/esm/storages/inRedis/RedisAdapter.js +9 -2
- package/esm/sync/syncManagerOnline.js +1 -1
- package/esm/trackers/impressionsTracker.js +2 -2
- package/esm/utils/inputValidation/attributes.js +1 -1
- package/esm/utils/lang/index.js +4 -2
- package/esm/utils/lang/maps.js +14 -1
- package/esm/utils/settingsValidation/index.js +21 -3
- package/package.json +14 -2
- package/src/{utils/consent.ts → consent/index.ts} +1 -1
- package/src/consent/sdkUserConsent.ts +58 -0
- package/src/evaluator/parser/index.ts +1 -1
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +2 -2
- package/src/evaluator/value/sanitize.ts +2 -2
- package/src/integrations/pluggable.ts +2 -2
- package/src/listeners/browser.ts +1 -1
- package/src/logger/constants.ts +1 -0
- package/src/logger/messages/info.ts +1 -0
- package/src/readiness/sdkReadinessManager.ts +7 -5
- package/src/sdkClient/client.ts +3 -4
- package/src/sdkClient/clientCS.ts +1 -1
- package/src/sdkClient/sdkClient.ts +4 -4
- package/src/sdkClient/sdkClientMethod.ts +2 -2
- package/src/sdkClient/sdkClientMethodCS.ts +5 -11
- package/src/sdkClient/sdkClientMethodCSWithTT.ts +6 -16
- package/src/sdkFactory/index.ts +3 -2
- package/src/sdkFactory/types.ts +13 -3
- package/src/services/splitHttpClient.ts +12 -10
- package/src/storages/inRedis/RedisAdapter.ts +8 -2
- package/src/sync/syncManagerOnline.ts +1 -1
- package/src/trackers/impressionsTracker.ts +4 -4
- package/src/types.ts +3 -3
- package/src/utils/inputValidation/attributes.ts +1 -2
- package/src/utils/lang/index.ts +7 -3
- package/src/utils/lang/maps.ts +15 -1
- package/src/utils/settingsValidation/index.ts +21 -3
- package/src/utils/settingsValidation/types.ts +5 -1
- package/types/consent/index.d.ts +2 -0
- package/types/consent/sdkUserConsent.d.ts +13 -0
- package/types/evaluator/types.d.ts +2 -2
- package/types/evaluator/value/index.d.ts +1 -1
- package/types/evaluator/value/sanitize.d.ts +1 -1
- package/types/logger/constants.d.ts +1 -0
- package/types/sdkClient/client.d.ts +2 -2
- package/types/sdkClient/sdkClient.d.ts +2 -2
- package/types/sdkClient/sdkClientMethod.d.ts +2 -2
- package/types/sdkClient/sdkClientMethodCS.d.ts +2 -2
- package/types/sdkClient/sdkClientMethodCSWithTT.d.ts +2 -2
- package/types/sdkFactory/types.d.ts +12 -3
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +8 -10
- package/types/storages/inRedis/RedisAdapter.d.ts +1 -1
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +21 -0
- package/types/storages/pluggable/TelemetryCachePluggable.d.ts +19 -1
- package/types/sync/submitters/telemetrySyncTask.d.ts +16 -6
- package/types/types.d.ts +3 -3
- package/types/utils/lang/index.d.ts +2 -1
- package/types/utils/lang/maps.d.ts +7 -0
- package/types/utils/settingsValidation/types.d.ts +5 -1
- package/cjs/sdkClient/types.js +0 -2
- package/cjs/sdkFactory/userConsentProps.js +0 -37
- package/esm/sdkClient/types.js +0 -1
- package/esm/sdkFactory/userConsentProps.js +0 -33
- package/src/sdkClient/types.ts +0 -21
- package/src/sdkFactory/userConsentProps.ts +0 -40
|
@@ -5,8 +5,8 @@ import { clientInputValidationDecorator } from './clientInputValidation';
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates an Sdk client, i.e., a base client with status and destroy interface
|
|
7
7
|
*/
|
|
8
|
-
export function sdkClientFactory(params) {
|
|
9
|
-
var sdkReadinessManager = params.sdkReadinessManager, syncManager = params.syncManager, storage = params.storage, signalListener = params.signalListener, settings = params.settings
|
|
8
|
+
export function sdkClientFactory(params, isSharedClient) {
|
|
9
|
+
var sdkReadinessManager = params.sdkReadinessManager, syncManager = params.syncManager, storage = params.storage, signalListener = params.signalListener, settings = params.settings;
|
|
10
10
|
return objectAssign(
|
|
11
11
|
// Proto-linkage of the readiness Event Emitter
|
|
12
12
|
Object.create(sdkReadinessManager.sdkStatus),
|
|
@@ -23,7 +23,7 @@ export function sdkClientFactory(params) {
|
|
|
23
23
|
sdkReadinessManager.readinessManager.destroy();
|
|
24
24
|
signalListener && signalListener.stop();
|
|
25
25
|
// Release the API Key if it is the main client
|
|
26
|
-
if (!
|
|
26
|
+
if (!isSharedClient)
|
|
27
27
|
releaseApiKey(settings.core.authorizationKey);
|
|
28
28
|
// Cleanup storage
|
|
29
29
|
return storage.destroy();
|
|
@@ -16,12 +16,7 @@ var method = 'Client instantiation';
|
|
|
16
16
|
*/
|
|
17
17
|
export function sdkClientMethodCSFactory(params) {
|
|
18
18
|
var storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, key = _a.core.key, readyTimeout = _a.startup.readyTimeout, log = _a.log;
|
|
19
|
-
|
|
20
|
-
// `false` value is used as binded key of the default client, but trafficType is ignored
|
|
21
|
-
// @TODO handle as a non-recoverable error
|
|
22
|
-
var validKey = validateKey(log, key, method);
|
|
23
|
-
var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), // @ts-ignore
|
|
24
|
-
validKey);
|
|
19
|
+
var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), key);
|
|
25
20
|
var parsedDefaultKey = keyParser(key);
|
|
26
21
|
var defaultInstanceId = buildInstanceId(parsedDefaultKey);
|
|
27
22
|
// Cache instances created per factory.
|
|
@@ -59,9 +54,8 @@ export function sdkClientMethodCSFactory(params) {
|
|
|
59
54
|
sdkReadinessManager: sharedSdkReadiness_1,
|
|
60
55
|
storage: sharedStorage || storage,
|
|
61
56
|
syncManager: sharedSyncManager,
|
|
62
|
-
signalListener: undefined,
|
|
63
|
-
|
|
64
|
-
})), validKey);
|
|
57
|
+
signalListener: undefined, // only the main client "destroy" method stops the signal listener
|
|
58
|
+
}), true), validKey);
|
|
65
59
|
sharedSyncManager && sharedSyncManager.start();
|
|
66
60
|
log.info(NEW_SHARED_CLIENT);
|
|
67
61
|
}
|
|
@@ -18,16 +18,7 @@ var method = 'Client instantiation';
|
|
|
18
18
|
*/
|
|
19
19
|
export function sdkClientMethodCSFactory(params) {
|
|
20
20
|
var storage = params.storage, syncManager = params.syncManager, sdkReadinessManager = params.sdkReadinessManager, _a = params.settings, _b = _a.core, key = _b.key, trafficType = _b.trafficType, readyTimeout = _a.startup.readyTimeout, log = _a.log;
|
|
21
|
-
|
|
22
|
-
// `false` value is used as binded key/TT of the default client, which leads to several issues.
|
|
23
|
-
// @TODO update when supporting non-recoverable errors
|
|
24
|
-
var validKey = validateKey(log, key, method);
|
|
25
|
-
var validTrafficType;
|
|
26
|
-
if (trafficType !== undefined) {
|
|
27
|
-
validTrafficType = validateTrafficType(log, trafficType, method);
|
|
28
|
-
}
|
|
29
|
-
var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), // @ts-ignore
|
|
30
|
-
validKey, validTrafficType);
|
|
21
|
+
var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), key, trafficType);
|
|
31
22
|
var parsedDefaultKey = keyParser(key);
|
|
32
23
|
var defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
|
|
33
24
|
// Cache instances created per factory.
|
|
@@ -72,9 +63,8 @@ export function sdkClientMethodCSFactory(params) {
|
|
|
72
63
|
sdkReadinessManager: sharedSdkReadiness_1,
|
|
73
64
|
storage: sharedStorage || storage,
|
|
74
65
|
syncManager: sharedSyncManager,
|
|
75
|
-
signalListener: undefined,
|
|
76
|
-
|
|
77
|
-
})), validKey, validTrafficType);
|
|
66
|
+
signalListener: undefined, // only the main client "destroy" method stops the signal listener
|
|
67
|
+
}), true), validKey, validTrafficType);
|
|
78
68
|
sharedSyncManager && sharedSyncManager.start();
|
|
79
69
|
log.info(NEW_SHARED_CLIENT);
|
|
80
70
|
}
|
package/esm/sdkFactory/index.js
CHANGED
|
@@ -61,7 +61,8 @@ export function sdkFactory(params) {
|
|
|
61
61
|
// signal listener
|
|
62
62
|
var signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi);
|
|
63
63
|
// Sdk client and manager
|
|
64
|
-
var
|
|
64
|
+
var ctx = { eventTracker: eventTracker, impressionsTracker: impressionsTracker, sdkReadinessManager: sdkReadinessManager, settings: settings, storage: storage, syncManager: syncManager, signalListener: signalListener };
|
|
65
|
+
var clientMethod = sdkClientMethodFactory(ctx);
|
|
65
66
|
var managerInstance = sdkManagerFactory(log, storage.splits, sdkReadinessManager);
|
|
66
67
|
syncManager && syncManager.start();
|
|
67
68
|
signalListener && signalListener.start();
|
|
@@ -78,5 +79,5 @@ export function sdkFactory(params) {
|
|
|
78
79
|
// Logger wrapper API
|
|
79
80
|
Logger: createLoggerAPI(settings.log),
|
|
80
81
|
settings: settings,
|
|
81
|
-
}, extraProps && extraProps(
|
|
82
|
+
}, extraProps && extraProps(ctx));
|
|
82
83
|
}
|
|
@@ -9,25 +9,26 @@ var messageNoFetch = 'Global fetch API is not available.';
|
|
|
9
9
|
* @param fetch optional http client to use instead of the global Fetch (for environments where Fetch API is not available such as Node)
|
|
10
10
|
*/
|
|
11
11
|
export function splitHttpClientFactory(settings, getFetch, getOptions) {
|
|
12
|
-
var log = settings.log
|
|
12
|
+
var log = settings.log;
|
|
13
13
|
var options = getOptions && getOptions();
|
|
14
14
|
var fetch = getFetch && getFetch();
|
|
15
15
|
// if fetch is not available, log Error
|
|
16
16
|
if (!fetch)
|
|
17
17
|
log.error(ERROR_CLIENT_CANNOT_GET_READY, [messageNoFetch]);
|
|
18
|
-
var headers = {
|
|
19
|
-
'Accept': 'application/json',
|
|
20
|
-
'Content-Type': 'application/json',
|
|
21
|
-
'Authorization': "Bearer " + authorizationKey,
|
|
22
|
-
'SplitSDKVersion': version
|
|
23
|
-
};
|
|
24
|
-
if (ip)
|
|
25
|
-
headers['SplitSDKMachineIP'] = ip;
|
|
26
|
-
if (hostname)
|
|
27
|
-
headers['SplitSDKMachineName'] = hostname;
|
|
28
18
|
return function httpClient(url, reqOpts, logErrorsAsInfo) {
|
|
29
19
|
if (reqOpts === void 0) { reqOpts = {}; }
|
|
30
20
|
if (logErrorsAsInfo === void 0) { logErrorsAsInfo = false; }
|
|
21
|
+
var authorizationKey = settings.core.authorizationKey, version = settings.version, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
|
|
22
|
+
var headers = {
|
|
23
|
+
'Accept': 'application/json',
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
'Authorization': "Bearer " + authorizationKey,
|
|
26
|
+
'SplitSDKVersion': version
|
|
27
|
+
};
|
|
28
|
+
if (ip)
|
|
29
|
+
headers['SplitSDKMachineIP'] = ip;
|
|
30
|
+
if (hostname)
|
|
31
|
+
headers['SplitSDKMachineName'] = hostname;
|
|
31
32
|
var request = objectAssign({
|
|
32
33
|
headers: reqOpts.headers ? objectAssign({}, headers, reqOpts.headers) : headers,
|
|
33
34
|
method: reqOpts.method || 'GET',
|
|
@@ -143,13 +143,19 @@ var RedisAdapter = /** @class */ (function (_super) {
|
|
|
143
143
|
else { // If it IS the string URL, that'll be the first param for ioredis.
|
|
144
144
|
result.unshift(options.url);
|
|
145
145
|
}
|
|
146
|
+
if (options.connectionTimeout) {
|
|
147
|
+
merge(opts, { connectTimeout: options.connectionTimeout });
|
|
148
|
+
}
|
|
149
|
+
if (options.tls) {
|
|
150
|
+
merge(opts, { tls: options.tls });
|
|
151
|
+
}
|
|
146
152
|
return result;
|
|
147
153
|
};
|
|
148
154
|
/**
|
|
149
155
|
* Parses the options into what we care about.
|
|
150
156
|
*/
|
|
151
157
|
RedisAdapter._defineOptions = function (_a) {
|
|
152
|
-
var connectionTimeout = _a.connectionTimeout, operationTimeout = _a.operationTimeout, url = _a.url, host = _a.host, port = _a.port, db = _a.db, pass = _a.pass;
|
|
158
|
+
var connectionTimeout = _a.connectionTimeout, operationTimeout = _a.operationTimeout, url = _a.url, host = _a.host, port = _a.port, db = _a.db, pass = _a.pass, tls = _a.tls;
|
|
153
159
|
var parsedOptions = {
|
|
154
160
|
connectionTimeout: connectionTimeout,
|
|
155
161
|
operationTimeout: operationTimeout,
|
|
@@ -157,7 +163,8 @@ var RedisAdapter = /** @class */ (function (_super) {
|
|
|
157
163
|
host: host,
|
|
158
164
|
port: port,
|
|
159
165
|
db: db,
|
|
160
|
-
pass: pass
|
|
166
|
+
pass: pass,
|
|
167
|
+
tls: tls
|
|
161
168
|
};
|
|
162
169
|
return merge({}, DEFAULT_OPTIONS, parsedOptions);
|
|
163
170
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { submitterManagerFactory } from './submitters/submitterManager';
|
|
2
2
|
import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants';
|
|
3
3
|
import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants';
|
|
4
|
-
import { isConsentGranted } from '../
|
|
4
|
+
import { isConsentGranted } from '../consent';
|
|
5
5
|
/**
|
|
6
6
|
* Online SyncManager factory.
|
|
7
7
|
* Can be used for server-side API, and client-side API with or without multiple clients.
|
|
@@ -18,7 +18,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, integratio
|
|
|
18
18
|
observer,
|
|
19
19
|
// if countsCache is provided, it implies `isOptimized` flag (i.e., if impressions should be deduped or not)
|
|
20
20
|
countsCache) {
|
|
21
|
-
var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname
|
|
21
|
+
var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
|
|
22
22
|
return {
|
|
23
23
|
track: function (impressions, attributes) {
|
|
24
24
|
if (settings.userConsent === CONSENT_DECLINED)
|
|
@@ -59,7 +59,7 @@ countsCache) {
|
|
|
59
59
|
attributes: attributes,
|
|
60
60
|
ip: ip,
|
|
61
61
|
hostname: hostname,
|
|
62
|
-
sdkLanguageVersion: version
|
|
62
|
+
sdkLanguageVersion: settings.version
|
|
63
63
|
};
|
|
64
64
|
// Wrap in a timeout because we don't want it to be blocking.
|
|
65
65
|
setTimeout(function () {
|
|
@@ -3,7 +3,7 @@ import { validateAttribute } from './attribute';
|
|
|
3
3
|
import { ERROR_NOT_PLAIN_OBJECT } from '../../logger/constants';
|
|
4
4
|
export function validateAttributes(log, maybeAttrs, method) {
|
|
5
5
|
// Attributes are optional
|
|
6
|
-
if (
|
|
6
|
+
if (maybeAttrs == undefined || isObject(maybeAttrs)) // eslint-disable-line eqeqeq
|
|
7
7
|
return maybeAttrs;
|
|
8
8
|
log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'attributes']);
|
|
9
9
|
return false;
|
package/esm/utils/lang/index.js
CHANGED
|
@@ -141,10 +141,12 @@ export function isNaNNumber(val) {
|
|
|
141
141
|
return val !== val;
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
|
-
* Validates if a value is an object
|
|
144
|
+
* Validates if a value is an object created by the Object constructor (plain object).
|
|
145
|
+
* It checks `constructor.name` to avoid false negatives when validating values on a separate VM context, which has its own global built-ins.
|
|
145
146
|
*/
|
|
146
147
|
export function isObject(obj) {
|
|
147
|
-
return obj !== null && typeof obj === 'object' && obj.constructor === Object
|
|
148
|
+
return obj !== null && typeof obj === 'object' && (obj.constructor === Object ||
|
|
149
|
+
(obj.constructor != null && obj.constructor.name === 'Object'));
|
|
148
150
|
}
|
|
149
151
|
/**
|
|
150
152
|
* Checks if a given value is a string.
|
package/esm/utils/lang/maps.js
CHANGED
|
@@ -68,4 +68,17 @@ var MapPoly = /** @class */ (function () {
|
|
|
68
68
|
return MapPoly;
|
|
69
69
|
}());
|
|
70
70
|
export { MapPoly };
|
|
71
|
-
|
|
71
|
+
/**
|
|
72
|
+
* return the Map constructor to use. If native Map is not available or it doesn't support the required features (e.g., IE11),
|
|
73
|
+
* a ponyfill with minimal features is returned instead.
|
|
74
|
+
*
|
|
75
|
+
* Exported for testing purposes only.
|
|
76
|
+
*/
|
|
77
|
+
export function __getMapConstructor() {
|
|
78
|
+
// eslint-disable-next-line compat/compat
|
|
79
|
+
if (typeof Array.from === 'function' && typeof Map === 'function' && Map.prototype && Map.prototype.values) {
|
|
80
|
+
return Map;
|
|
81
|
+
}
|
|
82
|
+
return MapPoly;
|
|
83
|
+
}
|
|
84
|
+
export var _Map = __getMapConstructor();
|
|
@@ -3,6 +3,8 @@ import { mode } from './mode';
|
|
|
3
3
|
import { validateSplitFilters } from './splitFilters';
|
|
4
4
|
import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE } from '../constants';
|
|
5
5
|
import { validImpressionsMode } from './impressionsMode';
|
|
6
|
+
import { validateKey } from '../inputValidation/key';
|
|
7
|
+
import { validateTrafficType } from '../inputValidation/trafficType';
|
|
6
8
|
var base = {
|
|
7
9
|
// Define which kind of object you want to retrieve from SplitFactory
|
|
8
10
|
mode: STANDALONE_MODE,
|
|
@@ -106,9 +108,25 @@ export function settingsValidation(config, validationParams) {
|
|
|
106
108
|
// @ts-ignore, modify readonly prop
|
|
107
109
|
if (storage)
|
|
108
110
|
withDefaults.storage = storage(withDefaults);
|
|
109
|
-
//
|
|
110
|
-
if (
|
|
111
|
-
withDefaults.core.key
|
|
111
|
+
// Validate key and TT (for client-side)
|
|
112
|
+
if (validationParams.acceptKey) {
|
|
113
|
+
var maybeKey = withDefaults.core.key;
|
|
114
|
+
// Although `key` is required in client-side, it can be omitted in LOCALHOST mode. In that case, the value `localhost_key` is used.
|
|
115
|
+
if (withDefaults.mode === LOCALHOST_MODE && maybeKey === undefined) {
|
|
116
|
+
withDefaults.core.key = 'localhost_key';
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Keeping same behaviour than JS SDK: if settings key or TT are invalid,
|
|
120
|
+
// `false` value is used as binded key/TT of the default client, which leads to some issues.
|
|
121
|
+
// @ts-ignore, @TODO handle invalid keys as a non-recoverable error?
|
|
122
|
+
withDefaults.core.key = validateKey(log, maybeKey, 'Client instantiation');
|
|
123
|
+
}
|
|
124
|
+
if (validationParams.acceptTT) {
|
|
125
|
+
var maybeTT = withDefaults.core.trafficType;
|
|
126
|
+
if (maybeTT !== undefined) { // @ts-ignore
|
|
127
|
+
withDefaults.core.trafficType = validateTrafficType(log, maybeTT, 'Client instantiation');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
112
130
|
}
|
|
113
131
|
// Current ip/hostname information
|
|
114
132
|
// @ts-ignore, modify readonly prop
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1-rc.0",
|
|
4
4
|
"description": "Split Javascript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -46,6 +46,18 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"tslib": "^2.3.1"
|
|
48
48
|
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"js-yaml": "^3.13.1",
|
|
51
|
+
"ioredis": "^4.28.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"js-yaml": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"ioredis": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
49
61
|
"devDependencies": {
|
|
50
62
|
"@types/google.analytics": "0.0.40",
|
|
51
63
|
"@types/ioredis": "^4.28.0",
|
|
@@ -62,7 +74,7 @@
|
|
|
62
74
|
"ioredis": "^4.28.0",
|
|
63
75
|
"jest": "^27.2.3",
|
|
64
76
|
"jest-localstorage-mock": "^2.4.3",
|
|
65
|
-
"js-yaml": "^3.
|
|
77
|
+
"js-yaml": "^3.13.1",
|
|
66
78
|
"lodash": "^4.17.21",
|
|
67
79
|
"node-fetch": "^2.6.7",
|
|
68
80
|
"redis-server": "1.2.2",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ERROR_NOT_BOOLEAN, USER_CONSENT_UPDATED, USER_CONSENT_NOT_UPDATED, USER_CONSENT_INITIAL } from '../logger/constants';
|
|
2
|
+
import { isConsentGranted } from './index';
|
|
3
|
+
import { CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../utils/constants';
|
|
4
|
+
import { isBoolean } from '../utils/lang';
|
|
5
|
+
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
6
|
+
|
|
7
|
+
// User consent enum
|
|
8
|
+
const ConsentStatus = {
|
|
9
|
+
GRANTED: CONSENT_GRANTED,
|
|
10
|
+
DECLINED: CONSENT_DECLINED,
|
|
11
|
+
UNKNOWN: CONSENT_UNKNOWN,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The public user consent API exposed via SplitFactory, used to control if the SDK tracks and sends impressions and events or not.
|
|
16
|
+
*/
|
|
17
|
+
export function createUserConsentAPI(params: ISdkFactoryContext) {
|
|
18
|
+
const { settings, settings: { log }, syncManager, storage: { events, impressions, impressionCounts } } = params;
|
|
19
|
+
|
|
20
|
+
if (!isConsentGranted(settings)) log.info(USER_CONSENT_INITIAL, [settings.userConsent]);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
setStatus(consent: unknown) {
|
|
24
|
+
// validate input param
|
|
25
|
+
if (!isBoolean(consent)) {
|
|
26
|
+
log.warn(ERROR_NOT_BOOLEAN, ['setUserConsent']);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const newConsentStatus = consent ? CONSENT_GRANTED : CONSENT_DECLINED;
|
|
31
|
+
|
|
32
|
+
if (settings.userConsent !== newConsentStatus) {
|
|
33
|
+
log.info(USER_CONSENT_UPDATED, [settings.userConsent, newConsentStatus]); // @ts-ignore, modify readonly prop
|
|
34
|
+
settings.userConsent = newConsentStatus;
|
|
35
|
+
|
|
36
|
+
if (consent) { // resumes submitters if transitioning to GRANTED
|
|
37
|
+
syncManager?.submitter?.start();
|
|
38
|
+
} else { // pauses submitters and drops tracked data if transitioning to DECLINED
|
|
39
|
+
syncManager?.submitter?.stop();
|
|
40
|
+
// @ts-ignore, clear method is present in storage for standalone and partial consumer mode
|
|
41
|
+
if (events.clear) events.clear(); // @ts-ignore
|
|
42
|
+
if (impressions.clear) impressions.clear();
|
|
43
|
+
if (impressionCounts) impressionCounts.clear();
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
log.info(USER_CONSENT_NOT_UPDATED, [newConsentStatus]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
getStatus() {
|
|
53
|
+
return settings.userConsent;
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
Status: ConsentStatus
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -31,7 +31,7 @@ export function parser(log: ILogger, conditions: ISplitCondition[], storage: ISt
|
|
|
31
31
|
const matcher = matcherFactory(log, matcherDto, storage);
|
|
32
32
|
|
|
33
33
|
// Evaluator function.
|
|
34
|
-
return (key: string, attributes: SplitIO.Attributes, splitEvaluator: ISplitEvaluator) => {
|
|
34
|
+
return (key: string, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator) => {
|
|
35
35
|
const value = sanitizeValue(log, key, matcherDto, attributes);
|
|
36
36
|
const result = value !== undefined && matcher ? matcher(value, splitEvaluator) : false;
|
|
37
37
|
|
package/src/evaluator/types.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ILogger } from '../logger/types';
|
|
|
6
6
|
|
|
7
7
|
export interface IDependencyMatcherValue {
|
|
8
8
|
key: SplitIO.SplitKey,
|
|
9
|
-
attributes
|
|
9
|
+
attributes?: SplitIO.Attributes
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface IMatcherDto {
|
|
@@ -27,7 +27,7 @@ export interface IEvaluation {
|
|
|
27
27
|
|
|
28
28
|
export type IEvaluationResult = IEvaluation & { treatment: string }
|
|
29
29
|
|
|
30
|
-
export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
|
|
30
|
+
export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
|
|
31
31
|
|
|
32
32
|
export type IEvaluator = (key: SplitIO.SplitKey, seed: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable<IEvaluation | undefined>
|
|
33
33
|
|
|
@@ -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: string, attributeName: string | null, attributes
|
|
7
|
+
function parseValue(log: ILogger, key: string, attributeName: string | null, attributes?: SplitIO.Attributes) {
|
|
8
8
|
let value = undefined;
|
|
9
9
|
if (attributeName) {
|
|
10
10
|
if (attributes) {
|
|
@@ -23,7 +23,7 @@ function parseValue(log: ILogger, key: string, attributeName: string | null, att
|
|
|
23
23
|
/**
|
|
24
24
|
* Defines value to be matched (key / attribute).
|
|
25
25
|
*/
|
|
26
|
-
export function sanitizeValue(log: ILogger, key: string, matcherDto: IMatcherDto, attributes
|
|
26
|
+
export function sanitizeValue(log: ILogger, key: string, matcherDto: IMatcherDto, attributes?: SplitIO.Attributes) {
|
|
27
27
|
const attributeName = matcherDto.attribute;
|
|
28
28
|
const valueToMatch = parseValue(log, key, attributeName, attributes);
|
|
29
29
|
const sanitizedValue = sanitize(log, matcherDto.type, valueToMatch, matcherDto.dataType, attributes);
|
|
@@ -41,7 +41,7 @@ function sanitizeBoolean(val: any): boolean | undefined {
|
|
|
41
41
|
return undefined;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function dependencyProcessor(sanitizedValue: string, attributes
|
|
44
|
+
function dependencyProcessor(sanitizedValue: string, attributes?: SplitIO.Attributes): IDependencyMatcherValue {
|
|
45
45
|
return {
|
|
46
46
|
key: sanitizedValue,
|
|
47
47
|
attributes
|
|
@@ -69,7 +69,7 @@ function getProcessingFunction(matcherTypeID: number, dataType: string) {
|
|
|
69
69
|
/**
|
|
70
70
|
* Sanitize matcher value
|
|
71
71
|
*/
|
|
72
|
-
export function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array<string | number> | undefined, dataType: string, attributes
|
|
72
|
+
export function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array<string | number> | undefined, dataType: string, attributes?: SplitIO.Attributes) {
|
|
73
73
|
const processor = getProcessingFunction(matcherTypeID, dataType);
|
|
74
74
|
let sanitizedValue: string | number | boolean | Array<string | number> | IDependencyMatcherValue | undefined;
|
|
75
75
|
|
|
@@ -29,10 +29,10 @@ export function pluggableIntegrationsManagerFactory(
|
|
|
29
29
|
|
|
30
30
|
// Exception safe methods: each integration module is responsable for handling errors
|
|
31
31
|
return {
|
|
32
|
-
handleImpression
|
|
32
|
+
handleImpression(impressionData: SplitIO.ImpressionData) {
|
|
33
33
|
listeners.forEach(listener => listener.queue({ type: SPLIT_IMPRESSION, payload: impressionData }));
|
|
34
34
|
},
|
|
35
|
-
handleEvent
|
|
35
|
+
handleEvent(eventData: SplitIO.EventData) {
|
|
36
36
|
listeners.forEach(listener => listener.queue({ type: SPLIT_EVENT, payload: eventData }));
|
|
37
37
|
}
|
|
38
38
|
};
|
package/src/listeners/browser.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { OPTIMIZED, DEBUG } from '../utils/constants';
|
|
|
11
11
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
12
12
|
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
|
|
13
13
|
import { ISyncManager } from '../sync/types';
|
|
14
|
-
import { isConsentGranted } from '../
|
|
14
|
+
import { isConsentGranted } from '../consent';
|
|
15
15
|
|
|
16
16
|
// 'unload' event is used instead of 'beforeunload', since 'unload' is not a cancelable event, so no other listeners can stop the event from occurring.
|
|
17
17
|
const UNLOAD_DOM_EVENT = 'unload';
|
package/src/logger/constants.ts
CHANGED
|
@@ -70,6 +70,7 @@ export const EVENTS_TRACKER_SUCCESS = 120;
|
|
|
70
70
|
export const IMPRESSIONS_TRACKER_SUCCESS = 121;
|
|
71
71
|
export const USER_CONSENT_UPDATED = 122;
|
|
72
72
|
export const USER_CONSENT_NOT_UPDATED = 123;
|
|
73
|
+
export const USER_CONSENT_INITIAL = 124;
|
|
73
74
|
|
|
74
75
|
export const ENGINE_VALUE_INVALID = 200;
|
|
75
76
|
export const ENGINE_VALUE_NO_ATTRIBUTES = 201;
|
|
@@ -16,6 +16,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
16
16
|
[c.IMPRESSIONS_TRACKER_SUCCESS, c.LOG_PREFIX_IMPRESSIONS_TRACKER + 'Successfully stored %s impression(s).'],
|
|
17
17
|
[c.USER_CONSENT_UPDATED, 'setUserConsent: consent status changed from %s to %s.'],
|
|
18
18
|
[c.USER_CONSENT_NOT_UPDATED, 'setUserConsent: call had no effect because it was the current consent status (%s).'],
|
|
19
|
+
[c.USER_CONSENT_INITIAL, 'Starting the SDK with %s user consent. No data will be sent.'],
|
|
19
20
|
|
|
20
21
|
// synchronizer
|
|
21
22
|
[c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
|
|
@@ -51,7 +51,7 @@ export function sdkReadinessManagerFactory(
|
|
|
51
51
|
|
|
52
52
|
// default onRejected handler, that just logs the error, if ready promise doesn't have one.
|
|
53
53
|
function defaultOnRejected(err: any) {
|
|
54
|
-
log.error(err);
|
|
54
|
+
log.error(err && err.message);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function generateReadyPromise() {
|
|
@@ -62,7 +62,9 @@ export function sdkReadinessManagerFactory(
|
|
|
62
62
|
if (readyCbCount === internalReadyCbCount && !promise.hasOnFulfilled()) log.warn(CLIENT_NO_LISTENER);
|
|
63
63
|
resolve();
|
|
64
64
|
});
|
|
65
|
-
readinessManager.gate.once(SDK_READY_TIMED_OUT,
|
|
65
|
+
readinessManager.gate.once(SDK_READY_TIMED_OUT, (message: string) => {
|
|
66
|
+
reject(new Error(message));
|
|
67
|
+
});
|
|
66
68
|
}), defaultOnRejected);
|
|
67
69
|
|
|
68
70
|
return promise;
|
|
@@ -106,10 +108,10 @@ export function sdkReadinessManagerFactory(
|
|
|
106
108
|
* @function ready
|
|
107
109
|
* @returns {Promise<void>}
|
|
108
110
|
*/
|
|
109
|
-
ready
|
|
111
|
+
ready() {
|
|
110
112
|
if (readinessManager.hasTimedout()) {
|
|
111
113
|
if (!readinessManager.isReady()) {
|
|
112
|
-
return promiseWrapper(Promise.reject('Split SDK has emitted SDK_READY_TIMED_OUT event.'), defaultOnRejected);
|
|
114
|
+
return promiseWrapper(Promise.reject(new Error('Split SDK has emitted SDK_READY_TIMED_OUT event.')), defaultOnRejected);
|
|
113
115
|
} else {
|
|
114
116
|
return Promise.resolve();
|
|
115
117
|
}
|
|
@@ -118,7 +120,7 @@ export function sdkReadinessManagerFactory(
|
|
|
118
120
|
},
|
|
119
121
|
|
|
120
122
|
// Expose status for internal purposes only. Not considered part of the public API, and might be updated eventually.
|
|
121
|
-
__getStatus
|
|
123
|
+
__getStatus() {
|
|
122
124
|
return {
|
|
123
125
|
isReady: readinessManager.isReady(),
|
|
124
126
|
isReadyFromCache: readinessManager.isReadyFromCache(),
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -5,17 +5,16 @@ import { validateSplitExistance } from '../utils/inputValidation/splitExistance'
|
|
|
5
5
|
import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance';
|
|
6
6
|
import { SDK_NOT_READY } from '../utils/labels';
|
|
7
7
|
import { CONTROL } from '../utils/constants';
|
|
8
|
-
import { IClientFactoryParams } from './types';
|
|
9
8
|
import { IEvaluationResult } from '../evaluator/types';
|
|
10
9
|
import { SplitIO, ImpressionDTO } from '../types';
|
|
11
10
|
import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
|
|
12
|
-
|
|
11
|
+
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Creator of base client with getTreatments and track methods.
|
|
16
15
|
*/
|
|
17
16
|
// @TODO missing time tracking to collect telemetry
|
|
18
|
-
export function clientFactory(params:
|
|
17
|
+
export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient {
|
|
19
18
|
const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker } = params;
|
|
20
19
|
const { log, mode } = settings;
|
|
21
20
|
|
|
@@ -125,6 +124,6 @@ export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | S
|
|
|
125
124
|
getTreatments,
|
|
126
125
|
getTreatmentsWithConfig,
|
|
127
126
|
track,
|
|
128
|
-
|
|
127
|
+
isClientSide: false
|
|
129
128
|
} as SplitIO.IClient | SplitIO.IAsyncClient;
|
|
130
129
|
}
|
|
@@ -25,6 +25,6 @@ export function clientCSDecorator(log: ILogger, client: SplitIO.IClient, key: Sp
|
|
|
25
25
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
26
26
|
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
isClientSide: true
|
|
29
29
|
}) as SplitIO.ICsClient;
|
|
30
30
|
}
|
|
@@ -3,13 +3,13 @@ import { IStatusInterface, SplitIO } from '../types';
|
|
|
3
3
|
import { releaseApiKey } from '../utils/inputValidation/apiKey';
|
|
4
4
|
import { clientFactory } from './client';
|
|
5
5
|
import { clientInputValidationDecorator } from './clientInputValidation';
|
|
6
|
-
import {
|
|
6
|
+
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates an Sdk client, i.e., a base client with status and destroy interface
|
|
10
10
|
*/
|
|
11
|
-
export function sdkClientFactory(params:
|
|
12
|
-
const { sdkReadinessManager, syncManager, storage, signalListener, settings
|
|
11
|
+
export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: boolean): SplitIO.IClient | SplitIO.IAsyncClient {
|
|
12
|
+
const { sdkReadinessManager, syncManager, storage, signalListener, settings } = params;
|
|
13
13
|
|
|
14
14
|
return objectAssign(
|
|
15
15
|
// Proto-linkage of the readiness Event Emitter
|
|
@@ -35,7 +35,7 @@ export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClie
|
|
|
35
35
|
signalListener && signalListener.stop();
|
|
36
36
|
|
|
37
37
|
// Release the API Key if it is the main client
|
|
38
|
-
if (!
|
|
38
|
+
if (!isSharedClient) releaseApiKey(settings.core.authorizationKey);
|
|
39
39
|
|
|
40
40
|
// Cleanup storage
|
|
41
41
|
return storage.destroy();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ISdkClientFactoryParams } from './types';
|
|
2
1
|
import { SplitIO } from '../types';
|
|
3
2
|
import { sdkClientFactory } from './sdkClient';
|
|
4
3
|
import { RETRIEVE_CLIENT_DEFAULT } from '../logger/constants';
|
|
4
|
+
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Factory of client method for server-side SDKs (ISDK and IAsyncSDK)
|
|
8
8
|
*/
|
|
9
|
-
export function sdkClientMethodFactory(params:
|
|
9
|
+
export function sdkClientMethodFactory(params: ISdkFactoryContext): () => SplitIO.IClient | SplitIO.IAsyncClient {
|
|
10
10
|
const log = params.settings.log;
|
|
11
11
|
const clientInstance = sdkClientFactory(params);
|
|
12
12
|
|