@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.
@@ -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 {
@@ -3520,7 +3564,7 @@ class DataProcessor {
3520
3564
  this.cleanupInterval = cleanupInterval;
3521
3565
  this.packageInfo = packageInfo;
3522
3566
  }
3523
- mutUpdateData({ infoData, visitorCode, mutData, dataItem, }) {
3567
+ mutUpdateData({ infoData, visitorCode, mutData, dataItem, extendTtl, }) {
3524
3568
  let { visitorReference, data, isReference } = this.dereferenceData(mutData, visitorCode);
3525
3569
  if (this.packageInfo.isServer &&
3526
3570
  isReference &&
@@ -3531,9 +3575,12 @@ class DataProcessor {
3531
3575
  delete mutData[visitorCode];
3532
3576
  visitorReference = visitorCode;
3533
3577
  }
3534
- const expirationTime = this.cleanupInterval
3535
- ? Date.now() + this.cleanupInterval
3536
- : 0;
3578
+ let expirationTime;
3579
+ if (extendTtl) {
3580
+ expirationTime = this.cleanupInterval
3581
+ ? Date.now() + this.cleanupInterval
3582
+ : 0;
3583
+ }
3537
3584
  switch (dataItem.data.type) {
3538
3585
  case KameleoonData.CustomData: {
3539
3586
  this.updateCustomData({
@@ -3888,13 +3935,17 @@ class DataProcessor {
3888
3935
  return closestCleanupTime;
3889
3936
  }
3890
3937
  updateField({ key, value, data, visitorCode, expirationTime, }) {
3891
- data[visitorCode][key] = Object.assign(Object.assign({}, value), { expirationTime });
3938
+ var _a;
3939
+ const existing = data[visitorCode][key];
3940
+ 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
3941
  }
3893
3942
  createField({ key, value, data, visitorCode, expirationTime, }) {
3894
3943
  data[visitorCode] = Object.assign(Object.assign({}, data[visitorCode]), { [key]: Object.assign(Object.assign({}, value), { expirationTime }) });
3895
3944
  }
3896
3945
  updateNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3897
- data[visitorCode][key][nestedKey] = Object.assign(Object.assign({}, value), { expirationTime });
3946
+ var _a;
3947
+ const existing = data[visitorCode][key][nestedKey];
3948
+ 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
3949
  }
3899
3950
  createNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3900
3951
  var _a;
@@ -5160,15 +5211,7 @@ var KameleoonStorageKey;
5160
5211
  KameleoonStorageKey["ForcedExperimentVariation"] = "kameleoonForcedExperimentVariation";
5161
5212
  })(KameleoonStorageKey || (KameleoonStorageKey = {}));
5162
5213
  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
- },
5214
+ data: DEFAULT_DATA_FILE_CONFIGURATION,
5172
5215
  lastUpdate: '',
5173
5216
  };
5174
5217
  // --- Note ---
@@ -5680,7 +5723,7 @@ class DataManager {
5680
5723
  return resultData;
5681
5724
  }
5682
5725
  storeTrackedData(data) {
5683
- this.storeData(data);
5726
+ this.storeData(data, false);
5684
5727
  const infoResult = this.infoStorage.read();
5685
5728
  if (!infoResult.ok) {
5686
5729
  return;
@@ -5732,15 +5775,18 @@ class DataManager {
5732
5775
  targetingData,
5733
5776
  visitorCode: firstParameter,
5734
5777
  kameleoonData: secondParameter,
5778
+ extendTtl: true,
5735
5779
  });
5736
5780
  }
5737
5781
  else {
5738
5782
  for (const [visitorCode, kameleoonData] of Object.entries(firstParameter)) {
5783
+ const extendTtl = typeof secondParameter[0] === 'boolean' ? secondParameter[0] : true;
5739
5784
  this.mutUpdateTargetingData({
5740
5785
  infoData,
5741
5786
  targetingData,
5742
5787
  visitorCode,
5743
5788
  kameleoonData,
5789
+ extendTtl,
5744
5790
  });
5745
5791
  }
5746
5792
  }
@@ -5857,8 +5903,8 @@ class DataManager {
5857
5903
  }
5858
5904
  return null;
5859
5905
  }
5860
- mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, }) {
5861
- var _a;
5906
+ mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, extendTtl, }) {
5907
+ var _a, _b, _c;
5862
5908
  for (const dataItem of kameleoonData) {
5863
5909
  // process custom data
5864
5910
  if (dataItem.data.type === KameleoonData.CustomData) {
@@ -5874,16 +5920,21 @@ class DataManager {
5874
5920
  }
5875
5921
  // process metadata of conversions
5876
5922
  if (dataItem.data.type === KameleoonData.Conversion) {
5877
- (_a = dataItem._metadata) === null || _a === void 0 ? void 0 : _a.forEach((item) => this.trySetCustomDataIndexByName(item));
5923
+ const conversion = dataItem;
5924
+ if (((_b = (_a = conversion._metadata) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
5925
+ conversion._metadata = (_c = conversion._metadata) === null || _c === void 0 ? void 0 : _c.filter((item) => this.trySetCustomDataIndexByName(item));
5926
+ }
5878
5927
  }
5879
5928
  const expirationTime = this.dataProcessor.mutUpdateData({
5880
5929
  infoData,
5881
5930
  visitorCode,
5882
5931
  mutData: targetingData,
5883
5932
  dataItem,
5933
+ extendTtl,
5884
5934
  });
5885
5935
  const nextCleanup = infoData.nextDataCleanup;
5886
- if (!nextCleanup || (nextCleanup && expirationTime < nextCleanup)) {
5936
+ if (!nextCleanup ||
5937
+ (nextCleanup && expirationTime && expirationTime < nextCleanup)) {
5887
5938
  infoData.nextDataCleanup = expirationTime;
5888
5939
  }
5889
5940
  if (dataItem.data.status === TrackingStatus.Unsent) {
@@ -5915,7 +5966,9 @@ class DataManager {
5915
5966
  var _a;
5916
5967
  const { data } = customData;
5917
5968
  const isDataValid = Boolean(data.value.length && data.value[0].length);
5918
- this.trySetCustomDataIndexByName(customData);
5969
+ if (!this.trySetCustomDataIndexByName(customData)) {
5970
+ return false;
5971
+ }
5919
5972
  if (data.index == CustomData$1.UNDEFINED_INDEX) {
5920
5973
  data.index = customData.index;
5921
5974
  }
@@ -5945,13 +5998,15 @@ class DataManager {
5945
5998
  return true;
5946
5999
  }
5947
6000
  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
- }
6001
+ if (customData.index !== CustomData$1.UNDEFINED_INDEX)
6002
+ return true;
6003
+ if (!customData.name)
6004
+ return false;
6005
+ const cdIndex = this.customDataIndexByName.get(customData.name);
6006
+ if (cdIndex == null)
6007
+ return false;
6008
+ customData.index = cdIndex;
6009
+ return true;
5955
6010
  }
5956
6011
  get unsentDataVisitors() {
5957
6012
  const infoResult = this.infoStorage.read();
@@ -7055,6 +7110,13 @@ class Hasher {
7055
7110
  }
7056
7111
  }
7057
7112
 
7113
+ var LegalConsent;
7114
+ (function (LegalConsent) {
7115
+ LegalConsent[LegalConsent["Unknown"] = 0] = "Unknown";
7116
+ LegalConsent[LegalConsent["Given"] = 1] = "Given";
7117
+ LegalConsent[LegalConsent["NotGiven"] = 2] = "NotGiven";
7118
+ })(LegalConsent || (LegalConsent = {}));
7119
+
7058
7120
  class VariationConfiguration {
7059
7121
  constructor(externalStorage, externalStorageForcedExperimentVariations, externalStorageForcedFeatureVariations, visitorCodeManager, clientConfiguration) {
7060
7122
  this.storage = externalStorage;
@@ -7113,9 +7175,12 @@ class VariationConfiguration {
7113
7175
  }
7114
7176
  return buildExports.Ok(featureFlagVariations);
7115
7177
  }
7116
- getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, track = true, withAssignment = false, }) {
7178
+ getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, legalConsent, track = true, withAssignment = false, }) {
7117
7179
  KameleoonLogger.debug `CALL: VariationConfiguration.getVariation(visitorCode: ${visitorCode}, visitorIdentifier: ${visitorIdentifier}, featureFlag: ${featureFlag}, targetingData: ${targetingData}, packageInfo: ${packageInfo}, clientConfiguration, dataManager, withAssignment: ${withAssignment})`;
7118
7180
  const { rules, featureKey, id: featureFlagId, defaultVariationKey, } = featureFlag;
7181
+ const consent = clientConfiguration.isConsentRequired
7182
+ ? legalConsent
7183
+ : LegalConsent.Given;
7119
7184
  for (const rule of rules) {
7120
7185
  const { segment, experimentId, id, exposition, respoolTime, variationByExposition, } = rule;
7121
7186
  const forcedVariationData = this.getForcedExperimentVariation(visitorCode, rule.experimentId);
@@ -7162,6 +7227,16 @@ class VariationConfiguration {
7162
7227
  });
7163
7228
  KameleoonLogger.debug `Calculated ruleHash: ${ruleHash} for code: ${visitorIdentifier}`;
7164
7229
  if (ruleHash <= exposition) {
7230
+ // Checking if the evaluation is blocked due to the consent policy
7231
+ if (consent == LegalConsent.NotGiven &&
7232
+ rule.type == RuleType.EXPERIMENTATION) {
7233
+ const behaviour = clientConfiguration.consentBlockingBehaviour;
7234
+ if (behaviour == ConsentBlockingBehaviour.PartiallyBlocked) {
7235
+ break;
7236
+ }
7237
+ return buildExports.Err(new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of ${rule} is blocked because consent is not provided for visitor '${visitorCode}'`));
7238
+ }
7239
+ // evaluate with CB scores if applicable
7165
7240
  let variation = this.evaluateCBScores(rule, visitorIdentifier, targetingData);
7166
7241
  if (!variation) {
7167
7242
  const variationHash = Hasher.getHashDouble({
@@ -7523,7 +7598,6 @@ class KameleoonEventSource {
7523
7598
  const VISITOR_CODE_LENGTH = 16;
7524
7599
  const VISITOR_CODE_MAX_LENGTH = 255;
7525
7600
  const DEFAULT_MAX_AGE = 60 * 60 * 24 * 365;
7526
- const ZERO_MAX_AGE = 0;
7527
7601
  const PATH = '/';
7528
7602
 
7529
7603
  /**
@@ -7991,7 +8065,7 @@ class Tracker {
7991
8065
  this.dataManager.storeTrackedData(updatedData);
7992
8066
  }
7993
8067
  else {
7994
- this.dataManager.storeData(updatedData);
8068
+ this.dataManager.storeData(updatedData, false);
7995
8069
  }
7996
8070
  }
7997
8071
  getUnsentVisitorData(visitorCode, isConsentProvided) {
@@ -8343,16 +8417,23 @@ class KameleoonClient {
8343
8417
  const variations = new Map();
8344
8418
  const featureFlags = this.clientConfiguration.featureFlags;
8345
8419
  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) {
8420
+ try {
8421
+ const variation = this._getFeatureVariation({
8422
+ visitorCode,
8423
+ featureKey: featureFlag.featureKey,
8424
+ track,
8425
+ });
8426
+ if (variation.ok &&
8427
+ (!onlyActive || variation.data.key !== OFF_VARIATION_KEY)) {
8353
8428
  variations.set(featureFlag.featureKey, variation.data);
8354
8429
  }
8355
8430
  }
8431
+ catch (err) {
8432
+ if (err instanceof KameleoonError &&
8433
+ err.type !== KameleoonException.FeatureFlagEnvironmentDisabled) {
8434
+ throw err;
8435
+ }
8436
+ }
8356
8437
  }
8357
8438
  KameleoonLogger.info `RETURN: KameleoonClient.getVariations(visitorCode: ${visitorCode}, onlyActive: ${onlyActive}, track: ${track}) -> (variations: ${variations})`;
8358
8439
  return variations;
@@ -8672,25 +8753,16 @@ class KameleoonClient {
8672
8753
  path: PATH,
8673
8754
  });
8674
8755
  }
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
8756
  KameleoonLogger.info `RETURN: KameleoonClient.setUserConsent(visitorCode: ${visitorCode}, consent: ${consent}, setData: ${setData})`;
8686
8757
  }
8687
8758
  updateConsentData(visitorCode, consent) {
8688
8759
  const readResult = this.consentDataStorage.read();
8760
+ const legalConsent = consent ? LegalConsent.Given : LegalConsent.NotGiven;
8689
8761
  if (!readResult.ok) {
8690
8762
  if (readResult.error.type === KameleoonException.StorageEmpty) {
8691
8763
  this.consentDataStorage.write({
8692
8764
  [visitorCode]: {
8693
- consent,
8765
+ consent: legalConsent,
8694
8766
  },
8695
8767
  });
8696
8768
  }
@@ -8698,28 +8770,36 @@ class KameleoonClient {
8698
8770
  }
8699
8771
  const data = readResult.data;
8700
8772
  data[visitorCode] = {
8701
- consent,
8773
+ consent: legalConsent,
8702
8774
  };
8703
8775
  this.consentDataStorage.write(data);
8704
8776
  }
8777
+ getLegalConsent(visitorCode) {
8778
+ KameleoonLogger.debug `CALL: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode})`;
8779
+ let legalConsent;
8780
+ const consentDataResult = this.consentDataStorage.read();
8781
+ legalConsent = consentDataResult.ok
8782
+ ? this.extractLegalConsent(consentDataResult.data[visitorCode])
8783
+ : LegalConsent.Unknown;
8784
+ KameleoonLogger.debug `RETURN: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode}) -> (legalConsent: ${legalConsent})`;
8785
+ return legalConsent;
8786
+ }
8787
+ extractLegalConsent(consentData) {
8788
+ if (consentData === undefined)
8789
+ return LegalConsent.Unknown;
8790
+ if (typeof consentData === 'boolean') {
8791
+ return consentData ? LegalConsent.Given : LegalConsent.NotGiven;
8792
+ }
8793
+ const value = consentData.consent;
8794
+ if (typeof value === 'boolean')
8795
+ return value ? LegalConsent.Given : LegalConsent.NotGiven;
8796
+ return value;
8797
+ }
8705
8798
  _isConsentProvided(visitorCode) {
8706
8799
  KameleoonLogger.debug `CALL: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode})`;
8707
8800
  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
- }
8801
+ const isConsentProvided = !isConsentRequired ||
8802
+ this.getLegalConsent(visitorCode) == LegalConsent.Given;
8723
8803
  KameleoonLogger.debug `RETURN: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode}) -> (isConsentProvided: ${isConsentProvided})`;
8724
8804
  return isConsentProvided;
8725
8805
  }
@@ -8747,22 +8827,32 @@ class KameleoonClient {
8747
8827
  if (!featureFlag.environmentEnabled) {
8748
8828
  continue;
8749
8829
  }
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,
8830
+ try {
8831
+ const evalExp = this._evaluate({
8832
+ visitorCode,
8833
+ featureFlag,
8834
+ track: false,
8835
+ save: false,
8765
8836
  });
8837
+ if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8838
+ activeVariations.push({
8839
+ variationKey: evalExp.variationKey,
8840
+ variationId: evalExp.variationId,
8841
+ experimentId: evalExp.experimentId,
8842
+ featureFlagId: featureFlag.id,
8843
+ featureKey: featureFlag.featureKey,
8844
+ rule: null,
8845
+ isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8846
+ });
8847
+ }
8848
+ }
8849
+ catch (err) {
8850
+ if (err instanceof KameleoonError) {
8851
+ if (err.type != KameleoonException.FeatureFlagEnvironmentDisabled) {
8852
+ KameleoonLogger.error `Unexpected error: ${err}`;
8853
+ }
8854
+ continue;
8855
+ }
8766
8856
  }
8767
8857
  }
8768
8858
  KameleoonLogger.debug `RETURN: KameleoonClient._getActiveFeatureVariations(visitorCode: ${visitorCode}) -> (activeVariations: ${activeVariations})`;
@@ -8783,6 +8873,7 @@ class KameleoonClient {
8783
8873
  else if (this._isVisitorNotInHoldout(visitorCode, track, save, featureFlag, visitorData) &&
8784
8874
  this._isFFUnrestrictedByMEGroup(visitorCode, featureFlag, visitorData)) {
8785
8875
  const visitorIdentifier = this._getCodeForHash(visitorCode, featureFlag.bucketingCustomDataIndex, visitorData);
8876
+ const legalConsent = this.getLegalConsent(visitorCode);
8786
8877
  const variationData = this.variationConfiguration
8787
8878
  .getVariation({
8788
8879
  visitorCode,
@@ -8794,6 +8885,7 @@ class KameleoonClient {
8794
8885
  clientConfiguration: this.clientConfiguration,
8795
8886
  dataManager: this.dataManager,
8796
8887
  packageInfo: this.externalPackageInfo,
8888
+ legalConsent,
8797
8889
  })
8798
8890
  .throw();
8799
8891
  evalExp =
@@ -8912,6 +9004,14 @@ class KameleoonClient {
8912
9004
  }
8913
9005
  KameleoonLogger.debug `CALL: KameleoonClient._isVisitorNotInHoldout(visitorCode: ${visitorCode}, track: ${track}, save: ${save}, featureFlag: ${featureFlag}, visitorData: ${visitorData})`;
8914
9006
  let isNotInHoldout = true;
9007
+ // Checking if the evaluation is blocked due to the consent policy
9008
+ const legalConsent = this.getLegalConsent(visitorCode);
9009
+ if (legalConsent == LegalConsent.NotGiven) {
9010
+ const behaviour = this.clientConfiguration.consentBlockingBehaviour;
9011
+ if (behaviour == ConsentBlockingBehaviour.CompletelyBlocked) {
9012
+ throw new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of holdout is blocked because consent is not provided for visitor '${visitorCode}'`);
9013
+ }
9014
+ }
8915
9015
  const codeForHash = this._getCodeForHash(visitorCode, featureFlag === null || featureFlag === void 0 ? void 0 : featureFlag.bucketingCustomDataIndex, visitorData);
8916
9016
  const holdoutHash = Hasher.getHashDouble({
8917
9017
  visitorIdentifier: codeForHash,