@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.
@@ -220,10 +220,17 @@ class KameleoonError extends Error {
220
220
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
221
221
  break;
222
222
  case exports.KameleoonException.FeatureFlagVariationNotFound:
223
- case exports.KameleoonException.FeatureFlagEnvironmentDisabled:
224
223
  case exports.KameleoonException.FeatureFlagVariableNotFound:
225
224
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
226
225
  break;
226
+ case exports.KameleoonException.FeatureFlagEnvironmentDisabled:
227
+ if (thirdParam !== undefined) {
228
+ this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
229
+ }
230
+ else {
231
+ this.message = secondParam;
232
+ }
233
+ break;
227
234
  case exports.KameleoonException.StorageWrite:
228
235
  case exports.KameleoonException.JSONParse:
229
236
  this.message = ERROR_MESSAGES[type](secondParam);
@@ -279,7 +286,7 @@ exports.RequestType = void 0;
279
286
  RequestType["RemoteData"] = "remoteData";
280
287
  })(exports.RequestType || (exports.RequestType = {}));
281
288
 
282
- const NUMBER_OF_RETRIES = 2;
289
+ const NUMBER_OF_RETRIES = 1;
283
290
  exports.Header = void 0;
284
291
  (function (Header) {
285
292
  Header["UserAgent"] = "User-Agent";
@@ -858,6 +865,9 @@ class UrlProvider {
858
865
  const currentDomain = this.domains[UrlType.DataApi];
859
866
  this.domains[UrlType.DataApi] = currentDomain.replace(/^[^.]+/, subDomain);
860
867
  }
868
+ get dataApiDomain() {
869
+ return this.domains[UrlType.DataApi];
870
+ }
861
871
  getClientConfigurationUrl(timeStamp) {
862
872
  this.isInitialized();
863
873
  const baseUrl = `https://${this.domains[UrlType.ClientConfiguration]}/v3/`;
@@ -1117,6 +1127,11 @@ var ConsentType;
1117
1127
  ConsentType["Required"] = "REQUIRED";
1118
1128
  ConsentType["NotRequired"] = "NOT_REQUIRED";
1119
1129
  })(ConsentType || (ConsentType = {}));
1130
+ var ConsentBlockingBehaviour;
1131
+ (function (ConsentBlockingBehaviour) {
1132
+ ConsentBlockingBehaviour["PartiallyBlocked"] = "PARTIALLY_BLOCK";
1133
+ ConsentBlockingBehaviour["CompletelyBlocked"] = "FULLY_BLOCK";
1134
+ })(ConsentBlockingBehaviour || (ConsentBlockingBehaviour = {}));
1120
1135
 
1121
1136
  class EventManager {
1122
1137
  addEventHandler(eventType, callback) {
@@ -1144,17 +1159,14 @@ exports.EventType = void 0;
1144
1159
  EventType["ConfigurationUpdate"] = "configurationUpdate";
1145
1160
  })(exports.EventType || (exports.EventType = {}));
1146
1161
 
1147
- ({
1148
- configuration: {
1149
- consentType: ConsentType.NotRequired},
1150
- });
1151
- const DEFAULT_CLIENT_CONFIGURATION$1 = {
1162
+ const DEFAULT_DATA_FILE_CONFIGURATION = {
1152
1163
  customData: [],
1153
1164
  featureFlags: [],
1154
1165
  configuration: {
1155
1166
  realTimeUpdate: false,
1156
1167
  consentType: ConsentType.NotRequired,
1157
1168
  dataApiDomain: 'data.kameleoon.io',
1169
+ consentOptOutBehavior: ConsentBlockingBehaviour.PartiallyBlocked,
1158
1170
  },
1159
1171
  };
1160
1172
 
@@ -1178,7 +1190,7 @@ class ClientConfiguration {
1178
1190
  constructor({ updateInterval, urlProvider, storage, requester, dataManager, eventSource, externalVisitorCodeManager, eventManager, externalPackageInfo, defaultDataFile, }) {
1179
1191
  this.updateConfigurationIntervalId = null;
1180
1192
  this.updateType = UpdateType.Polling;
1181
- this.configurationData = DEFAULT_CLIENT_CONFIGURATION$1;
1193
+ this.configurationData = DEFAULT_DATA_FILE_CONFIGURATION;
1182
1194
  this.featureFlagsData = new Map();
1183
1195
  this.isTargetedDeliveryRule = null;
1184
1196
  this.segmentsData = null;
@@ -1187,6 +1199,7 @@ class ClientConfiguration {
1187
1199
  this.mappedRules = null;
1188
1200
  this.mappedExperiments = null;
1189
1201
  this.usedDefaultDataFile = false;
1202
+ this.blockingBehaviourMode = ConsentBlockingBehaviour.PartiallyBlocked;
1190
1203
  this.CACHE_REVALIDATE_PERIOD = 90 * exports.Milliseconds.Minute;
1191
1204
  this.urlProvider = urlProvider;
1192
1205
  this.requester = requester;
@@ -1361,6 +1374,9 @@ class ClientConfiguration {
1361
1374
  get isConsentRequired() {
1362
1375
  return this.configuration.consentType === ConsentType.Required;
1363
1376
  }
1377
+ get consentBlockingBehaviour() {
1378
+ return this.blockingBehaviourMode;
1379
+ }
1364
1380
  get hasAnyTargetedDeliveryRule() {
1365
1381
  if (this.isTargetedDeliveryRule !== null) {
1366
1382
  return this.isTargetedDeliveryRule;
@@ -1489,6 +1505,7 @@ class ClientConfiguration {
1489
1505
  KameleoonLogger.info `Configuration update type was toggled to ${UpdateType[updateType]}`;
1490
1506
  yield this.initialize();
1491
1507
  }
1508
+ this.blockingBehaviourMode = this.consentBlockingBehaviourFromStr(clientConfigurationData.configuration.consentOptOutBehavior);
1492
1509
  return buildExports.Ok(toggleUpdateType);
1493
1510
  });
1494
1511
  }
@@ -1595,6 +1612,14 @@ class ClientConfiguration {
1595
1612
  this.visitorCodeManager.consentRequired =
1596
1613
  this.isConsentRequired && !this.hasAnyTargetedDeliveryRule;
1597
1614
  }
1615
+ consentBlockingBehaviourFromStr(str) {
1616
+ if (str === ConsentBlockingBehaviour.PartiallyBlocked ||
1617
+ str === ConsentBlockingBehaviour.CompletelyBlocked) {
1618
+ return str;
1619
+ }
1620
+ KameleoonLogger.error(`Unexpected consent blocking type '${str}'`);
1621
+ return ConsentBlockingBehaviour.PartiallyBlocked;
1622
+ }
1598
1623
  }
1599
1624
 
1600
1625
  function constructTypeMap(indexMap) {
@@ -2389,6 +2414,13 @@ let CustomData$1 = class CustomData {
2389
2414
  get name() {
2390
2415
  return this._name;
2391
2416
  }
2417
+ /**
2418
+ * @private
2419
+ * @method name - an internal getter for a name of custom data
2420
+ * */
2421
+ get values() {
2422
+ return this.value;
2423
+ }
2392
2424
  };
2393
2425
  CustomData$1.UNDEFINED_INDEX = -1;
2394
2426
 
@@ -2406,7 +2438,7 @@ let Conversion$1 = class Conversion {
2406
2438
  this.negative = negative;
2407
2439
  this.status = exports.TrackingStatus.Unsent;
2408
2440
  this.id = Math.floor(Math.random() * 1000000);
2409
- this.metadata = metadata;
2441
+ this._metadata = metadata;
2410
2442
  }
2411
2443
  set _id(id) {
2412
2444
  this.id = id;
@@ -2427,9 +2459,21 @@ let Conversion$1 = class Conversion {
2427
2459
  ? UrlParameter.Metadata + this._encodeMetadata()
2428
2460
  : ''));
2429
2461
  }
2462
+ /**
2463
+ * @private
2464
+ * @method metadata - an internal getter for a metadata of conversion
2465
+ * */
2430
2466
  get _metadata() {
2431
2467
  return this.metadata;
2432
2468
  }
2469
+ /**
2470
+ * @private
2471
+ * @method metadata - an internal setter for setting metadata of conversion
2472
+ * @param {number} value - an index value
2473
+ * */
2474
+ set _metadata(value) {
2475
+ this.metadata = value;
2476
+ }
2433
2477
  get data() {
2434
2478
  var _a;
2435
2479
  return {
@@ -3522,7 +3566,7 @@ class DataProcessor {
3522
3566
  this.cleanupInterval = cleanupInterval;
3523
3567
  this.packageInfo = packageInfo;
3524
3568
  }
3525
- mutUpdateData({ infoData, visitorCode, mutData, dataItem, }) {
3569
+ mutUpdateData({ infoData, visitorCode, mutData, dataItem, extendTtl, }) {
3526
3570
  let { visitorReference, data, isReference } = this.dereferenceData(mutData, visitorCode);
3527
3571
  if (this.packageInfo.isServer &&
3528
3572
  isReference &&
@@ -3533,9 +3577,12 @@ class DataProcessor {
3533
3577
  delete mutData[visitorCode];
3534
3578
  visitorReference = visitorCode;
3535
3579
  }
3536
- const expirationTime = this.cleanupInterval
3537
- ? Date.now() + this.cleanupInterval
3538
- : 0;
3580
+ let expirationTime;
3581
+ if (extendTtl) {
3582
+ expirationTime = this.cleanupInterval
3583
+ ? Date.now() + this.cleanupInterval
3584
+ : 0;
3585
+ }
3539
3586
  switch (dataItem.data.type) {
3540
3587
  case exports.KameleoonData.CustomData: {
3541
3588
  this.updateCustomData({
@@ -3890,13 +3937,17 @@ class DataProcessor {
3890
3937
  return closestCleanupTime;
3891
3938
  }
3892
3939
  updateField({ key, value, data, visitorCode, expirationTime, }) {
3893
- data[visitorCode][key] = Object.assign(Object.assign({}, value), { expirationTime });
3940
+ var _a;
3941
+ const existing = data[visitorCode][key];
3942
+ 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() });
3894
3943
  }
3895
3944
  createField({ key, value, data, visitorCode, expirationTime, }) {
3896
3945
  data[visitorCode] = Object.assign(Object.assign({}, data[visitorCode]), { [key]: Object.assign(Object.assign({}, value), { expirationTime }) });
3897
3946
  }
3898
3947
  updateNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3899
- data[visitorCode][key][nestedKey] = Object.assign(Object.assign({}, value), { expirationTime });
3948
+ var _a;
3949
+ const existing = data[visitorCode][key][nestedKey];
3950
+ 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() });
3900
3951
  }
3901
3952
  createNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3902
3953
  var _a;
@@ -5162,15 +5213,7 @@ exports.KameleoonStorageKey = void 0;
5162
5213
  KameleoonStorageKey["ForcedExperimentVariation"] = "kameleoonForcedExperimentVariation";
5163
5214
  })(exports.KameleoonStorageKey || (exports.KameleoonStorageKey = {}));
5164
5215
  const DEFAULT_CLIENT_CONFIGURATION = {
5165
- data: {
5166
- customData: [],
5167
- featureFlags: [],
5168
- configuration: {
5169
- realTimeUpdate: false,
5170
- consentType: ConsentType.NotRequired,
5171
- dataApiDomain: 'data.kameleoon.io',
5172
- },
5173
- },
5216
+ data: DEFAULT_DATA_FILE_CONFIGURATION,
5174
5217
  lastUpdate: '',
5175
5218
  };
5176
5219
  // --- Note ---
@@ -5682,7 +5725,7 @@ class DataManager {
5682
5725
  return resultData;
5683
5726
  }
5684
5727
  storeTrackedData(data) {
5685
- this.storeData(data);
5728
+ this.storeData(data, false);
5686
5729
  const infoResult = this.infoStorage.read();
5687
5730
  if (!infoResult.ok) {
5688
5731
  return;
@@ -5734,15 +5777,18 @@ class DataManager {
5734
5777
  targetingData,
5735
5778
  visitorCode: firstParameter,
5736
5779
  kameleoonData: secondParameter,
5780
+ extendTtl: true,
5737
5781
  });
5738
5782
  }
5739
5783
  else {
5740
5784
  for (const [visitorCode, kameleoonData] of Object.entries(firstParameter)) {
5785
+ const extendTtl = typeof secondParameter[0] === 'boolean' ? secondParameter[0] : true;
5741
5786
  this.mutUpdateTargetingData({
5742
5787
  infoData,
5743
5788
  targetingData,
5744
5789
  visitorCode,
5745
5790
  kameleoonData,
5791
+ extendTtl,
5746
5792
  });
5747
5793
  }
5748
5794
  }
@@ -5859,8 +5905,8 @@ class DataManager {
5859
5905
  }
5860
5906
  return null;
5861
5907
  }
5862
- mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, }) {
5863
- var _a;
5908
+ mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, extendTtl, }) {
5909
+ var _a, _b, _c;
5864
5910
  for (const dataItem of kameleoonData) {
5865
5911
  // process custom data
5866
5912
  if (dataItem.data.type === exports.KameleoonData.CustomData) {
@@ -5876,16 +5922,21 @@ class DataManager {
5876
5922
  }
5877
5923
  // process metadata of conversions
5878
5924
  if (dataItem.data.type === exports.KameleoonData.Conversion) {
5879
- (_a = dataItem._metadata) === null || _a === void 0 ? void 0 : _a.forEach((item) => this.trySetCustomDataIndexByName(item));
5925
+ const conversion = dataItem;
5926
+ if (((_b = (_a = conversion._metadata) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
5927
+ conversion._metadata = (_c = conversion._metadata) === null || _c === void 0 ? void 0 : _c.filter((item) => this.trySetCustomDataIndexByName(item));
5928
+ }
5880
5929
  }
5881
5930
  const expirationTime = this.dataProcessor.mutUpdateData({
5882
5931
  infoData,
5883
5932
  visitorCode,
5884
5933
  mutData: targetingData,
5885
5934
  dataItem,
5935
+ extendTtl,
5886
5936
  });
5887
5937
  const nextCleanup = infoData.nextDataCleanup;
5888
- if (!nextCleanup || (nextCleanup && expirationTime < nextCleanup)) {
5938
+ if (!nextCleanup ||
5939
+ (nextCleanup && expirationTime && expirationTime < nextCleanup)) {
5889
5940
  infoData.nextDataCleanup = expirationTime;
5890
5941
  }
5891
5942
  if (dataItem.data.status === exports.TrackingStatus.Unsent) {
@@ -5917,7 +5968,9 @@ class DataManager {
5917
5968
  var _a;
5918
5969
  const { data } = customData;
5919
5970
  const isDataValid = Boolean(data.value.length && data.value[0].length);
5920
- this.trySetCustomDataIndexByName(customData);
5971
+ if (!this.trySetCustomDataIndexByName(customData)) {
5972
+ return false;
5973
+ }
5921
5974
  if (data.index == CustomData$1.UNDEFINED_INDEX) {
5922
5975
  data.index = customData.index;
5923
5976
  }
@@ -5947,13 +6000,15 @@ class DataManager {
5947
6000
  return true;
5948
6001
  }
5949
6002
  trySetCustomDataIndexByName(customData) {
5950
- if (customData.index == CustomData$1.UNDEFINED_INDEX && customData.name) {
5951
- const cdIndex = this.customDataIndexByName.get(customData.name);
5952
- if (cdIndex === undefined) {
5953
- return;
5954
- }
5955
- customData.index = cdIndex;
5956
- }
6003
+ if (customData.index !== CustomData$1.UNDEFINED_INDEX)
6004
+ return true;
6005
+ if (!customData.name)
6006
+ return false;
6007
+ const cdIndex = this.customDataIndexByName.get(customData.name);
6008
+ if (cdIndex == null)
6009
+ return false;
6010
+ customData.index = cdIndex;
6011
+ return true;
5957
6012
  }
5958
6013
  get unsentDataVisitors() {
5959
6014
  const infoResult = this.infoStorage.read();
@@ -7057,6 +7112,13 @@ class Hasher {
7057
7112
  }
7058
7113
  }
7059
7114
 
7115
+ var LegalConsent;
7116
+ (function (LegalConsent) {
7117
+ LegalConsent[LegalConsent["Unknown"] = 0] = "Unknown";
7118
+ LegalConsent[LegalConsent["Given"] = 1] = "Given";
7119
+ LegalConsent[LegalConsent["NotGiven"] = 2] = "NotGiven";
7120
+ })(LegalConsent || (LegalConsent = {}));
7121
+
7060
7122
  class VariationConfiguration {
7061
7123
  constructor(externalStorage, externalStorageForcedExperimentVariations, externalStorageForcedFeatureVariations, visitorCodeManager, clientConfiguration) {
7062
7124
  this.storage = externalStorage;
@@ -7115,9 +7177,12 @@ class VariationConfiguration {
7115
7177
  }
7116
7178
  return buildExports.Ok(featureFlagVariations);
7117
7179
  }
7118
- getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, track = true, withAssignment = false, }) {
7180
+ getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, legalConsent, track = true, withAssignment = false, }) {
7119
7181
  KameleoonLogger.debug `CALL: VariationConfiguration.getVariation(visitorCode: ${visitorCode}, visitorIdentifier: ${visitorIdentifier}, featureFlag: ${featureFlag}, targetingData: ${targetingData}, packageInfo: ${packageInfo}, clientConfiguration, dataManager, withAssignment: ${withAssignment})`;
7120
7182
  const { rules, featureKey, id: featureFlagId, defaultVariationKey, } = featureFlag;
7183
+ const consent = clientConfiguration.isConsentRequired
7184
+ ? legalConsent
7185
+ : LegalConsent.Given;
7121
7186
  for (const rule of rules) {
7122
7187
  const { segment, experimentId, id, exposition, respoolTime, variationByExposition, } = rule;
7123
7188
  const forcedVariationData = this.getForcedExperimentVariation(visitorCode, rule.experimentId);
@@ -7164,6 +7229,16 @@ class VariationConfiguration {
7164
7229
  });
7165
7230
  KameleoonLogger.debug `Calculated ruleHash: ${ruleHash} for code: ${visitorIdentifier}`;
7166
7231
  if (ruleHash <= exposition) {
7232
+ // Checking if the evaluation is blocked due to the consent policy
7233
+ if (consent == LegalConsent.NotGiven &&
7234
+ rule.type == RuleType.EXPERIMENTATION) {
7235
+ const behaviour = clientConfiguration.consentBlockingBehaviour;
7236
+ if (behaviour == ConsentBlockingBehaviour.PartiallyBlocked) {
7237
+ break;
7238
+ }
7239
+ return buildExports.Err(new KameleoonError(exports.KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of ${rule} is blocked because consent is not provided for visitor '${visitorCode}'`));
7240
+ }
7241
+ // evaluate with CB scores if applicable
7167
7242
  let variation = this.evaluateCBScores(rule, visitorIdentifier, targetingData);
7168
7243
  if (!variation) {
7169
7244
  const variationHash = Hasher.getHashDouble({
@@ -7525,7 +7600,6 @@ class KameleoonEventSource {
7525
7600
  const VISITOR_CODE_LENGTH = 16;
7526
7601
  const VISITOR_CODE_MAX_LENGTH = 255;
7527
7602
  const DEFAULT_MAX_AGE = 60 * 60 * 24 * 365;
7528
- const ZERO_MAX_AGE = 0;
7529
7603
  const PATH = '/';
7530
7604
 
7531
7605
  /**
@@ -7993,7 +8067,7 @@ class Tracker {
7993
8067
  this.dataManager.storeTrackedData(updatedData);
7994
8068
  }
7995
8069
  else {
7996
- this.dataManager.storeData(updatedData);
8070
+ this.dataManager.storeData(updatedData, false);
7997
8071
  }
7998
8072
  }
7999
8073
  getUnsentVisitorData(visitorCode, isConsentProvided) {
@@ -8345,16 +8419,23 @@ class KameleoonClient {
8345
8419
  const variations = new Map();
8346
8420
  const featureFlags = this.clientConfiguration.featureFlags;
8347
8421
  for (const featureFlag of featureFlags.values()) {
8348
- const variation = this._getFeatureVariation({
8349
- visitorCode,
8350
- featureKey: featureFlag.featureKey,
8351
- track,
8352
- });
8353
- if (variation.ok) {
8354
- if (!onlyActive || variation.data.key !== OFF_VARIATION_KEY) {
8422
+ try {
8423
+ const variation = this._getFeatureVariation({
8424
+ visitorCode,
8425
+ featureKey: featureFlag.featureKey,
8426
+ track,
8427
+ });
8428
+ if (variation.ok &&
8429
+ (!onlyActive || variation.data.key !== OFF_VARIATION_KEY)) {
8355
8430
  variations.set(featureFlag.featureKey, variation.data);
8356
8431
  }
8357
8432
  }
8433
+ catch (err) {
8434
+ if (err instanceof KameleoonError &&
8435
+ err.type !== exports.KameleoonException.FeatureFlagEnvironmentDisabled) {
8436
+ throw err;
8437
+ }
8438
+ }
8358
8439
  }
8359
8440
  KameleoonLogger.info `RETURN: KameleoonClient.getVariations(visitorCode: ${visitorCode}, onlyActive: ${onlyActive}, track: ${track}) -> (variations: ${variations})`;
8360
8441
  return variations;
@@ -8674,25 +8755,16 @@ class KameleoonClient {
8674
8755
  path: PATH,
8675
8756
  });
8676
8757
  }
8677
- else {
8678
- if (this.visitorCodeManager.consentRequired) {
8679
- setData({
8680
- visitorCode: '',
8681
- key: exports.KameleoonStorageKey.VisitorCode,
8682
- maxAge: ZERO_MAX_AGE,
8683
- path: PATH,
8684
- });
8685
- }
8686
- }
8687
8758
  KameleoonLogger.info `RETURN: KameleoonClient.setUserConsent(visitorCode: ${visitorCode}, consent: ${consent}, setData: ${setData})`;
8688
8759
  }
8689
8760
  updateConsentData(visitorCode, consent) {
8690
8761
  const readResult = this.consentDataStorage.read();
8762
+ const legalConsent = consent ? LegalConsent.Given : LegalConsent.NotGiven;
8691
8763
  if (!readResult.ok) {
8692
8764
  if (readResult.error.type === exports.KameleoonException.StorageEmpty) {
8693
8765
  this.consentDataStorage.write({
8694
8766
  [visitorCode]: {
8695
- consent,
8767
+ consent: legalConsent,
8696
8768
  },
8697
8769
  });
8698
8770
  }
@@ -8700,28 +8772,36 @@ class KameleoonClient {
8700
8772
  }
8701
8773
  const data = readResult.data;
8702
8774
  data[visitorCode] = {
8703
- consent,
8775
+ consent: legalConsent,
8704
8776
  };
8705
8777
  this.consentDataStorage.write(data);
8706
8778
  }
8779
+ getLegalConsent(visitorCode) {
8780
+ KameleoonLogger.debug `CALL: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode})`;
8781
+ let legalConsent;
8782
+ const consentDataResult = this.consentDataStorage.read();
8783
+ legalConsent = consentDataResult.ok
8784
+ ? this.extractLegalConsent(consentDataResult.data[visitorCode])
8785
+ : LegalConsent.Unknown;
8786
+ KameleoonLogger.debug `RETURN: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode}) -> (legalConsent: ${legalConsent})`;
8787
+ return legalConsent;
8788
+ }
8789
+ extractLegalConsent(consentData) {
8790
+ if (consentData === undefined)
8791
+ return LegalConsent.Unknown;
8792
+ if (typeof consentData === 'boolean') {
8793
+ return consentData ? LegalConsent.Given : LegalConsent.NotGiven;
8794
+ }
8795
+ const value = consentData.consent;
8796
+ if (typeof value === 'boolean')
8797
+ return value ? LegalConsent.Given : LegalConsent.NotGiven;
8798
+ return value;
8799
+ }
8707
8800
  _isConsentProvided(visitorCode) {
8708
8801
  KameleoonLogger.debug `CALL: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode})`;
8709
8802
  const { isConsentRequired } = this.clientConfiguration;
8710
- const consentDataResult = this.consentDataStorage.read();
8711
- let isConsentProvided = false;
8712
- if (!isConsentRequired) {
8713
- isConsentProvided = true;
8714
- }
8715
- else if (consentDataResult.ok) {
8716
- const consentData = consentDataResult.data[visitorCode];
8717
- // for consistency with the old data in the storage
8718
- if (typeof consentData === 'boolean') {
8719
- isConsentProvided = consentData;
8720
- }
8721
- else {
8722
- isConsentProvided = consentData && consentData.consent;
8723
- }
8724
- }
8803
+ const isConsentProvided = !isConsentRequired ||
8804
+ this.getLegalConsent(visitorCode) == LegalConsent.Given;
8725
8805
  KameleoonLogger.debug `RETURN: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode}) -> (isConsentProvided: ${isConsentProvided})`;
8726
8806
  return isConsentProvided;
8727
8807
  }
@@ -8749,22 +8829,32 @@ class KameleoonClient {
8749
8829
  if (!featureFlag.environmentEnabled) {
8750
8830
  continue;
8751
8831
  }
8752
- const evalExp = this._evaluate({
8753
- visitorCode,
8754
- featureFlag,
8755
- track: false,
8756
- save: false,
8757
- });
8758
- if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8759
- activeVariations.push({
8760
- variationKey: evalExp.variationKey,
8761
- variationId: evalExp.variationId,
8762
- experimentId: evalExp.experimentId,
8763
- featureFlagId: featureFlag.id,
8764
- featureKey: featureFlag.featureKey,
8765
- rule: null,
8766
- isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8832
+ try {
8833
+ const evalExp = this._evaluate({
8834
+ visitorCode,
8835
+ featureFlag,
8836
+ track: false,
8837
+ save: false,
8767
8838
  });
8839
+ if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8840
+ activeVariations.push({
8841
+ variationKey: evalExp.variationKey,
8842
+ variationId: evalExp.variationId,
8843
+ experimentId: evalExp.experimentId,
8844
+ featureFlagId: featureFlag.id,
8845
+ featureKey: featureFlag.featureKey,
8846
+ rule: null,
8847
+ isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8848
+ });
8849
+ }
8850
+ }
8851
+ catch (err) {
8852
+ if (err instanceof KameleoonError) {
8853
+ if (err.type != exports.KameleoonException.FeatureFlagEnvironmentDisabled) {
8854
+ KameleoonLogger.error `Unexpected error: ${err}`;
8855
+ }
8856
+ continue;
8857
+ }
8768
8858
  }
8769
8859
  }
8770
8860
  KameleoonLogger.debug `RETURN: KameleoonClient._getActiveFeatureVariations(visitorCode: ${visitorCode}) -> (activeVariations: ${activeVariations})`;
@@ -8785,6 +8875,7 @@ class KameleoonClient {
8785
8875
  else if (this._isVisitorNotInHoldout(visitorCode, track, save, featureFlag, visitorData) &&
8786
8876
  this._isFFUnrestrictedByMEGroup(visitorCode, featureFlag, visitorData)) {
8787
8877
  const visitorIdentifier = this._getCodeForHash(visitorCode, featureFlag.bucketingCustomDataIndex, visitorData);
8878
+ const legalConsent = this.getLegalConsent(visitorCode);
8788
8879
  const variationData = this.variationConfiguration
8789
8880
  .getVariation({
8790
8881
  visitorCode,
@@ -8796,6 +8887,7 @@ class KameleoonClient {
8796
8887
  clientConfiguration: this.clientConfiguration,
8797
8888
  dataManager: this.dataManager,
8798
8889
  packageInfo: this.externalPackageInfo,
8890
+ legalConsent,
8799
8891
  })
8800
8892
  .throw();
8801
8893
  evalExp =
@@ -8914,6 +9006,14 @@ class KameleoonClient {
8914
9006
  }
8915
9007
  KameleoonLogger.debug `CALL: KameleoonClient._isVisitorNotInHoldout(visitorCode: ${visitorCode}, track: ${track}, save: ${save}, featureFlag: ${featureFlag}, visitorData: ${visitorData})`;
8916
9008
  let isNotInHoldout = true;
9009
+ // Checking if the evaluation is blocked due to the consent policy
9010
+ const legalConsent = this.getLegalConsent(visitorCode);
9011
+ if (legalConsent == LegalConsent.NotGiven) {
9012
+ const behaviour = this.clientConfiguration.consentBlockingBehaviour;
9013
+ if (behaviour == ConsentBlockingBehaviour.CompletelyBlocked) {
9014
+ throw new KameleoonError(exports.KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of holdout is blocked because consent is not provided for visitor '${visitorCode}'`);
9015
+ }
9016
+ }
8917
9017
  const codeForHash = this._getCodeForHash(visitorCode, featureFlag === null || featureFlag === void 0 ? void 0 : featureFlag.bucketingCustomDataIndex, visitorData);
8918
9018
  const holdoutHash = Hasher.getHashDouble({
8919
9019
  visitorIdentifier: codeForHash,