@kameleoon/javascript-sdk-core 5.16.1 → 5.17.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/CHANGELOG.md +10 -1
- package/dist/clientConfiguration/clientConfiguration.d.ts +4 -1
- package/dist/clientConfiguration/constants.d.ts +2 -3
- package/dist/clientConfiguration/types.d.ts +5 -0
- package/dist/javascript-sdk-core-browser.cjs.js +189 -89
- package/dist/javascript-sdk-core-browser.cjs.js.map +1 -1
- package/dist/javascript-sdk-core-browser.es.js +189 -89
- package/dist/javascript-sdk-core-browser.es.js.map +1 -1
- package/dist/javascript-sdk-core.cjs.js +189 -89
- package/dist/javascript-sdk-core.cjs.js.map +1 -1
- package/dist/javascript-sdk-core.es.js +189 -89
- package/dist/javascript-sdk-core.es.js.map +1 -1
- package/dist/kameleoonClient.d.ts +2 -0
- package/dist/kameleoonData/conversion.d.ts +10 -0
- package/dist/kameleoonData/customData.d.ts +5 -0
- package/dist/kameleoonData/dataManager.d.ts +2 -2
- package/dist/kameleoonData/dataProcessor.d.ts +2 -2
- package/dist/kameleoonData/types.d.ts +5 -3
- package/dist/requester/constants.d.ts +1 -1
- package/dist/requester/urlProvider.d.ts +1 -0
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/types.d.ts +13 -5
- package/dist/variationConfiguration/types.d.ts +2 -0
- package/dist/variationConfiguration/variationConfiguration.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 5.17.0 (2025-12-17)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Fixed an issue where **[Kameleoon Data](https://developers.kameleoon.com/feature-management-and-experimentation/web-sdks/js-sdk/#data-types)** was retained beyond the configured **[`targetingDataCleanupInterval`](https://developers.kameleoon.com/feature-management-and-experimentation/web-sdks/js-sdk/#configuration-parameters)** if the **[Data API](https://developers.kameleoon.com/apis/data-api-rest/all-endpoints/post-visit-events/)** encountered internal errors. Expired data is now reliably removed in accordance with the cleanup interval.
|
|
8
|
+
- Updated evaluation and tracking logic to comply with GDPR requirements when consent is not given:
|
|
9
|
+
- If behavior is **partially blocked**, the default variation will be returned.
|
|
10
|
+
- If behavior is **completely blocked**, an exception will be thrown.
|
|
11
|
+
|
|
3
12
|
## 5.16.1 (2025-10-23)
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -8,7 +17,7 @@
|
|
|
8
17
|
|
|
9
18
|
## 5.16.0 (2025-10-22)
|
|
10
19
|
|
|
11
|
-
###
|
|
20
|
+
### Features
|
|
12
21
|
|
|
13
22
|
- Introduced a new [`getDataFile`](getDataFile) method. This method returns the current SDK configuration (also known as the **data file**) used for evaluation and targeting. It is **not** intended for production use to fetch variations for every feature flag in the returned list, as it is not optimized for performance. For that purpose, use [`getVariations`](getVariations) instead. `getDataFile` is mainly useful for debugging or QA, for example to let internal users manually select a variant for a specific feature flag in production.
|
|
14
23
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Result } from 'ts-res';
|
|
2
2
|
import { KameleoonError } from '../kameleoonError/kameleoonError';
|
|
3
3
|
import { SegmentType } from '../targeting';
|
|
4
|
-
import { ClientConfigurationParametersType, ConfigurationType, ExperimentInfoType, ExperimentType, FeatureFlagType, MappedRuleType } from './types';
|
|
4
|
+
import { ClientConfigurationParametersType, ConfigurationType, ConsentBlockingBehaviour, ExperimentInfoType, ExperimentType, FeatureFlagType, MappedRuleType } from './types';
|
|
5
5
|
import { MEGroup } from '../clientConfiguration/meGroup';
|
|
6
6
|
interface IClientConfiguration {
|
|
7
7
|
initialize: () => Promise<Result<void, KameleoonError>>;
|
|
@@ -38,6 +38,7 @@ export declare class ClientConfiguration implements IClientConfiguration {
|
|
|
38
38
|
private externalPackageInfo;
|
|
39
39
|
private usedDefaultDataFile;
|
|
40
40
|
private defaultDataFile?;
|
|
41
|
+
private blockingBehaviourMode;
|
|
41
42
|
private readonly CACHE_REVALIDATE_PERIOD;
|
|
42
43
|
constructor({ updateInterval, urlProvider, storage, requester, dataManager, eventSource, externalVisitorCodeManager, eventManager, externalPackageInfo, defaultDataFile, }: ClientConfigurationParametersType);
|
|
43
44
|
initialize(): Promise<Result<void, KameleoonError>>;
|
|
@@ -52,6 +53,7 @@ export declare class ClientConfiguration implements IClientConfiguration {
|
|
|
52
53
|
get holdout(): ExperimentType | null;
|
|
53
54
|
get meGroups(): Map<string, MEGroup>;
|
|
54
55
|
get isConsentRequired(): boolean;
|
|
56
|
+
get consentBlockingBehaviour(): ConsentBlockingBehaviour;
|
|
55
57
|
get hasAnyTargetedDeliveryRule(): boolean;
|
|
56
58
|
private checkShouldUpdate;
|
|
57
59
|
private readStorageData;
|
|
@@ -71,5 +73,6 @@ export declare class ClientConfiguration implements IClientConfiguration {
|
|
|
71
73
|
private makeMEGroups;
|
|
72
74
|
private updateStorageData;
|
|
73
75
|
private updateConsentRequired;
|
|
76
|
+
private consentBlockingBehaviourFromStr;
|
|
74
77
|
}
|
|
75
78
|
export {};
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const DEFAULT_CLIENT_CONFIGURATION: ConfigurationDataType;
|
|
1
|
+
import { ConfigurationDataType } from '../clientConfiguration';
|
|
2
|
+
export declare const DEFAULT_DATA_FILE_CONFIGURATION: ConfigurationDataType;
|
|
@@ -50,6 +50,10 @@ export declare enum ConsentType {
|
|
|
50
50
|
Required = "REQUIRED",
|
|
51
51
|
NotRequired = "NOT_REQUIRED"
|
|
52
52
|
}
|
|
53
|
+
export declare enum ConsentBlockingBehaviour {
|
|
54
|
+
PartiallyBlocked = "PARTIALLY_BLOCK",
|
|
55
|
+
CompletelyBlocked = "FULLY_BLOCK"
|
|
56
|
+
}
|
|
53
57
|
export type DeviationType = {
|
|
54
58
|
variationId: string;
|
|
55
59
|
value: number;
|
|
@@ -129,6 +133,7 @@ export type ConfigurationType = {
|
|
|
129
133
|
realTimeUpdate: boolean;
|
|
130
134
|
consentType: ConsentType;
|
|
131
135
|
dataApiDomain: string;
|
|
136
|
+
consentOptOutBehavior: string;
|
|
132
137
|
};
|
|
133
138
|
export type ClientConfigurationParametersType = {
|
|
134
139
|
urlProvider: IUrlProvider;
|
|
@@ -249,10 +249,17 @@ class KameleoonError extends Error {
|
|
|
249
249
|
this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
|
|
250
250
|
break;
|
|
251
251
|
case exports.KameleoonException.FeatureFlagVariationNotFound:
|
|
252
|
-
case exports.KameleoonException.FeatureFlagEnvironmentDisabled:
|
|
253
252
|
case exports.KameleoonException.FeatureFlagVariableNotFound:
|
|
254
253
|
this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
|
|
255
254
|
break;
|
|
255
|
+
case exports.KameleoonException.FeatureFlagEnvironmentDisabled:
|
|
256
|
+
if (thirdParam !== undefined) {
|
|
257
|
+
this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
this.message = secondParam;
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
256
263
|
case exports.KameleoonException.StorageWrite:
|
|
257
264
|
case exports.KameleoonException.JSONParse:
|
|
258
265
|
this.message = ERROR_MESSAGES[type](secondParam);
|
|
@@ -308,7 +315,7 @@ exports.RequestType = void 0;
|
|
|
308
315
|
RequestType["RemoteData"] = "remoteData";
|
|
309
316
|
})(exports.RequestType || (exports.RequestType = {}));
|
|
310
317
|
|
|
311
|
-
const NUMBER_OF_RETRIES =
|
|
318
|
+
const NUMBER_OF_RETRIES = 1;
|
|
312
319
|
exports.Header = void 0;
|
|
313
320
|
(function (Header) {
|
|
314
321
|
Header["UserAgent"] = "User-Agent";
|
|
@@ -887,6 +894,9 @@ class UrlProvider {
|
|
|
887
894
|
const currentDomain = this.domains[UrlType.DataApi];
|
|
888
895
|
this.domains[UrlType.DataApi] = currentDomain.replace(/^[^.]+/, subDomain);
|
|
889
896
|
}
|
|
897
|
+
get dataApiDomain() {
|
|
898
|
+
return this.domains[UrlType.DataApi];
|
|
899
|
+
}
|
|
890
900
|
getClientConfigurationUrl(timeStamp) {
|
|
891
901
|
this.isInitialized();
|
|
892
902
|
const baseUrl = `https://${this.domains[UrlType.ClientConfiguration]}/v3/`;
|
|
@@ -1146,6 +1156,11 @@ var ConsentType;
|
|
|
1146
1156
|
ConsentType["Required"] = "REQUIRED";
|
|
1147
1157
|
ConsentType["NotRequired"] = "NOT_REQUIRED";
|
|
1148
1158
|
})(ConsentType || (ConsentType = {}));
|
|
1159
|
+
var ConsentBlockingBehaviour;
|
|
1160
|
+
(function (ConsentBlockingBehaviour) {
|
|
1161
|
+
ConsentBlockingBehaviour["PartiallyBlocked"] = "PARTIALLY_BLOCK";
|
|
1162
|
+
ConsentBlockingBehaviour["CompletelyBlocked"] = "FULLY_BLOCK";
|
|
1163
|
+
})(ConsentBlockingBehaviour || (ConsentBlockingBehaviour = {}));
|
|
1149
1164
|
|
|
1150
1165
|
class EventManager {
|
|
1151
1166
|
addEventHandler(eventType, callback) {
|
|
@@ -1173,17 +1188,14 @@ exports.EventType = void 0;
|
|
|
1173
1188
|
EventType["ConfigurationUpdate"] = "configurationUpdate";
|
|
1174
1189
|
})(exports.EventType || (exports.EventType = {}));
|
|
1175
1190
|
|
|
1176
|
-
|
|
1177
|
-
configuration: {
|
|
1178
|
-
consentType: ConsentType.NotRequired},
|
|
1179
|
-
});
|
|
1180
|
-
const DEFAULT_CLIENT_CONFIGURATION$1 = {
|
|
1191
|
+
const DEFAULT_DATA_FILE_CONFIGURATION = {
|
|
1181
1192
|
customData: [],
|
|
1182
1193
|
featureFlags: [],
|
|
1183
1194
|
configuration: {
|
|
1184
1195
|
realTimeUpdate: false,
|
|
1185
1196
|
consentType: ConsentType.NotRequired,
|
|
1186
1197
|
dataApiDomain: 'data.kameleoon.io',
|
|
1198
|
+
consentOptOutBehavior: ConsentBlockingBehaviour.PartiallyBlocked,
|
|
1187
1199
|
},
|
|
1188
1200
|
};
|
|
1189
1201
|
|
|
@@ -1207,7 +1219,7 @@ class ClientConfiguration {
|
|
|
1207
1219
|
constructor({ updateInterval, urlProvider, storage, requester, dataManager, eventSource, externalVisitorCodeManager, eventManager, externalPackageInfo, defaultDataFile, }) {
|
|
1208
1220
|
this.updateConfigurationIntervalId = null;
|
|
1209
1221
|
this.updateType = UpdateType.Polling;
|
|
1210
|
-
this.configurationData =
|
|
1222
|
+
this.configurationData = DEFAULT_DATA_FILE_CONFIGURATION;
|
|
1211
1223
|
this.featureFlagsData = new Map();
|
|
1212
1224
|
this.isTargetedDeliveryRule = null;
|
|
1213
1225
|
this.segmentsData = null;
|
|
@@ -1216,6 +1228,7 @@ class ClientConfiguration {
|
|
|
1216
1228
|
this.mappedRules = null;
|
|
1217
1229
|
this.mappedExperiments = null;
|
|
1218
1230
|
this.usedDefaultDataFile = false;
|
|
1231
|
+
this.blockingBehaviourMode = ConsentBlockingBehaviour.PartiallyBlocked;
|
|
1219
1232
|
this.CACHE_REVALIDATE_PERIOD = 90 * exports.Milliseconds.Minute;
|
|
1220
1233
|
this.urlProvider = urlProvider;
|
|
1221
1234
|
this.requester = requester;
|
|
@@ -1390,6 +1403,9 @@ class ClientConfiguration {
|
|
|
1390
1403
|
get isConsentRequired() {
|
|
1391
1404
|
return this.configuration.consentType === ConsentType.Required;
|
|
1392
1405
|
}
|
|
1406
|
+
get consentBlockingBehaviour() {
|
|
1407
|
+
return this.blockingBehaviourMode;
|
|
1408
|
+
}
|
|
1393
1409
|
get hasAnyTargetedDeliveryRule() {
|
|
1394
1410
|
if (this.isTargetedDeliveryRule !== null) {
|
|
1395
1411
|
return this.isTargetedDeliveryRule;
|
|
@@ -1518,6 +1534,7 @@ class ClientConfiguration {
|
|
|
1518
1534
|
KameleoonLogger.info `Configuration update type was toggled to ${UpdateType[updateType]}`;
|
|
1519
1535
|
yield this.initialize();
|
|
1520
1536
|
}
|
|
1537
|
+
this.blockingBehaviourMode = this.consentBlockingBehaviourFromStr(clientConfigurationData.configuration.consentOptOutBehavior);
|
|
1521
1538
|
return buildExports.Ok(toggleUpdateType);
|
|
1522
1539
|
});
|
|
1523
1540
|
}
|
|
@@ -1624,6 +1641,14 @@ class ClientConfiguration {
|
|
|
1624
1641
|
this.visitorCodeManager.consentRequired =
|
|
1625
1642
|
this.isConsentRequired && !this.hasAnyTargetedDeliveryRule;
|
|
1626
1643
|
}
|
|
1644
|
+
consentBlockingBehaviourFromStr(str) {
|
|
1645
|
+
if (str === ConsentBlockingBehaviour.PartiallyBlocked ||
|
|
1646
|
+
str === ConsentBlockingBehaviour.CompletelyBlocked) {
|
|
1647
|
+
return str;
|
|
1648
|
+
}
|
|
1649
|
+
KameleoonLogger.error(`Unexpected consent blocking type '${str}'`);
|
|
1650
|
+
return ConsentBlockingBehaviour.PartiallyBlocked;
|
|
1651
|
+
}
|
|
1627
1652
|
}
|
|
1628
1653
|
|
|
1629
1654
|
function constructTypeMap(indexMap) {
|
|
@@ -2418,6 +2443,13 @@ let CustomData$1 = class CustomData {
|
|
|
2418
2443
|
get name() {
|
|
2419
2444
|
return this._name;
|
|
2420
2445
|
}
|
|
2446
|
+
/**
|
|
2447
|
+
* @private
|
|
2448
|
+
* @method name - an internal getter for a name of custom data
|
|
2449
|
+
* */
|
|
2450
|
+
get values() {
|
|
2451
|
+
return this.value;
|
|
2452
|
+
}
|
|
2421
2453
|
};
|
|
2422
2454
|
CustomData$1.UNDEFINED_INDEX = -1;
|
|
2423
2455
|
|
|
@@ -2435,7 +2467,7 @@ let Conversion$1 = class Conversion {
|
|
|
2435
2467
|
this.negative = negative;
|
|
2436
2468
|
this.status = exports.TrackingStatus.Unsent;
|
|
2437
2469
|
this.id = Math.floor(Math.random() * 1000000);
|
|
2438
|
-
this.
|
|
2470
|
+
this._metadata = metadata;
|
|
2439
2471
|
}
|
|
2440
2472
|
set _id(id) {
|
|
2441
2473
|
this.id = id;
|
|
@@ -2456,9 +2488,21 @@ let Conversion$1 = class Conversion {
|
|
|
2456
2488
|
? UrlParameter.Metadata + this._encodeMetadata()
|
|
2457
2489
|
: ''));
|
|
2458
2490
|
}
|
|
2491
|
+
/**
|
|
2492
|
+
* @private
|
|
2493
|
+
* @method metadata - an internal getter for a metadata of conversion
|
|
2494
|
+
* */
|
|
2459
2495
|
get _metadata() {
|
|
2460
2496
|
return this.metadata;
|
|
2461
2497
|
}
|
|
2498
|
+
/**
|
|
2499
|
+
* @private
|
|
2500
|
+
* @method metadata - an internal setter for setting metadata of conversion
|
|
2501
|
+
* @param {number} value - an index value
|
|
2502
|
+
* */
|
|
2503
|
+
set _metadata(value) {
|
|
2504
|
+
this.metadata = value;
|
|
2505
|
+
}
|
|
2462
2506
|
get data() {
|
|
2463
2507
|
var _a;
|
|
2464
2508
|
return {
|
|
@@ -3551,7 +3595,7 @@ class DataProcessor {
|
|
|
3551
3595
|
this.cleanupInterval = cleanupInterval;
|
|
3552
3596
|
this.packageInfo = packageInfo;
|
|
3553
3597
|
}
|
|
3554
|
-
mutUpdateData({ infoData, visitorCode, mutData, dataItem, }) {
|
|
3598
|
+
mutUpdateData({ infoData, visitorCode, mutData, dataItem, extendTtl, }) {
|
|
3555
3599
|
let { visitorReference, data, isReference } = this.dereferenceData(mutData, visitorCode);
|
|
3556
3600
|
if (this.packageInfo.isServer &&
|
|
3557
3601
|
isReference &&
|
|
@@ -3562,9 +3606,12 @@ class DataProcessor {
|
|
|
3562
3606
|
delete mutData[visitorCode];
|
|
3563
3607
|
visitorReference = visitorCode;
|
|
3564
3608
|
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3609
|
+
let expirationTime;
|
|
3610
|
+
if (extendTtl) {
|
|
3611
|
+
expirationTime = this.cleanupInterval
|
|
3612
|
+
? Date.now() + this.cleanupInterval
|
|
3613
|
+
: 0;
|
|
3614
|
+
}
|
|
3568
3615
|
switch (dataItem.data.type) {
|
|
3569
3616
|
case exports.KameleoonData.CustomData: {
|
|
3570
3617
|
this.updateCustomData({
|
|
@@ -3919,13 +3966,17 @@ class DataProcessor {
|
|
|
3919
3966
|
return closestCleanupTime;
|
|
3920
3967
|
}
|
|
3921
3968
|
updateField({ key, value, data, visitorCode, expirationTime, }) {
|
|
3922
|
-
|
|
3969
|
+
var _a;
|
|
3970
|
+
const existing = data[visitorCode][key];
|
|
3971
|
+
data[visitorCode][key] = Object.assign(Object.assign({}, value), { expirationTime: (_a = expirationTime !== null && expirationTime !== void 0 ? expirationTime : (existing.expirationTime && existing.expirationTime)) !== null && _a !== void 0 ? _a : Date.now() });
|
|
3923
3972
|
}
|
|
3924
3973
|
createField({ key, value, data, visitorCode, expirationTime, }) {
|
|
3925
3974
|
data[visitorCode] = Object.assign(Object.assign({}, data[visitorCode]), { [key]: Object.assign(Object.assign({}, value), { expirationTime }) });
|
|
3926
3975
|
}
|
|
3927
3976
|
updateNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
|
|
3928
|
-
|
|
3977
|
+
var _a;
|
|
3978
|
+
const existing = data[visitorCode][key][nestedKey];
|
|
3979
|
+
data[visitorCode][key][nestedKey] = Object.assign(Object.assign({}, value), { expirationTime: (_a = expirationTime !== null && expirationTime !== void 0 ? expirationTime : existing === null || existing === void 0 ? void 0 : existing.expirationTime) !== null && _a !== void 0 ? _a : Date.now() });
|
|
3929
3980
|
}
|
|
3930
3981
|
createNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
|
|
3931
3982
|
var _a;
|
|
@@ -5191,15 +5242,7 @@ exports.KameleoonStorageKey = void 0;
|
|
|
5191
5242
|
KameleoonStorageKey["ForcedExperimentVariation"] = "kameleoonForcedExperimentVariation";
|
|
5192
5243
|
})(exports.KameleoonStorageKey || (exports.KameleoonStorageKey = {}));
|
|
5193
5244
|
const DEFAULT_CLIENT_CONFIGURATION = {
|
|
5194
|
-
data:
|
|
5195
|
-
customData: [],
|
|
5196
|
-
featureFlags: [],
|
|
5197
|
-
configuration: {
|
|
5198
|
-
realTimeUpdate: false,
|
|
5199
|
-
consentType: ConsentType.NotRequired,
|
|
5200
|
-
dataApiDomain: 'data.kameleoon.io',
|
|
5201
|
-
},
|
|
5202
|
-
},
|
|
5245
|
+
data: DEFAULT_DATA_FILE_CONFIGURATION,
|
|
5203
5246
|
lastUpdate: '',
|
|
5204
5247
|
};
|
|
5205
5248
|
// --- Note ---
|
|
@@ -5711,7 +5754,7 @@ class DataManager {
|
|
|
5711
5754
|
return resultData;
|
|
5712
5755
|
}
|
|
5713
5756
|
storeTrackedData(data) {
|
|
5714
|
-
this.storeData(data);
|
|
5757
|
+
this.storeData(data, false);
|
|
5715
5758
|
const infoResult = this.infoStorage.read();
|
|
5716
5759
|
if (!infoResult.ok) {
|
|
5717
5760
|
return;
|
|
@@ -5763,15 +5806,18 @@ class DataManager {
|
|
|
5763
5806
|
targetingData,
|
|
5764
5807
|
visitorCode: firstParameter,
|
|
5765
5808
|
kameleoonData: secondParameter,
|
|
5809
|
+
extendTtl: true,
|
|
5766
5810
|
});
|
|
5767
5811
|
}
|
|
5768
5812
|
else {
|
|
5769
5813
|
for (const [visitorCode, kameleoonData] of Object.entries(firstParameter)) {
|
|
5814
|
+
const extendTtl = typeof secondParameter[0] === 'boolean' ? secondParameter[0] : true;
|
|
5770
5815
|
this.mutUpdateTargetingData({
|
|
5771
5816
|
infoData,
|
|
5772
5817
|
targetingData,
|
|
5773
5818
|
visitorCode,
|
|
5774
5819
|
kameleoonData,
|
|
5820
|
+
extendTtl,
|
|
5775
5821
|
});
|
|
5776
5822
|
}
|
|
5777
5823
|
}
|
|
@@ -5888,8 +5934,8 @@ class DataManager {
|
|
|
5888
5934
|
}
|
|
5889
5935
|
return null;
|
|
5890
5936
|
}
|
|
5891
|
-
mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, }) {
|
|
5892
|
-
var _a;
|
|
5937
|
+
mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, extendTtl, }) {
|
|
5938
|
+
var _a, _b, _c;
|
|
5893
5939
|
for (const dataItem of kameleoonData) {
|
|
5894
5940
|
// process custom data
|
|
5895
5941
|
if (dataItem.data.type === exports.KameleoonData.CustomData) {
|
|
@@ -5905,16 +5951,21 @@ class DataManager {
|
|
|
5905
5951
|
}
|
|
5906
5952
|
// process metadata of conversions
|
|
5907
5953
|
if (dataItem.data.type === exports.KameleoonData.Conversion) {
|
|
5908
|
-
|
|
5954
|
+
const conversion = dataItem;
|
|
5955
|
+
if (((_b = (_a = conversion._metadata) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
|
|
5956
|
+
conversion._metadata = (_c = conversion._metadata) === null || _c === void 0 ? void 0 : _c.filter((item) => this.trySetCustomDataIndexByName(item));
|
|
5957
|
+
}
|
|
5909
5958
|
}
|
|
5910
5959
|
const expirationTime = this.dataProcessor.mutUpdateData({
|
|
5911
5960
|
infoData,
|
|
5912
5961
|
visitorCode,
|
|
5913
5962
|
mutData: targetingData,
|
|
5914
5963
|
dataItem,
|
|
5964
|
+
extendTtl,
|
|
5915
5965
|
});
|
|
5916
5966
|
const nextCleanup = infoData.nextDataCleanup;
|
|
5917
|
-
if (!nextCleanup ||
|
|
5967
|
+
if (!nextCleanup ||
|
|
5968
|
+
(nextCleanup && expirationTime && expirationTime < nextCleanup)) {
|
|
5918
5969
|
infoData.nextDataCleanup = expirationTime;
|
|
5919
5970
|
}
|
|
5920
5971
|
if (dataItem.data.status === exports.TrackingStatus.Unsent) {
|
|
@@ -5946,7 +5997,9 @@ class DataManager {
|
|
|
5946
5997
|
var _a;
|
|
5947
5998
|
const { data } = customData;
|
|
5948
5999
|
const isDataValid = Boolean(data.value.length && data.value[0].length);
|
|
5949
|
-
this.trySetCustomDataIndexByName(customData)
|
|
6000
|
+
if (!this.trySetCustomDataIndexByName(customData)) {
|
|
6001
|
+
return false;
|
|
6002
|
+
}
|
|
5950
6003
|
if (data.index == CustomData$1.UNDEFINED_INDEX) {
|
|
5951
6004
|
data.index = customData.index;
|
|
5952
6005
|
}
|
|
@@ -5976,13 +6029,15 @@ class DataManager {
|
|
|
5976
6029
|
return true;
|
|
5977
6030
|
}
|
|
5978
6031
|
trySetCustomDataIndexByName(customData) {
|
|
5979
|
-
if (customData.index
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
6032
|
+
if (customData.index !== CustomData$1.UNDEFINED_INDEX)
|
|
6033
|
+
return true;
|
|
6034
|
+
if (!customData.name)
|
|
6035
|
+
return false;
|
|
6036
|
+
const cdIndex = this.customDataIndexByName.get(customData.name);
|
|
6037
|
+
if (cdIndex == null)
|
|
6038
|
+
return false;
|
|
6039
|
+
customData.index = cdIndex;
|
|
6040
|
+
return true;
|
|
5986
6041
|
}
|
|
5987
6042
|
get unsentDataVisitors() {
|
|
5988
6043
|
const infoResult = this.infoStorage.read();
|
|
@@ -7095,6 +7150,13 @@ class Hasher {
|
|
|
7095
7150
|
}
|
|
7096
7151
|
}
|
|
7097
7152
|
|
|
7153
|
+
var LegalConsent;
|
|
7154
|
+
(function (LegalConsent) {
|
|
7155
|
+
LegalConsent[LegalConsent["Unknown"] = 0] = "Unknown";
|
|
7156
|
+
LegalConsent[LegalConsent["Given"] = 1] = "Given";
|
|
7157
|
+
LegalConsent[LegalConsent["NotGiven"] = 2] = "NotGiven";
|
|
7158
|
+
})(LegalConsent || (LegalConsent = {}));
|
|
7159
|
+
|
|
7098
7160
|
class VariationConfiguration {
|
|
7099
7161
|
constructor(externalStorage, externalStorageForcedExperimentVariations, externalStorageForcedFeatureVariations, visitorCodeManager, clientConfiguration) {
|
|
7100
7162
|
this.storage = externalStorage;
|
|
@@ -7153,9 +7215,12 @@ class VariationConfiguration {
|
|
|
7153
7215
|
}
|
|
7154
7216
|
return buildExports.Ok(featureFlagVariations);
|
|
7155
7217
|
}
|
|
7156
|
-
getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, track = true, withAssignment = false, }) {
|
|
7218
|
+
getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, legalConsent, track = true, withAssignment = false, }) {
|
|
7157
7219
|
KameleoonLogger.debug `CALL: VariationConfiguration.getVariation(visitorCode: ${visitorCode}, visitorIdentifier: ${visitorIdentifier}, featureFlag: ${featureFlag}, targetingData: ${targetingData}, packageInfo: ${packageInfo}, clientConfiguration, dataManager, withAssignment: ${withAssignment})`;
|
|
7158
7220
|
const { rules, featureKey, id: featureFlagId, defaultVariationKey, } = featureFlag;
|
|
7221
|
+
const consent = clientConfiguration.isConsentRequired
|
|
7222
|
+
? legalConsent
|
|
7223
|
+
: LegalConsent.Given;
|
|
7159
7224
|
for (const rule of rules) {
|
|
7160
7225
|
const { segment, experimentId, id, exposition, respoolTime, variationByExposition, } = rule;
|
|
7161
7226
|
const forcedVariationData = this.getForcedExperimentVariation(visitorCode, rule.experimentId);
|
|
@@ -7202,6 +7267,16 @@ class VariationConfiguration {
|
|
|
7202
7267
|
});
|
|
7203
7268
|
KameleoonLogger.debug `Calculated ruleHash: ${ruleHash} for code: ${visitorIdentifier}`;
|
|
7204
7269
|
if (ruleHash <= exposition) {
|
|
7270
|
+
// Checking if the evaluation is blocked due to the consent policy
|
|
7271
|
+
if (consent == LegalConsent.NotGiven &&
|
|
7272
|
+
rule.type == RuleType.EXPERIMENTATION) {
|
|
7273
|
+
const behaviour = clientConfiguration.consentBlockingBehaviour;
|
|
7274
|
+
if (behaviour == ConsentBlockingBehaviour.PartiallyBlocked) {
|
|
7275
|
+
break;
|
|
7276
|
+
}
|
|
7277
|
+
return buildExports.Err(new KameleoonError(exports.KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of ${rule} is blocked because consent is not provided for visitor '${visitorCode}'`));
|
|
7278
|
+
}
|
|
7279
|
+
// evaluate with CB scores if applicable
|
|
7205
7280
|
let variation = this.evaluateCBScores(rule, visitorIdentifier, targetingData);
|
|
7206
7281
|
if (!variation) {
|
|
7207
7282
|
const variationHash = Hasher.getHashDouble({
|
|
@@ -7563,7 +7638,6 @@ class KameleoonEventSource {
|
|
|
7563
7638
|
const VISITOR_CODE_LENGTH = 16;
|
|
7564
7639
|
const VISITOR_CODE_MAX_LENGTH = 255;
|
|
7565
7640
|
const DEFAULT_MAX_AGE = 60 * 60 * 24 * 365;
|
|
7566
|
-
const ZERO_MAX_AGE = 0;
|
|
7567
7641
|
const PATH = '/';
|
|
7568
7642
|
|
|
7569
7643
|
/**
|
|
@@ -8031,7 +8105,7 @@ class Tracker {
|
|
|
8031
8105
|
this.dataManager.storeTrackedData(updatedData);
|
|
8032
8106
|
}
|
|
8033
8107
|
else {
|
|
8034
|
-
this.dataManager.storeData(updatedData);
|
|
8108
|
+
this.dataManager.storeData(updatedData, false);
|
|
8035
8109
|
}
|
|
8036
8110
|
}
|
|
8037
8111
|
getUnsentVisitorData(visitorCode, isConsentProvided) {
|
|
@@ -8383,16 +8457,23 @@ class KameleoonClient {
|
|
|
8383
8457
|
const variations = new Map();
|
|
8384
8458
|
const featureFlags = this.clientConfiguration.featureFlags;
|
|
8385
8459
|
for (const featureFlag of featureFlags.values()) {
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
if (
|
|
8460
|
+
try {
|
|
8461
|
+
const variation = this._getFeatureVariation({
|
|
8462
|
+
visitorCode,
|
|
8463
|
+
featureKey: featureFlag.featureKey,
|
|
8464
|
+
track,
|
|
8465
|
+
});
|
|
8466
|
+
if (variation.ok &&
|
|
8467
|
+
(!onlyActive || variation.data.key !== OFF_VARIATION_KEY)) {
|
|
8393
8468
|
variations.set(featureFlag.featureKey, variation.data);
|
|
8394
8469
|
}
|
|
8395
8470
|
}
|
|
8471
|
+
catch (err) {
|
|
8472
|
+
if (err instanceof KameleoonError &&
|
|
8473
|
+
err.type !== exports.KameleoonException.FeatureFlagEnvironmentDisabled) {
|
|
8474
|
+
throw err;
|
|
8475
|
+
}
|
|
8476
|
+
}
|
|
8396
8477
|
}
|
|
8397
8478
|
KameleoonLogger.info `RETURN: KameleoonClient.getVariations(visitorCode: ${visitorCode}, onlyActive: ${onlyActive}, track: ${track}) -> (variations: ${variations})`;
|
|
8398
8479
|
return variations;
|
|
@@ -8712,25 +8793,16 @@ class KameleoonClient {
|
|
|
8712
8793
|
path: PATH,
|
|
8713
8794
|
});
|
|
8714
8795
|
}
|
|
8715
|
-
else {
|
|
8716
|
-
if (this.visitorCodeManager.consentRequired) {
|
|
8717
|
-
setData({
|
|
8718
|
-
visitorCode: '',
|
|
8719
|
-
key: exports.KameleoonStorageKey.VisitorCode,
|
|
8720
|
-
maxAge: ZERO_MAX_AGE,
|
|
8721
|
-
path: PATH,
|
|
8722
|
-
});
|
|
8723
|
-
}
|
|
8724
|
-
}
|
|
8725
8796
|
KameleoonLogger.info `RETURN: KameleoonClient.setUserConsent(visitorCode: ${visitorCode}, consent: ${consent}, setData: ${setData})`;
|
|
8726
8797
|
}
|
|
8727
8798
|
updateConsentData(visitorCode, consent) {
|
|
8728
8799
|
const readResult = this.consentDataStorage.read();
|
|
8800
|
+
const legalConsent = consent ? LegalConsent.Given : LegalConsent.NotGiven;
|
|
8729
8801
|
if (!readResult.ok) {
|
|
8730
8802
|
if (readResult.error.type === exports.KameleoonException.StorageEmpty) {
|
|
8731
8803
|
this.consentDataStorage.write({
|
|
8732
8804
|
[visitorCode]: {
|
|
8733
|
-
consent,
|
|
8805
|
+
consent: legalConsent,
|
|
8734
8806
|
},
|
|
8735
8807
|
});
|
|
8736
8808
|
}
|
|
@@ -8738,28 +8810,36 @@ class KameleoonClient {
|
|
|
8738
8810
|
}
|
|
8739
8811
|
const data = readResult.data;
|
|
8740
8812
|
data[visitorCode] = {
|
|
8741
|
-
consent,
|
|
8813
|
+
consent: legalConsent,
|
|
8742
8814
|
};
|
|
8743
8815
|
this.consentDataStorage.write(data);
|
|
8744
8816
|
}
|
|
8817
|
+
getLegalConsent(visitorCode) {
|
|
8818
|
+
KameleoonLogger.debug `CALL: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode})`;
|
|
8819
|
+
let legalConsent;
|
|
8820
|
+
const consentDataResult = this.consentDataStorage.read();
|
|
8821
|
+
legalConsent = consentDataResult.ok
|
|
8822
|
+
? this.extractLegalConsent(consentDataResult.data[visitorCode])
|
|
8823
|
+
: LegalConsent.Unknown;
|
|
8824
|
+
KameleoonLogger.debug `RETURN: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode}) -> (legalConsent: ${legalConsent})`;
|
|
8825
|
+
return legalConsent;
|
|
8826
|
+
}
|
|
8827
|
+
extractLegalConsent(consentData) {
|
|
8828
|
+
if (consentData === undefined)
|
|
8829
|
+
return LegalConsent.Unknown;
|
|
8830
|
+
if (typeof consentData === 'boolean') {
|
|
8831
|
+
return consentData ? LegalConsent.Given : LegalConsent.NotGiven;
|
|
8832
|
+
}
|
|
8833
|
+
const value = consentData.consent;
|
|
8834
|
+
if (typeof value === 'boolean')
|
|
8835
|
+
return value ? LegalConsent.Given : LegalConsent.NotGiven;
|
|
8836
|
+
return value;
|
|
8837
|
+
}
|
|
8745
8838
|
_isConsentProvided(visitorCode) {
|
|
8746
8839
|
KameleoonLogger.debug `CALL: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode})`;
|
|
8747
8840
|
const { isConsentRequired } = this.clientConfiguration;
|
|
8748
|
-
const
|
|
8749
|
-
|
|
8750
|
-
if (!isConsentRequired) {
|
|
8751
|
-
isConsentProvided = true;
|
|
8752
|
-
}
|
|
8753
|
-
else if (consentDataResult.ok) {
|
|
8754
|
-
const consentData = consentDataResult.data[visitorCode];
|
|
8755
|
-
// for consistency with the old data in the storage
|
|
8756
|
-
if (typeof consentData === 'boolean') {
|
|
8757
|
-
isConsentProvided = consentData;
|
|
8758
|
-
}
|
|
8759
|
-
else {
|
|
8760
|
-
isConsentProvided = consentData && consentData.consent;
|
|
8761
|
-
}
|
|
8762
|
-
}
|
|
8841
|
+
const isConsentProvided = !isConsentRequired ||
|
|
8842
|
+
this.getLegalConsent(visitorCode) == LegalConsent.Given;
|
|
8763
8843
|
KameleoonLogger.debug `RETURN: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode}) -> (isConsentProvided: ${isConsentProvided})`;
|
|
8764
8844
|
return isConsentProvided;
|
|
8765
8845
|
}
|
|
@@ -8787,22 +8867,32 @@ class KameleoonClient {
|
|
|
8787
8867
|
if (!featureFlag.environmentEnabled) {
|
|
8788
8868
|
continue;
|
|
8789
8869
|
}
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
if (evalExp.variationKey !== OFF_VARIATION_KEY) {
|
|
8797
|
-
activeVariations.push({
|
|
8798
|
-
variationKey: evalExp.variationKey,
|
|
8799
|
-
variationId: evalExp.variationId,
|
|
8800
|
-
experimentId: evalExp.experimentId,
|
|
8801
|
-
featureFlagId: featureFlag.id,
|
|
8802
|
-
featureKey: featureFlag.featureKey,
|
|
8803
|
-
rule: null,
|
|
8804
|
-
isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
|
|
8870
|
+
try {
|
|
8871
|
+
const evalExp = this._evaluate({
|
|
8872
|
+
visitorCode,
|
|
8873
|
+
featureFlag,
|
|
8874
|
+
track: false,
|
|
8875
|
+
save: false,
|
|
8805
8876
|
});
|
|
8877
|
+
if (evalExp.variationKey !== OFF_VARIATION_KEY) {
|
|
8878
|
+
activeVariations.push({
|
|
8879
|
+
variationKey: evalExp.variationKey,
|
|
8880
|
+
variationId: evalExp.variationId,
|
|
8881
|
+
experimentId: evalExp.experimentId,
|
|
8882
|
+
featureFlagId: featureFlag.id,
|
|
8883
|
+
featureKey: featureFlag.featureKey,
|
|
8884
|
+
rule: null,
|
|
8885
|
+
isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
|
|
8886
|
+
});
|
|
8887
|
+
}
|
|
8888
|
+
}
|
|
8889
|
+
catch (err) {
|
|
8890
|
+
if (err instanceof KameleoonError) {
|
|
8891
|
+
if (err.type != exports.KameleoonException.FeatureFlagEnvironmentDisabled) {
|
|
8892
|
+
KameleoonLogger.error `Unexpected error: ${err}`;
|
|
8893
|
+
}
|
|
8894
|
+
continue;
|
|
8895
|
+
}
|
|
8806
8896
|
}
|
|
8807
8897
|
}
|
|
8808
8898
|
KameleoonLogger.debug `RETURN: KameleoonClient._getActiveFeatureVariations(visitorCode: ${visitorCode}) -> (activeVariations: ${activeVariations})`;
|
|
@@ -8823,6 +8913,7 @@ class KameleoonClient {
|
|
|
8823
8913
|
else if (this._isVisitorNotInHoldout(visitorCode, track, save, featureFlag, visitorData) &&
|
|
8824
8914
|
this._isFFUnrestrictedByMEGroup(visitorCode, featureFlag, visitorData)) {
|
|
8825
8915
|
const visitorIdentifier = this._getCodeForHash(visitorCode, featureFlag.bucketingCustomDataIndex, visitorData);
|
|
8916
|
+
const legalConsent = this.getLegalConsent(visitorCode);
|
|
8826
8917
|
const variationData = this.variationConfiguration
|
|
8827
8918
|
.getVariation({
|
|
8828
8919
|
visitorCode,
|
|
@@ -8834,6 +8925,7 @@ class KameleoonClient {
|
|
|
8834
8925
|
clientConfiguration: this.clientConfiguration,
|
|
8835
8926
|
dataManager: this.dataManager,
|
|
8836
8927
|
packageInfo: this.externalPackageInfo,
|
|
8928
|
+
legalConsent,
|
|
8837
8929
|
})
|
|
8838
8930
|
.throw();
|
|
8839
8931
|
evalExp =
|
|
@@ -8952,6 +9044,14 @@ class KameleoonClient {
|
|
|
8952
9044
|
}
|
|
8953
9045
|
KameleoonLogger.debug `CALL: KameleoonClient._isVisitorNotInHoldout(visitorCode: ${visitorCode}, track: ${track}, save: ${save}, featureFlag: ${featureFlag}, visitorData: ${visitorData})`;
|
|
8954
9046
|
let isNotInHoldout = true;
|
|
9047
|
+
// Checking if the evaluation is blocked due to the consent policy
|
|
9048
|
+
const legalConsent = this.getLegalConsent(visitorCode);
|
|
9049
|
+
if (legalConsent == LegalConsent.NotGiven) {
|
|
9050
|
+
const behaviour = this.clientConfiguration.consentBlockingBehaviour;
|
|
9051
|
+
if (behaviour == ConsentBlockingBehaviour.CompletelyBlocked) {
|
|
9052
|
+
throw new KameleoonError(exports.KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of holdout is blocked because consent is not provided for visitor '${visitorCode}'`);
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
8955
9055
|
const codeForHash = this._getCodeForHash(visitorCode, featureFlag === null || featureFlag === void 0 ? void 0 : featureFlag.bucketingCustomDataIndex, visitorData);
|
|
8956
9056
|
const holdoutHash = Hasher.getHashDouble({
|
|
8957
9057
|
visitorIdentifier: codeForHash,
|