@kameleoon/javascript-sdk-core 5.16.1 → 5.17.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.
@@ -218,10 +218,17 @@ class KameleoonError extends Error {
218
218
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
219
219
  break;
220
220
  case KameleoonException.FeatureFlagVariationNotFound:
221
- case KameleoonException.FeatureFlagEnvironmentDisabled:
222
221
  case KameleoonException.FeatureFlagVariableNotFound:
223
222
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
224
223
  break;
224
+ case KameleoonException.FeatureFlagEnvironmentDisabled:
225
+ if (thirdParam !== undefined) {
226
+ this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
227
+ }
228
+ else {
229
+ this.message = secondParam;
230
+ }
231
+ break;
225
232
  case KameleoonException.StorageWrite:
226
233
  case KameleoonException.JSONParse:
227
234
  this.message = ERROR_MESSAGES[type](secondParam);
@@ -277,7 +284,7 @@ var RequestType;
277
284
  RequestType["RemoteData"] = "remoteData";
278
285
  })(RequestType || (RequestType = {}));
279
286
 
280
- const NUMBER_OF_RETRIES = 2;
287
+ const NUMBER_OF_RETRIES = 1;
281
288
  var Header;
282
289
  (function (Header) {
283
290
  Header["UserAgent"] = "User-Agent";
@@ -856,6 +863,9 @@ class UrlProvider {
856
863
  const currentDomain = this.domains[UrlType.DataApi];
857
864
  this.domains[UrlType.DataApi] = currentDomain.replace(/^[^.]+/, subDomain);
858
865
  }
866
+ get dataApiDomain() {
867
+ return this.domains[UrlType.DataApi];
868
+ }
859
869
  getClientConfigurationUrl(timeStamp) {
860
870
  this.isInitialized();
861
871
  const baseUrl = `https://${this.domains[UrlType.ClientConfiguration]}/v3/`;
@@ -1115,6 +1125,11 @@ var ConsentType;
1115
1125
  ConsentType["Required"] = "REQUIRED";
1116
1126
  ConsentType["NotRequired"] = "NOT_REQUIRED";
1117
1127
  })(ConsentType || (ConsentType = {}));
1128
+ var ConsentBlockingBehaviour;
1129
+ (function (ConsentBlockingBehaviour) {
1130
+ ConsentBlockingBehaviour["PartiallyBlocked"] = "PARTIALLY_BLOCK";
1131
+ ConsentBlockingBehaviour["CompletelyBlocked"] = "FULLY_BLOCK";
1132
+ })(ConsentBlockingBehaviour || (ConsentBlockingBehaviour = {}));
1118
1133
 
1119
1134
  class EventManager {
1120
1135
  addEventHandler(eventType, callback) {
@@ -1142,17 +1157,14 @@ var EventType;
1142
1157
  EventType["ConfigurationUpdate"] = "configurationUpdate";
1143
1158
  })(EventType || (EventType = {}));
1144
1159
 
1145
- ({
1146
- configuration: {
1147
- consentType: ConsentType.NotRequired},
1148
- });
1149
- const DEFAULT_CLIENT_CONFIGURATION$1 = {
1160
+ const DEFAULT_DATA_FILE_CONFIGURATION = {
1150
1161
  customData: [],
1151
1162
  featureFlags: [],
1152
1163
  configuration: {
1153
1164
  realTimeUpdate: false,
1154
1165
  consentType: ConsentType.NotRequired,
1155
1166
  dataApiDomain: 'data.kameleoon.io',
1167
+ consentOptOutBehavior: ConsentBlockingBehaviour.PartiallyBlocked,
1156
1168
  },
1157
1169
  };
1158
1170
 
@@ -1176,7 +1188,7 @@ class ClientConfiguration {
1176
1188
  constructor({ updateInterval, urlProvider, storage, requester, dataManager, eventSource, externalVisitorCodeManager, eventManager, externalPackageInfo, defaultDataFile, }) {
1177
1189
  this.updateConfigurationIntervalId = null;
1178
1190
  this.updateType = UpdateType.Polling;
1179
- this.configurationData = DEFAULT_CLIENT_CONFIGURATION$1;
1191
+ this.configurationData = DEFAULT_DATA_FILE_CONFIGURATION;
1180
1192
  this.featureFlagsData = new Map();
1181
1193
  this.isTargetedDeliveryRule = null;
1182
1194
  this.segmentsData = null;
@@ -1185,6 +1197,7 @@ class ClientConfiguration {
1185
1197
  this.mappedRules = null;
1186
1198
  this.mappedExperiments = null;
1187
1199
  this.usedDefaultDataFile = false;
1200
+ this.blockingBehaviourMode = ConsentBlockingBehaviour.PartiallyBlocked;
1188
1201
  this.CACHE_REVALIDATE_PERIOD = 90 * Milliseconds.Minute;
1189
1202
  this.urlProvider = urlProvider;
1190
1203
  this.requester = requester;
@@ -1359,6 +1372,9 @@ class ClientConfiguration {
1359
1372
  get isConsentRequired() {
1360
1373
  return this.configuration.consentType === ConsentType.Required;
1361
1374
  }
1375
+ get consentBlockingBehaviour() {
1376
+ return this.blockingBehaviourMode;
1377
+ }
1362
1378
  get hasAnyTargetedDeliveryRule() {
1363
1379
  if (this.isTargetedDeliveryRule !== null) {
1364
1380
  return this.isTargetedDeliveryRule;
@@ -1487,6 +1503,7 @@ class ClientConfiguration {
1487
1503
  KameleoonLogger.info `Configuration update type was toggled to ${UpdateType[updateType]}`;
1488
1504
  yield this.initialize();
1489
1505
  }
1506
+ this.blockingBehaviourMode = this.consentBlockingBehaviourFromStr(clientConfigurationData.configuration.consentOptOutBehavior);
1490
1507
  return buildExports.Ok(toggleUpdateType);
1491
1508
  });
1492
1509
  }
@@ -1593,6 +1610,14 @@ class ClientConfiguration {
1593
1610
  this.visitorCodeManager.consentRequired =
1594
1611
  this.isConsentRequired && !this.hasAnyTargetedDeliveryRule;
1595
1612
  }
1613
+ consentBlockingBehaviourFromStr(str) {
1614
+ if (str === ConsentBlockingBehaviour.PartiallyBlocked ||
1615
+ str === ConsentBlockingBehaviour.CompletelyBlocked) {
1616
+ return str;
1617
+ }
1618
+ KameleoonLogger.error(`Unexpected consent blocking type '${str}'`);
1619
+ return ConsentBlockingBehaviour.PartiallyBlocked;
1620
+ }
1596
1621
  }
1597
1622
 
1598
1623
  function constructTypeMap(indexMap) {
@@ -2387,6 +2412,13 @@ let CustomData$1 = class CustomData {
2387
2412
  get name() {
2388
2413
  return this._name;
2389
2414
  }
2415
+ /**
2416
+ * @private
2417
+ * @method name - an internal getter for a name of custom data
2418
+ * */
2419
+ get values() {
2420
+ return this.value;
2421
+ }
2390
2422
  };
2391
2423
  CustomData$1.UNDEFINED_INDEX = -1;
2392
2424
 
@@ -2404,7 +2436,7 @@ let Conversion$1 = class Conversion {
2404
2436
  this.negative = negative;
2405
2437
  this.status = TrackingStatus.Unsent;
2406
2438
  this.id = Math.floor(Math.random() * 1000000);
2407
- this.metadata = metadata;
2439
+ this._metadata = metadata;
2408
2440
  }
2409
2441
  set _id(id) {
2410
2442
  this.id = id;
@@ -2425,9 +2457,21 @@ let Conversion$1 = class Conversion {
2425
2457
  ? UrlParameter.Metadata + this._encodeMetadata()
2426
2458
  : ''));
2427
2459
  }
2460
+ /**
2461
+ * @private
2462
+ * @method metadata - an internal getter for a metadata of conversion
2463
+ * */
2428
2464
  get _metadata() {
2429
2465
  return this.metadata;
2430
2466
  }
2467
+ /**
2468
+ * @private
2469
+ * @method metadata - an internal setter for setting metadata of conversion
2470
+ * @param {number} value - an index value
2471
+ * */
2472
+ set _metadata(value) {
2473
+ this.metadata = value;
2474
+ }
2431
2475
  get data() {
2432
2476
  var _a;
2433
2477
  return {
@@ -2861,7 +2905,10 @@ class VisitsData {
2861
2905
  constructor(visits) {
2862
2906
  this.status = TrackingStatus.Sent;
2863
2907
  this.visits = visits;
2864
- this.visitNumber = visits.length ? visits.length - 1 : 0;
2908
+ this._visitNumber = visits.length ? visits.length - 1 : 0;
2909
+ }
2910
+ get visitNumber() {
2911
+ return this._visitNumber;
2865
2912
  }
2866
2913
  get url() {
2867
2914
  return '';
@@ -2869,7 +2916,7 @@ class VisitsData {
2869
2916
  get data() {
2870
2917
  return {
2871
2918
  visits: this.visits,
2872
- visitNumber: this.visitNumber,
2919
+ visitNumber: this._visitNumber,
2873
2920
  type: KameleoonData.VisitsData,
2874
2921
  status: this.status,
2875
2922
  };
@@ -2886,11 +2933,11 @@ class VisitsData {
2886
2933
  list: this.visits,
2887
2934
  visit,
2888
2935
  });
2889
- this.visitNumber = this.visits.length ? this.visits.length - 1 : 0;
2936
+ this._visitNumber = this.visits.length ? this.visits.length - 1 : 0;
2890
2937
  }
2891
2938
  updateVisitNumber(visitNumber) {
2892
- if (visitNumber > this.visitNumber) {
2893
- this.visitNumber = visitNumber;
2939
+ if (visitNumber > this._visitNumber) {
2940
+ this._visitNumber = visitNumber;
2894
2941
  }
2895
2942
  }
2896
2943
  }
@@ -3097,10 +3144,12 @@ class VisitProcessor {
3097
3144
  timeLastActivity: (_a = visit.timeLastEvent) !== null && _a !== void 0 ? _a : visit.timeStarted,
3098
3145
  };
3099
3146
  }
3100
- processVisitNumber(visit, isCurrentVisit) {
3101
- if (visit.staticDataEvent) {
3102
- const visitNumber = visit.staticDataEvent.data.visitNumber + (isCurrentVisit ? 0 : 1);
3103
- this.visitsData.updateVisitNumber(visitNumber);
3147
+ processVisitNumber(visit, visitOffset) {
3148
+ var _a, _b, _c;
3149
+ if (this.visitsData.visitNumber <= visitOffset &&
3150
+ ((_a = visit.staticDataEvent) === null || _a === void 0 ? void 0 : _a.data.visitNumber)) {
3151
+ const visitNumber = (_c = (_b = visit.staticDataEvent) === null || _b === void 0 ? void 0 : _b.data.visitNumber) !== null && _c !== void 0 ? _c : 0;
3152
+ this.visitsData.updateVisitNumber(visitNumber + visitOffset);
3104
3153
  }
3105
3154
  }
3106
3155
  processCbs(cbsData) {
@@ -3274,12 +3323,12 @@ class Parser {
3274
3323
  }
3275
3324
  if (currentVisit) {
3276
3325
  visitProcessor.processVisit(currentVisit);
3277
- visitProcessor.processVisitNumber(currentVisit, true);
3326
+ visitProcessor.processVisitNumber(currentVisit, 0);
3278
3327
  }
3279
- previousVisits === null || previousVisits === void 0 ? void 0 : previousVisits.forEach((visit) => {
3328
+ previousVisits === null || previousVisits === void 0 ? void 0 : previousVisits.forEach((visit, index) => {
3280
3329
  visitProcessor.processVisit(visit);
3281
3330
  visitProcessor.processVisitsData(visit);
3282
- visitProcessor.processVisitNumber(visit, false);
3331
+ visitProcessor.processVisitNumber(visit, index + 1);
3283
3332
  });
3284
3333
  visitProcessor.processKcs(kcs);
3285
3334
  visitProcessor.processCbs(cbs);
@@ -3520,7 +3569,7 @@ class DataProcessor {
3520
3569
  this.cleanupInterval = cleanupInterval;
3521
3570
  this.packageInfo = packageInfo;
3522
3571
  }
3523
- mutUpdateData({ infoData, visitorCode, mutData, dataItem, }) {
3572
+ mutUpdateData({ infoData, visitorCode, mutData, dataItem, extendTtl, }) {
3524
3573
  let { visitorReference, data, isReference } = this.dereferenceData(mutData, visitorCode);
3525
3574
  if (this.packageInfo.isServer &&
3526
3575
  isReference &&
@@ -3531,9 +3580,12 @@ class DataProcessor {
3531
3580
  delete mutData[visitorCode];
3532
3581
  visitorReference = visitorCode;
3533
3582
  }
3534
- const expirationTime = this.cleanupInterval
3535
- ? Date.now() + this.cleanupInterval
3536
- : 0;
3583
+ let expirationTime;
3584
+ if (extendTtl) {
3585
+ expirationTime = this.cleanupInterval
3586
+ ? Date.now() + this.cleanupInterval
3587
+ : 0;
3588
+ }
3537
3589
  switch (dataItem.data.type) {
3538
3590
  case KameleoonData.CustomData: {
3539
3591
  this.updateCustomData({
@@ -3888,13 +3940,17 @@ class DataProcessor {
3888
3940
  return closestCleanupTime;
3889
3941
  }
3890
3942
  updateField({ key, value, data, visitorCode, expirationTime, }) {
3891
- data[visitorCode][key] = Object.assign(Object.assign({}, value), { expirationTime });
3943
+ var _a;
3944
+ const existing = data[visitorCode][key];
3945
+ 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() });
3892
3946
  }
3893
3947
  createField({ key, value, data, visitorCode, expirationTime, }) {
3894
3948
  data[visitorCode] = Object.assign(Object.assign({}, data[visitorCode]), { [key]: Object.assign(Object.assign({}, value), { expirationTime }) });
3895
3949
  }
3896
3950
  updateNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3897
- data[visitorCode][key][nestedKey] = Object.assign(Object.assign({}, value), { expirationTime });
3951
+ var _a;
3952
+ const existing = data[visitorCode][key][nestedKey];
3953
+ 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() });
3898
3954
  }
3899
3955
  createNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3900
3956
  var _a;
@@ -5160,15 +5216,7 @@ var KameleoonStorageKey;
5160
5216
  KameleoonStorageKey["ForcedExperimentVariation"] = "kameleoonForcedExperimentVariation";
5161
5217
  })(KameleoonStorageKey || (KameleoonStorageKey = {}));
5162
5218
  const DEFAULT_CLIENT_CONFIGURATION = {
5163
- data: {
5164
- customData: [],
5165
- featureFlags: [],
5166
- configuration: {
5167
- realTimeUpdate: false,
5168
- consentType: ConsentType.NotRequired,
5169
- dataApiDomain: 'data.kameleoon.io',
5170
- },
5171
- },
5219
+ data: DEFAULT_DATA_FILE_CONFIGURATION,
5172
5220
  lastUpdate: '',
5173
5221
  };
5174
5222
  // --- Note ---
@@ -5680,7 +5728,7 @@ class DataManager {
5680
5728
  return resultData;
5681
5729
  }
5682
5730
  storeTrackedData(data) {
5683
- this.storeData(data);
5731
+ this.storeData(data, false);
5684
5732
  const infoResult = this.infoStorage.read();
5685
5733
  if (!infoResult.ok) {
5686
5734
  return;
@@ -5732,15 +5780,18 @@ class DataManager {
5732
5780
  targetingData,
5733
5781
  visitorCode: firstParameter,
5734
5782
  kameleoonData: secondParameter,
5783
+ extendTtl: true,
5735
5784
  });
5736
5785
  }
5737
5786
  else {
5738
5787
  for (const [visitorCode, kameleoonData] of Object.entries(firstParameter)) {
5788
+ const extendTtl = typeof secondParameter[0] === 'boolean' ? secondParameter[0] : true;
5739
5789
  this.mutUpdateTargetingData({
5740
5790
  infoData,
5741
5791
  targetingData,
5742
5792
  visitorCode,
5743
5793
  kameleoonData,
5794
+ extendTtl,
5744
5795
  });
5745
5796
  }
5746
5797
  }
@@ -5857,8 +5908,8 @@ class DataManager {
5857
5908
  }
5858
5909
  return null;
5859
5910
  }
5860
- mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, }) {
5861
- var _a;
5911
+ mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, extendTtl, }) {
5912
+ var _a, _b, _c;
5862
5913
  for (const dataItem of kameleoonData) {
5863
5914
  // process custom data
5864
5915
  if (dataItem.data.type === KameleoonData.CustomData) {
@@ -5874,16 +5925,21 @@ class DataManager {
5874
5925
  }
5875
5926
  // process metadata of conversions
5876
5927
  if (dataItem.data.type === KameleoonData.Conversion) {
5877
- (_a = dataItem._metadata) === null || _a === void 0 ? void 0 : _a.forEach((item) => this.trySetCustomDataIndexByName(item));
5928
+ const conversion = dataItem;
5929
+ if (((_b = (_a = conversion._metadata) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
5930
+ conversion._metadata = (_c = conversion._metadata) === null || _c === void 0 ? void 0 : _c.filter((item) => this.trySetCustomDataIndexByName(item));
5931
+ }
5878
5932
  }
5879
5933
  const expirationTime = this.dataProcessor.mutUpdateData({
5880
5934
  infoData,
5881
5935
  visitorCode,
5882
5936
  mutData: targetingData,
5883
5937
  dataItem,
5938
+ extendTtl,
5884
5939
  });
5885
5940
  const nextCleanup = infoData.nextDataCleanup;
5886
- if (!nextCleanup || (nextCleanup && expirationTime < nextCleanup)) {
5941
+ if (!nextCleanup ||
5942
+ (nextCleanup && expirationTime && expirationTime < nextCleanup)) {
5887
5943
  infoData.nextDataCleanup = expirationTime;
5888
5944
  }
5889
5945
  if (dataItem.data.status === TrackingStatus.Unsent) {
@@ -5915,7 +5971,9 @@ class DataManager {
5915
5971
  var _a;
5916
5972
  const { data } = customData;
5917
5973
  const isDataValid = Boolean(data.value.length && data.value[0].length);
5918
- this.trySetCustomDataIndexByName(customData);
5974
+ if (!this.trySetCustomDataIndexByName(customData)) {
5975
+ return false;
5976
+ }
5919
5977
  if (data.index == CustomData$1.UNDEFINED_INDEX) {
5920
5978
  data.index = customData.index;
5921
5979
  }
@@ -5945,13 +6003,15 @@ class DataManager {
5945
6003
  return true;
5946
6004
  }
5947
6005
  trySetCustomDataIndexByName(customData) {
5948
- if (customData.index == CustomData$1.UNDEFINED_INDEX && customData.name) {
5949
- const cdIndex = this.customDataIndexByName.get(customData.name);
5950
- if (cdIndex === undefined) {
5951
- return;
5952
- }
5953
- customData.index = cdIndex;
5954
- }
6006
+ if (customData.index !== CustomData$1.UNDEFINED_INDEX)
6007
+ return true;
6008
+ if (!customData.name)
6009
+ return false;
6010
+ const cdIndex = this.customDataIndexByName.get(customData.name);
6011
+ if (cdIndex == null)
6012
+ return false;
6013
+ customData.index = cdIndex;
6014
+ return true;
5955
6015
  }
5956
6016
  get unsentDataVisitors() {
5957
6017
  const infoResult = this.infoStorage.read();
@@ -7055,6 +7115,13 @@ class Hasher {
7055
7115
  }
7056
7116
  }
7057
7117
 
7118
+ var LegalConsent;
7119
+ (function (LegalConsent) {
7120
+ LegalConsent[LegalConsent["Unknown"] = 0] = "Unknown";
7121
+ LegalConsent[LegalConsent["Given"] = 1] = "Given";
7122
+ LegalConsent[LegalConsent["NotGiven"] = 2] = "NotGiven";
7123
+ })(LegalConsent || (LegalConsent = {}));
7124
+
7058
7125
  class VariationConfiguration {
7059
7126
  constructor(externalStorage, externalStorageForcedExperimentVariations, externalStorageForcedFeatureVariations, visitorCodeManager, clientConfiguration) {
7060
7127
  this.storage = externalStorage;
@@ -7113,9 +7180,12 @@ class VariationConfiguration {
7113
7180
  }
7114
7181
  return buildExports.Ok(featureFlagVariations);
7115
7182
  }
7116
- getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, track = true, withAssignment = false, }) {
7183
+ getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, legalConsent, track = true, withAssignment = false, }) {
7117
7184
  KameleoonLogger.debug `CALL: VariationConfiguration.getVariation(visitorCode: ${visitorCode}, visitorIdentifier: ${visitorIdentifier}, featureFlag: ${featureFlag}, targetingData: ${targetingData}, packageInfo: ${packageInfo}, clientConfiguration, dataManager, withAssignment: ${withAssignment})`;
7118
7185
  const { rules, featureKey, id: featureFlagId, defaultVariationKey, } = featureFlag;
7186
+ const consent = clientConfiguration.isConsentRequired
7187
+ ? legalConsent
7188
+ : LegalConsent.Given;
7119
7189
  for (const rule of rules) {
7120
7190
  const { segment, experimentId, id, exposition, respoolTime, variationByExposition, } = rule;
7121
7191
  const forcedVariationData = this.getForcedExperimentVariation(visitorCode, rule.experimentId);
@@ -7162,6 +7232,16 @@ class VariationConfiguration {
7162
7232
  });
7163
7233
  KameleoonLogger.debug `Calculated ruleHash: ${ruleHash} for code: ${visitorIdentifier}`;
7164
7234
  if (ruleHash <= exposition) {
7235
+ // Checking if the evaluation is blocked due to the consent policy
7236
+ if (consent == LegalConsent.NotGiven &&
7237
+ rule.type == RuleType.EXPERIMENTATION) {
7238
+ const behaviour = clientConfiguration.consentBlockingBehaviour;
7239
+ if (behaviour == ConsentBlockingBehaviour.PartiallyBlocked) {
7240
+ break;
7241
+ }
7242
+ return buildExports.Err(new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of ${rule} is blocked because consent is not provided for visitor '${visitorCode}'`));
7243
+ }
7244
+ // evaluate with CB scores if applicable
7165
7245
  let variation = this.evaluateCBScores(rule, visitorIdentifier, targetingData);
7166
7246
  if (!variation) {
7167
7247
  const variationHash = Hasher.getHashDouble({
@@ -7523,7 +7603,6 @@ class KameleoonEventSource {
7523
7603
  const VISITOR_CODE_LENGTH = 16;
7524
7604
  const VISITOR_CODE_MAX_LENGTH = 255;
7525
7605
  const DEFAULT_MAX_AGE = 60 * 60 * 24 * 365;
7526
- const ZERO_MAX_AGE = 0;
7527
7606
  const PATH = '/';
7528
7607
 
7529
7608
  /**
@@ -7991,7 +8070,7 @@ class Tracker {
7991
8070
  this.dataManager.storeTrackedData(updatedData);
7992
8071
  }
7993
8072
  else {
7994
- this.dataManager.storeData(updatedData);
8073
+ this.dataManager.storeData(updatedData, false);
7995
8074
  }
7996
8075
  }
7997
8076
  getUnsentVisitorData(visitorCode, isConsentProvided) {
@@ -8343,16 +8422,23 @@ class KameleoonClient {
8343
8422
  const variations = new Map();
8344
8423
  const featureFlags = this.clientConfiguration.featureFlags;
8345
8424
  for (const featureFlag of featureFlags.values()) {
8346
- const variation = this._getFeatureVariation({
8347
- visitorCode,
8348
- featureKey: featureFlag.featureKey,
8349
- track,
8350
- });
8351
- if (variation.ok) {
8352
- if (!onlyActive || variation.data.key !== OFF_VARIATION_KEY) {
8425
+ try {
8426
+ const variation = this._getFeatureVariation({
8427
+ visitorCode,
8428
+ featureKey: featureFlag.featureKey,
8429
+ track,
8430
+ });
8431
+ if (variation.ok &&
8432
+ (!onlyActive || variation.data.key !== OFF_VARIATION_KEY)) {
8353
8433
  variations.set(featureFlag.featureKey, variation.data);
8354
8434
  }
8355
8435
  }
8436
+ catch (err) {
8437
+ if (err instanceof KameleoonError &&
8438
+ err.type !== KameleoonException.FeatureFlagEnvironmentDisabled) {
8439
+ throw err;
8440
+ }
8441
+ }
8356
8442
  }
8357
8443
  KameleoonLogger.info `RETURN: KameleoonClient.getVariations(visitorCode: ${visitorCode}, onlyActive: ${onlyActive}, track: ${track}) -> (variations: ${variations})`;
8358
8444
  return variations;
@@ -8672,25 +8758,16 @@ class KameleoonClient {
8672
8758
  path: PATH,
8673
8759
  });
8674
8760
  }
8675
- else {
8676
- if (this.visitorCodeManager.consentRequired) {
8677
- setData({
8678
- visitorCode: '',
8679
- key: KameleoonStorageKey.VisitorCode,
8680
- maxAge: ZERO_MAX_AGE,
8681
- path: PATH,
8682
- });
8683
- }
8684
- }
8685
8761
  KameleoonLogger.info `RETURN: KameleoonClient.setUserConsent(visitorCode: ${visitorCode}, consent: ${consent}, setData: ${setData})`;
8686
8762
  }
8687
8763
  updateConsentData(visitorCode, consent) {
8688
8764
  const readResult = this.consentDataStorage.read();
8765
+ const legalConsent = consent ? LegalConsent.Given : LegalConsent.NotGiven;
8689
8766
  if (!readResult.ok) {
8690
8767
  if (readResult.error.type === KameleoonException.StorageEmpty) {
8691
8768
  this.consentDataStorage.write({
8692
8769
  [visitorCode]: {
8693
- consent,
8770
+ consent: legalConsent,
8694
8771
  },
8695
8772
  });
8696
8773
  }
@@ -8698,28 +8775,36 @@ class KameleoonClient {
8698
8775
  }
8699
8776
  const data = readResult.data;
8700
8777
  data[visitorCode] = {
8701
- consent,
8778
+ consent: legalConsent,
8702
8779
  };
8703
8780
  this.consentDataStorage.write(data);
8704
8781
  }
8782
+ getLegalConsent(visitorCode) {
8783
+ KameleoonLogger.debug `CALL: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode})`;
8784
+ let legalConsent;
8785
+ const consentDataResult = this.consentDataStorage.read();
8786
+ legalConsent = consentDataResult.ok
8787
+ ? this.extractLegalConsent(consentDataResult.data[visitorCode])
8788
+ : LegalConsent.Unknown;
8789
+ KameleoonLogger.debug `RETURN: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode}) -> (legalConsent: ${legalConsent})`;
8790
+ return legalConsent;
8791
+ }
8792
+ extractLegalConsent(consentData) {
8793
+ if (consentData === undefined)
8794
+ return LegalConsent.Unknown;
8795
+ if (typeof consentData === 'boolean') {
8796
+ return consentData ? LegalConsent.Given : LegalConsent.NotGiven;
8797
+ }
8798
+ const value = consentData.consent;
8799
+ if (typeof value === 'boolean')
8800
+ return value ? LegalConsent.Given : LegalConsent.NotGiven;
8801
+ return value;
8802
+ }
8705
8803
  _isConsentProvided(visitorCode) {
8706
8804
  KameleoonLogger.debug `CALL: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode})`;
8707
8805
  const { isConsentRequired } = this.clientConfiguration;
8708
- const consentDataResult = this.consentDataStorage.read();
8709
- let isConsentProvided = false;
8710
- if (!isConsentRequired) {
8711
- isConsentProvided = true;
8712
- }
8713
- else if (consentDataResult.ok) {
8714
- const consentData = consentDataResult.data[visitorCode];
8715
- // for consistency with the old data in the storage
8716
- if (typeof consentData === 'boolean') {
8717
- isConsentProvided = consentData;
8718
- }
8719
- else {
8720
- isConsentProvided = consentData && consentData.consent;
8721
- }
8722
- }
8806
+ const isConsentProvided = !isConsentRequired ||
8807
+ this.getLegalConsent(visitorCode) == LegalConsent.Given;
8723
8808
  KameleoonLogger.debug `RETURN: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode}) -> (isConsentProvided: ${isConsentProvided})`;
8724
8809
  return isConsentProvided;
8725
8810
  }
@@ -8747,22 +8832,32 @@ class KameleoonClient {
8747
8832
  if (!featureFlag.environmentEnabled) {
8748
8833
  continue;
8749
8834
  }
8750
- const evalExp = this._evaluate({
8751
- visitorCode,
8752
- featureFlag,
8753
- track: false,
8754
- save: false,
8755
- });
8756
- if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8757
- activeVariations.push({
8758
- variationKey: evalExp.variationKey,
8759
- variationId: evalExp.variationId,
8760
- experimentId: evalExp.experimentId,
8761
- featureFlagId: featureFlag.id,
8762
- featureKey: featureFlag.featureKey,
8763
- rule: null,
8764
- isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8835
+ try {
8836
+ const evalExp = this._evaluate({
8837
+ visitorCode,
8838
+ featureFlag,
8839
+ track: false,
8840
+ save: false,
8765
8841
  });
8842
+ if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8843
+ activeVariations.push({
8844
+ variationKey: evalExp.variationKey,
8845
+ variationId: evalExp.variationId,
8846
+ experimentId: evalExp.experimentId,
8847
+ featureFlagId: featureFlag.id,
8848
+ featureKey: featureFlag.featureKey,
8849
+ rule: null,
8850
+ isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8851
+ });
8852
+ }
8853
+ }
8854
+ catch (err) {
8855
+ if (err instanceof KameleoonError) {
8856
+ if (err.type != KameleoonException.FeatureFlagEnvironmentDisabled) {
8857
+ KameleoonLogger.error `Unexpected error: ${err}`;
8858
+ }
8859
+ continue;
8860
+ }
8766
8861
  }
8767
8862
  }
8768
8863
  KameleoonLogger.debug `RETURN: KameleoonClient._getActiveFeatureVariations(visitorCode: ${visitorCode}) -> (activeVariations: ${activeVariations})`;
@@ -8783,6 +8878,7 @@ class KameleoonClient {
8783
8878
  else if (this._isVisitorNotInHoldout(visitorCode, track, save, featureFlag, visitorData) &&
8784
8879
  this._isFFUnrestrictedByMEGroup(visitorCode, featureFlag, visitorData)) {
8785
8880
  const visitorIdentifier = this._getCodeForHash(visitorCode, featureFlag.bucketingCustomDataIndex, visitorData);
8881
+ const legalConsent = this.getLegalConsent(visitorCode);
8786
8882
  const variationData = this.variationConfiguration
8787
8883
  .getVariation({
8788
8884
  visitorCode,
@@ -8794,6 +8890,7 @@ class KameleoonClient {
8794
8890
  clientConfiguration: this.clientConfiguration,
8795
8891
  dataManager: this.dataManager,
8796
8892
  packageInfo: this.externalPackageInfo,
8893
+ legalConsent,
8797
8894
  })
8798
8895
  .throw();
8799
8896
  evalExp =
@@ -8912,6 +9009,16 @@ class KameleoonClient {
8912
9009
  }
8913
9010
  KameleoonLogger.debug `CALL: KameleoonClient._isVisitorNotInHoldout(visitorCode: ${visitorCode}, track: ${track}, save: ${save}, featureFlag: ${featureFlag}, visitorData: ${visitorData})`;
8914
9011
  let isNotInHoldout = true;
9012
+ // Checking if the evaluation is blocked due to the consent policy
9013
+ const legalConsent = this.clientConfiguration.isConsentRequired
9014
+ ? this.getLegalConsent(visitorCode)
9015
+ : LegalConsent.Given;
9016
+ if (legalConsent == LegalConsent.NotGiven) {
9017
+ const behaviour = this.clientConfiguration.consentBlockingBehaviour;
9018
+ if (behaviour == ConsentBlockingBehaviour.CompletelyBlocked) {
9019
+ throw new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of holdout is blocked because consent is not provided for visitor '${visitorCode}'`);
9020
+ }
9021
+ }
8915
9022
  const codeForHash = this._getCodeForHash(visitorCode, featureFlag === null || featureFlag === void 0 ? void 0 : featureFlag.bucketingCustomDataIndex, visitorData);
8916
9023
  const holdoutHash = Hasher.getHashDouble({
8917
9024
  visitorIdentifier: codeForHash,