@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.
@@ -247,10 +247,17 @@ class KameleoonError extends Error {
247
247
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
248
248
  break;
249
249
  case KameleoonException.FeatureFlagVariationNotFound:
250
- case KameleoonException.FeatureFlagEnvironmentDisabled:
251
250
  case KameleoonException.FeatureFlagVariableNotFound:
252
251
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
253
252
  break;
253
+ case KameleoonException.FeatureFlagEnvironmentDisabled:
254
+ if (thirdParam !== undefined) {
255
+ this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
256
+ }
257
+ else {
258
+ this.message = secondParam;
259
+ }
260
+ break;
254
261
  case KameleoonException.StorageWrite:
255
262
  case KameleoonException.JSONParse:
256
263
  this.message = ERROR_MESSAGES[type](secondParam);
@@ -306,7 +313,7 @@ var RequestType;
306
313
  RequestType["RemoteData"] = "remoteData";
307
314
  })(RequestType || (RequestType = {}));
308
315
 
309
- const NUMBER_OF_RETRIES = 2;
316
+ const NUMBER_OF_RETRIES = 1;
310
317
  var Header;
311
318
  (function (Header) {
312
319
  Header["UserAgent"] = "User-Agent";
@@ -885,6 +892,9 @@ class UrlProvider {
885
892
  const currentDomain = this.domains[UrlType.DataApi];
886
893
  this.domains[UrlType.DataApi] = currentDomain.replace(/^[^.]+/, subDomain);
887
894
  }
895
+ get dataApiDomain() {
896
+ return this.domains[UrlType.DataApi];
897
+ }
888
898
  getClientConfigurationUrl(timeStamp) {
889
899
  this.isInitialized();
890
900
  const baseUrl = `https://${this.domains[UrlType.ClientConfiguration]}/v3/`;
@@ -1144,6 +1154,11 @@ var ConsentType;
1144
1154
  ConsentType["Required"] = "REQUIRED";
1145
1155
  ConsentType["NotRequired"] = "NOT_REQUIRED";
1146
1156
  })(ConsentType || (ConsentType = {}));
1157
+ var ConsentBlockingBehaviour;
1158
+ (function (ConsentBlockingBehaviour) {
1159
+ ConsentBlockingBehaviour["PartiallyBlocked"] = "PARTIALLY_BLOCK";
1160
+ ConsentBlockingBehaviour["CompletelyBlocked"] = "FULLY_BLOCK";
1161
+ })(ConsentBlockingBehaviour || (ConsentBlockingBehaviour = {}));
1147
1162
 
1148
1163
  class EventManager {
1149
1164
  addEventHandler(eventType, callback) {
@@ -1171,17 +1186,14 @@ var EventType;
1171
1186
  EventType["ConfigurationUpdate"] = "configurationUpdate";
1172
1187
  })(EventType || (EventType = {}));
1173
1188
 
1174
- ({
1175
- configuration: {
1176
- consentType: ConsentType.NotRequired},
1177
- });
1178
- const DEFAULT_CLIENT_CONFIGURATION$1 = {
1189
+ const DEFAULT_DATA_FILE_CONFIGURATION = {
1179
1190
  customData: [],
1180
1191
  featureFlags: [],
1181
1192
  configuration: {
1182
1193
  realTimeUpdate: false,
1183
1194
  consentType: ConsentType.NotRequired,
1184
1195
  dataApiDomain: 'data.kameleoon.io',
1196
+ consentOptOutBehavior: ConsentBlockingBehaviour.PartiallyBlocked,
1185
1197
  },
1186
1198
  };
1187
1199
 
@@ -1205,7 +1217,7 @@ class ClientConfiguration {
1205
1217
  constructor({ updateInterval, urlProvider, storage, requester, dataManager, eventSource, externalVisitorCodeManager, eventManager, externalPackageInfo, defaultDataFile, }) {
1206
1218
  this.updateConfigurationIntervalId = null;
1207
1219
  this.updateType = UpdateType.Polling;
1208
- this.configurationData = DEFAULT_CLIENT_CONFIGURATION$1;
1220
+ this.configurationData = DEFAULT_DATA_FILE_CONFIGURATION;
1209
1221
  this.featureFlagsData = new Map();
1210
1222
  this.isTargetedDeliveryRule = null;
1211
1223
  this.segmentsData = null;
@@ -1214,6 +1226,7 @@ class ClientConfiguration {
1214
1226
  this.mappedRules = null;
1215
1227
  this.mappedExperiments = null;
1216
1228
  this.usedDefaultDataFile = false;
1229
+ this.blockingBehaviourMode = ConsentBlockingBehaviour.PartiallyBlocked;
1217
1230
  this.CACHE_REVALIDATE_PERIOD = 90 * Milliseconds.Minute;
1218
1231
  this.urlProvider = urlProvider;
1219
1232
  this.requester = requester;
@@ -1388,6 +1401,9 @@ class ClientConfiguration {
1388
1401
  get isConsentRequired() {
1389
1402
  return this.configuration.consentType === ConsentType.Required;
1390
1403
  }
1404
+ get consentBlockingBehaviour() {
1405
+ return this.blockingBehaviourMode;
1406
+ }
1391
1407
  get hasAnyTargetedDeliveryRule() {
1392
1408
  if (this.isTargetedDeliveryRule !== null) {
1393
1409
  return this.isTargetedDeliveryRule;
@@ -1516,6 +1532,7 @@ class ClientConfiguration {
1516
1532
  KameleoonLogger.info `Configuration update type was toggled to ${UpdateType[updateType]}`;
1517
1533
  yield this.initialize();
1518
1534
  }
1535
+ this.blockingBehaviourMode = this.consentBlockingBehaviourFromStr(clientConfigurationData.configuration.consentOptOutBehavior);
1519
1536
  return buildExports.Ok(toggleUpdateType);
1520
1537
  });
1521
1538
  }
@@ -1622,6 +1639,14 @@ class ClientConfiguration {
1622
1639
  this.visitorCodeManager.consentRequired =
1623
1640
  this.isConsentRequired && !this.hasAnyTargetedDeliveryRule;
1624
1641
  }
1642
+ consentBlockingBehaviourFromStr(str) {
1643
+ if (str === ConsentBlockingBehaviour.PartiallyBlocked ||
1644
+ str === ConsentBlockingBehaviour.CompletelyBlocked) {
1645
+ return str;
1646
+ }
1647
+ KameleoonLogger.error(`Unexpected consent blocking type '${str}'`);
1648
+ return ConsentBlockingBehaviour.PartiallyBlocked;
1649
+ }
1625
1650
  }
1626
1651
 
1627
1652
  function constructTypeMap(indexMap) {
@@ -2416,6 +2441,13 @@ let CustomData$1 = class CustomData {
2416
2441
  get name() {
2417
2442
  return this._name;
2418
2443
  }
2444
+ /**
2445
+ * @private
2446
+ * @method name - an internal getter for a name of custom data
2447
+ * */
2448
+ get values() {
2449
+ return this.value;
2450
+ }
2419
2451
  };
2420
2452
  CustomData$1.UNDEFINED_INDEX = -1;
2421
2453
 
@@ -2433,7 +2465,7 @@ let Conversion$1 = class Conversion {
2433
2465
  this.negative = negative;
2434
2466
  this.status = TrackingStatus.Unsent;
2435
2467
  this.id = Math.floor(Math.random() * 1000000);
2436
- this.metadata = metadata;
2468
+ this._metadata = metadata;
2437
2469
  }
2438
2470
  set _id(id) {
2439
2471
  this.id = id;
@@ -2454,9 +2486,21 @@ let Conversion$1 = class Conversion {
2454
2486
  ? UrlParameter.Metadata + this._encodeMetadata()
2455
2487
  : ''));
2456
2488
  }
2489
+ /**
2490
+ * @private
2491
+ * @method metadata - an internal getter for a metadata of conversion
2492
+ * */
2457
2493
  get _metadata() {
2458
2494
  return this.metadata;
2459
2495
  }
2496
+ /**
2497
+ * @private
2498
+ * @method metadata - an internal setter for setting metadata of conversion
2499
+ * @param {number} value - an index value
2500
+ * */
2501
+ set _metadata(value) {
2502
+ this.metadata = value;
2503
+ }
2460
2504
  get data() {
2461
2505
  var _a;
2462
2506
  return {
@@ -3549,7 +3593,7 @@ class DataProcessor {
3549
3593
  this.cleanupInterval = cleanupInterval;
3550
3594
  this.packageInfo = packageInfo;
3551
3595
  }
3552
- mutUpdateData({ infoData, visitorCode, mutData, dataItem, }) {
3596
+ mutUpdateData({ infoData, visitorCode, mutData, dataItem, extendTtl, }) {
3553
3597
  let { visitorReference, data, isReference } = this.dereferenceData(mutData, visitorCode);
3554
3598
  if (this.packageInfo.isServer &&
3555
3599
  isReference &&
@@ -3560,9 +3604,12 @@ class DataProcessor {
3560
3604
  delete mutData[visitorCode];
3561
3605
  visitorReference = visitorCode;
3562
3606
  }
3563
- const expirationTime = this.cleanupInterval
3564
- ? Date.now() + this.cleanupInterval
3565
- : 0;
3607
+ let expirationTime;
3608
+ if (extendTtl) {
3609
+ expirationTime = this.cleanupInterval
3610
+ ? Date.now() + this.cleanupInterval
3611
+ : 0;
3612
+ }
3566
3613
  switch (dataItem.data.type) {
3567
3614
  case KameleoonData.CustomData: {
3568
3615
  this.updateCustomData({
@@ -3917,13 +3964,17 @@ class DataProcessor {
3917
3964
  return closestCleanupTime;
3918
3965
  }
3919
3966
  updateField({ key, value, data, visitorCode, expirationTime, }) {
3920
- data[visitorCode][key] = Object.assign(Object.assign({}, value), { expirationTime });
3967
+ var _a;
3968
+ const existing = data[visitorCode][key];
3969
+ 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() });
3921
3970
  }
3922
3971
  createField({ key, value, data, visitorCode, expirationTime, }) {
3923
3972
  data[visitorCode] = Object.assign(Object.assign({}, data[visitorCode]), { [key]: Object.assign(Object.assign({}, value), { expirationTime }) });
3924
3973
  }
3925
3974
  updateNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3926
- data[visitorCode][key][nestedKey] = Object.assign(Object.assign({}, value), { expirationTime });
3975
+ var _a;
3976
+ const existing = data[visitorCode][key][nestedKey];
3977
+ 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() });
3927
3978
  }
3928
3979
  createNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3929
3980
  var _a;
@@ -5189,15 +5240,7 @@ var KameleoonStorageKey;
5189
5240
  KameleoonStorageKey["ForcedExperimentVariation"] = "kameleoonForcedExperimentVariation";
5190
5241
  })(KameleoonStorageKey || (KameleoonStorageKey = {}));
5191
5242
  const DEFAULT_CLIENT_CONFIGURATION = {
5192
- data: {
5193
- customData: [],
5194
- featureFlags: [],
5195
- configuration: {
5196
- realTimeUpdate: false,
5197
- consentType: ConsentType.NotRequired,
5198
- dataApiDomain: 'data.kameleoon.io',
5199
- },
5200
- },
5243
+ data: DEFAULT_DATA_FILE_CONFIGURATION,
5201
5244
  lastUpdate: '',
5202
5245
  };
5203
5246
  // --- Note ---
@@ -5709,7 +5752,7 @@ class DataManager {
5709
5752
  return resultData;
5710
5753
  }
5711
5754
  storeTrackedData(data) {
5712
- this.storeData(data);
5755
+ this.storeData(data, false);
5713
5756
  const infoResult = this.infoStorage.read();
5714
5757
  if (!infoResult.ok) {
5715
5758
  return;
@@ -5761,15 +5804,18 @@ class DataManager {
5761
5804
  targetingData,
5762
5805
  visitorCode: firstParameter,
5763
5806
  kameleoonData: secondParameter,
5807
+ extendTtl: true,
5764
5808
  });
5765
5809
  }
5766
5810
  else {
5767
5811
  for (const [visitorCode, kameleoonData] of Object.entries(firstParameter)) {
5812
+ const extendTtl = typeof secondParameter[0] === 'boolean' ? secondParameter[0] : true;
5768
5813
  this.mutUpdateTargetingData({
5769
5814
  infoData,
5770
5815
  targetingData,
5771
5816
  visitorCode,
5772
5817
  kameleoonData,
5818
+ extendTtl,
5773
5819
  });
5774
5820
  }
5775
5821
  }
@@ -5886,8 +5932,8 @@ class DataManager {
5886
5932
  }
5887
5933
  return null;
5888
5934
  }
5889
- mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, }) {
5890
- var _a;
5935
+ mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, extendTtl, }) {
5936
+ var _a, _b, _c;
5891
5937
  for (const dataItem of kameleoonData) {
5892
5938
  // process custom data
5893
5939
  if (dataItem.data.type === KameleoonData.CustomData) {
@@ -5903,16 +5949,21 @@ class DataManager {
5903
5949
  }
5904
5950
  // process metadata of conversions
5905
5951
  if (dataItem.data.type === KameleoonData.Conversion) {
5906
- (_a = dataItem._metadata) === null || _a === void 0 ? void 0 : _a.forEach((item) => this.trySetCustomDataIndexByName(item));
5952
+ const conversion = dataItem;
5953
+ if (((_b = (_a = conversion._metadata) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
5954
+ conversion._metadata = (_c = conversion._metadata) === null || _c === void 0 ? void 0 : _c.filter((item) => this.trySetCustomDataIndexByName(item));
5955
+ }
5907
5956
  }
5908
5957
  const expirationTime = this.dataProcessor.mutUpdateData({
5909
5958
  infoData,
5910
5959
  visitorCode,
5911
5960
  mutData: targetingData,
5912
5961
  dataItem,
5962
+ extendTtl,
5913
5963
  });
5914
5964
  const nextCleanup = infoData.nextDataCleanup;
5915
- if (!nextCleanup || (nextCleanup && expirationTime < nextCleanup)) {
5965
+ if (!nextCleanup ||
5966
+ (nextCleanup && expirationTime && expirationTime < nextCleanup)) {
5916
5967
  infoData.nextDataCleanup = expirationTime;
5917
5968
  }
5918
5969
  if (dataItem.data.status === TrackingStatus.Unsent) {
@@ -5944,7 +5995,9 @@ class DataManager {
5944
5995
  var _a;
5945
5996
  const { data } = customData;
5946
5997
  const isDataValid = Boolean(data.value.length && data.value[0].length);
5947
- this.trySetCustomDataIndexByName(customData);
5998
+ if (!this.trySetCustomDataIndexByName(customData)) {
5999
+ return false;
6000
+ }
5948
6001
  if (data.index == CustomData$1.UNDEFINED_INDEX) {
5949
6002
  data.index = customData.index;
5950
6003
  }
@@ -5974,13 +6027,15 @@ class DataManager {
5974
6027
  return true;
5975
6028
  }
5976
6029
  trySetCustomDataIndexByName(customData) {
5977
- if (customData.index == CustomData$1.UNDEFINED_INDEX && customData.name) {
5978
- const cdIndex = this.customDataIndexByName.get(customData.name);
5979
- if (cdIndex === undefined) {
5980
- return;
5981
- }
5982
- customData.index = cdIndex;
5983
- }
6030
+ if (customData.index !== CustomData$1.UNDEFINED_INDEX)
6031
+ return true;
6032
+ if (!customData.name)
6033
+ return false;
6034
+ const cdIndex = this.customDataIndexByName.get(customData.name);
6035
+ if (cdIndex == null)
6036
+ return false;
6037
+ customData.index = cdIndex;
6038
+ return true;
5984
6039
  }
5985
6040
  get unsentDataVisitors() {
5986
6041
  const infoResult = this.infoStorage.read();
@@ -7093,6 +7148,13 @@ class Hasher {
7093
7148
  }
7094
7149
  }
7095
7150
 
7151
+ var LegalConsent;
7152
+ (function (LegalConsent) {
7153
+ LegalConsent[LegalConsent["Unknown"] = 0] = "Unknown";
7154
+ LegalConsent[LegalConsent["Given"] = 1] = "Given";
7155
+ LegalConsent[LegalConsent["NotGiven"] = 2] = "NotGiven";
7156
+ })(LegalConsent || (LegalConsent = {}));
7157
+
7096
7158
  class VariationConfiguration {
7097
7159
  constructor(externalStorage, externalStorageForcedExperimentVariations, externalStorageForcedFeatureVariations, visitorCodeManager, clientConfiguration) {
7098
7160
  this.storage = externalStorage;
@@ -7151,9 +7213,12 @@ class VariationConfiguration {
7151
7213
  }
7152
7214
  return buildExports.Ok(featureFlagVariations);
7153
7215
  }
7154
- getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, track = true, withAssignment = false, }) {
7216
+ getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, legalConsent, track = true, withAssignment = false, }) {
7155
7217
  KameleoonLogger.debug `CALL: VariationConfiguration.getVariation(visitorCode: ${visitorCode}, visitorIdentifier: ${visitorIdentifier}, featureFlag: ${featureFlag}, targetingData: ${targetingData}, packageInfo: ${packageInfo}, clientConfiguration, dataManager, withAssignment: ${withAssignment})`;
7156
7218
  const { rules, featureKey, id: featureFlagId, defaultVariationKey, } = featureFlag;
7219
+ const consent = clientConfiguration.isConsentRequired
7220
+ ? legalConsent
7221
+ : LegalConsent.Given;
7157
7222
  for (const rule of rules) {
7158
7223
  const { segment, experimentId, id, exposition, respoolTime, variationByExposition, } = rule;
7159
7224
  const forcedVariationData = this.getForcedExperimentVariation(visitorCode, rule.experimentId);
@@ -7200,6 +7265,16 @@ class VariationConfiguration {
7200
7265
  });
7201
7266
  KameleoonLogger.debug `Calculated ruleHash: ${ruleHash} for code: ${visitorIdentifier}`;
7202
7267
  if (ruleHash <= exposition) {
7268
+ // Checking if the evaluation is blocked due to the consent policy
7269
+ if (consent == LegalConsent.NotGiven &&
7270
+ rule.type == RuleType.EXPERIMENTATION) {
7271
+ const behaviour = clientConfiguration.consentBlockingBehaviour;
7272
+ if (behaviour == ConsentBlockingBehaviour.PartiallyBlocked) {
7273
+ break;
7274
+ }
7275
+ return buildExports.Err(new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of ${rule} is blocked because consent is not provided for visitor '${visitorCode}'`));
7276
+ }
7277
+ // evaluate with CB scores if applicable
7203
7278
  let variation = this.evaluateCBScores(rule, visitorIdentifier, targetingData);
7204
7279
  if (!variation) {
7205
7280
  const variationHash = Hasher.getHashDouble({
@@ -7561,7 +7636,6 @@ class KameleoonEventSource {
7561
7636
  const VISITOR_CODE_LENGTH = 16;
7562
7637
  const VISITOR_CODE_MAX_LENGTH = 255;
7563
7638
  const DEFAULT_MAX_AGE = 60 * 60 * 24 * 365;
7564
- const ZERO_MAX_AGE = 0;
7565
7639
  const PATH = '/';
7566
7640
 
7567
7641
  /**
@@ -8029,7 +8103,7 @@ class Tracker {
8029
8103
  this.dataManager.storeTrackedData(updatedData);
8030
8104
  }
8031
8105
  else {
8032
- this.dataManager.storeData(updatedData);
8106
+ this.dataManager.storeData(updatedData, false);
8033
8107
  }
8034
8108
  }
8035
8109
  getUnsentVisitorData(visitorCode, isConsentProvided) {
@@ -8381,16 +8455,23 @@ class KameleoonClient {
8381
8455
  const variations = new Map();
8382
8456
  const featureFlags = this.clientConfiguration.featureFlags;
8383
8457
  for (const featureFlag of featureFlags.values()) {
8384
- const variation = this._getFeatureVariation({
8385
- visitorCode,
8386
- featureKey: featureFlag.featureKey,
8387
- track,
8388
- });
8389
- if (variation.ok) {
8390
- if (!onlyActive || variation.data.key !== OFF_VARIATION_KEY) {
8458
+ try {
8459
+ const variation = this._getFeatureVariation({
8460
+ visitorCode,
8461
+ featureKey: featureFlag.featureKey,
8462
+ track,
8463
+ });
8464
+ if (variation.ok &&
8465
+ (!onlyActive || variation.data.key !== OFF_VARIATION_KEY)) {
8391
8466
  variations.set(featureFlag.featureKey, variation.data);
8392
8467
  }
8393
8468
  }
8469
+ catch (err) {
8470
+ if (err instanceof KameleoonError &&
8471
+ err.type !== KameleoonException.FeatureFlagEnvironmentDisabled) {
8472
+ throw err;
8473
+ }
8474
+ }
8394
8475
  }
8395
8476
  KameleoonLogger.info `RETURN: KameleoonClient.getVariations(visitorCode: ${visitorCode}, onlyActive: ${onlyActive}, track: ${track}) -> (variations: ${variations})`;
8396
8477
  return variations;
@@ -8710,25 +8791,16 @@ class KameleoonClient {
8710
8791
  path: PATH,
8711
8792
  });
8712
8793
  }
8713
- else {
8714
- if (this.visitorCodeManager.consentRequired) {
8715
- setData({
8716
- visitorCode: '',
8717
- key: KameleoonStorageKey.VisitorCode,
8718
- maxAge: ZERO_MAX_AGE,
8719
- path: PATH,
8720
- });
8721
- }
8722
- }
8723
8794
  KameleoonLogger.info `RETURN: KameleoonClient.setUserConsent(visitorCode: ${visitorCode}, consent: ${consent}, setData: ${setData})`;
8724
8795
  }
8725
8796
  updateConsentData(visitorCode, consent) {
8726
8797
  const readResult = this.consentDataStorage.read();
8798
+ const legalConsent = consent ? LegalConsent.Given : LegalConsent.NotGiven;
8727
8799
  if (!readResult.ok) {
8728
8800
  if (readResult.error.type === KameleoonException.StorageEmpty) {
8729
8801
  this.consentDataStorage.write({
8730
8802
  [visitorCode]: {
8731
- consent,
8803
+ consent: legalConsent,
8732
8804
  },
8733
8805
  });
8734
8806
  }
@@ -8736,28 +8808,36 @@ class KameleoonClient {
8736
8808
  }
8737
8809
  const data = readResult.data;
8738
8810
  data[visitorCode] = {
8739
- consent,
8811
+ consent: legalConsent,
8740
8812
  };
8741
8813
  this.consentDataStorage.write(data);
8742
8814
  }
8815
+ getLegalConsent(visitorCode) {
8816
+ KameleoonLogger.debug `CALL: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode})`;
8817
+ let legalConsent;
8818
+ const consentDataResult = this.consentDataStorage.read();
8819
+ legalConsent = consentDataResult.ok
8820
+ ? this.extractLegalConsent(consentDataResult.data[visitorCode])
8821
+ : LegalConsent.Unknown;
8822
+ KameleoonLogger.debug `RETURN: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode}) -> (legalConsent: ${legalConsent})`;
8823
+ return legalConsent;
8824
+ }
8825
+ extractLegalConsent(consentData) {
8826
+ if (consentData === undefined)
8827
+ return LegalConsent.Unknown;
8828
+ if (typeof consentData === 'boolean') {
8829
+ return consentData ? LegalConsent.Given : LegalConsent.NotGiven;
8830
+ }
8831
+ const value = consentData.consent;
8832
+ if (typeof value === 'boolean')
8833
+ return value ? LegalConsent.Given : LegalConsent.NotGiven;
8834
+ return value;
8835
+ }
8743
8836
  _isConsentProvided(visitorCode) {
8744
8837
  KameleoonLogger.debug `CALL: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode})`;
8745
8838
  const { isConsentRequired } = this.clientConfiguration;
8746
- const consentDataResult = this.consentDataStorage.read();
8747
- let isConsentProvided = false;
8748
- if (!isConsentRequired) {
8749
- isConsentProvided = true;
8750
- }
8751
- else if (consentDataResult.ok) {
8752
- const consentData = consentDataResult.data[visitorCode];
8753
- // for consistency with the old data in the storage
8754
- if (typeof consentData === 'boolean') {
8755
- isConsentProvided = consentData;
8756
- }
8757
- else {
8758
- isConsentProvided = consentData && consentData.consent;
8759
- }
8760
- }
8839
+ const isConsentProvided = !isConsentRequired ||
8840
+ this.getLegalConsent(visitorCode) == LegalConsent.Given;
8761
8841
  KameleoonLogger.debug `RETURN: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode}) -> (isConsentProvided: ${isConsentProvided})`;
8762
8842
  return isConsentProvided;
8763
8843
  }
@@ -8785,22 +8865,32 @@ class KameleoonClient {
8785
8865
  if (!featureFlag.environmentEnabled) {
8786
8866
  continue;
8787
8867
  }
8788
- const evalExp = this._evaluate({
8789
- visitorCode,
8790
- featureFlag,
8791
- track: false,
8792
- save: false,
8793
- });
8794
- if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8795
- activeVariations.push({
8796
- variationKey: evalExp.variationKey,
8797
- variationId: evalExp.variationId,
8798
- experimentId: evalExp.experimentId,
8799
- featureFlagId: featureFlag.id,
8800
- featureKey: featureFlag.featureKey,
8801
- rule: null,
8802
- isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8868
+ try {
8869
+ const evalExp = this._evaluate({
8870
+ visitorCode,
8871
+ featureFlag,
8872
+ track: false,
8873
+ save: false,
8803
8874
  });
8875
+ if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8876
+ activeVariations.push({
8877
+ variationKey: evalExp.variationKey,
8878
+ variationId: evalExp.variationId,
8879
+ experimentId: evalExp.experimentId,
8880
+ featureFlagId: featureFlag.id,
8881
+ featureKey: featureFlag.featureKey,
8882
+ rule: null,
8883
+ isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8884
+ });
8885
+ }
8886
+ }
8887
+ catch (err) {
8888
+ if (err instanceof KameleoonError) {
8889
+ if (err.type != KameleoonException.FeatureFlagEnvironmentDisabled) {
8890
+ KameleoonLogger.error `Unexpected error: ${err}`;
8891
+ }
8892
+ continue;
8893
+ }
8804
8894
  }
8805
8895
  }
8806
8896
  KameleoonLogger.debug `RETURN: KameleoonClient._getActiveFeatureVariations(visitorCode: ${visitorCode}) -> (activeVariations: ${activeVariations})`;
@@ -8821,6 +8911,7 @@ class KameleoonClient {
8821
8911
  else if (this._isVisitorNotInHoldout(visitorCode, track, save, featureFlag, visitorData) &&
8822
8912
  this._isFFUnrestrictedByMEGroup(visitorCode, featureFlag, visitorData)) {
8823
8913
  const visitorIdentifier = this._getCodeForHash(visitorCode, featureFlag.bucketingCustomDataIndex, visitorData);
8914
+ const legalConsent = this.getLegalConsent(visitorCode);
8824
8915
  const variationData = this.variationConfiguration
8825
8916
  .getVariation({
8826
8917
  visitorCode,
@@ -8832,6 +8923,7 @@ class KameleoonClient {
8832
8923
  clientConfiguration: this.clientConfiguration,
8833
8924
  dataManager: this.dataManager,
8834
8925
  packageInfo: this.externalPackageInfo,
8926
+ legalConsent,
8835
8927
  })
8836
8928
  .throw();
8837
8929
  evalExp =
@@ -8950,6 +9042,14 @@ class KameleoonClient {
8950
9042
  }
8951
9043
  KameleoonLogger.debug `CALL: KameleoonClient._isVisitorNotInHoldout(visitorCode: ${visitorCode}, track: ${track}, save: ${save}, featureFlag: ${featureFlag}, visitorData: ${visitorData})`;
8952
9044
  let isNotInHoldout = true;
9045
+ // Checking if the evaluation is blocked due to the consent policy
9046
+ const legalConsent = this.getLegalConsent(visitorCode);
9047
+ if (legalConsent == LegalConsent.NotGiven) {
9048
+ const behaviour = this.clientConfiguration.consentBlockingBehaviour;
9049
+ if (behaviour == ConsentBlockingBehaviour.CompletelyBlocked) {
9050
+ throw new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of holdout is blocked because consent is not provided for visitor '${visitorCode}'`);
9051
+ }
9052
+ }
8953
9053
  const codeForHash = this._getCodeForHash(visitorCode, featureFlag === null || featureFlag === void 0 ? void 0 : featureFlag.bucketingCustomDataIndex, visitorData);
8954
9054
  const holdoutHash = Hasher.getHashDouble({
8955
9055
  visitorIdentifier: codeForHash,