@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.
@@ -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 {
@@ -2890,7 +2934,10 @@ class VisitsData {
2890
2934
  constructor(visits) {
2891
2935
  this.status = TrackingStatus.Sent;
2892
2936
  this.visits = visits;
2893
- this.visitNumber = visits.length ? visits.length - 1 : 0;
2937
+ this._visitNumber = visits.length ? visits.length - 1 : 0;
2938
+ }
2939
+ get visitNumber() {
2940
+ return this._visitNumber;
2894
2941
  }
2895
2942
  get url() {
2896
2943
  return '';
@@ -2898,7 +2945,7 @@ class VisitsData {
2898
2945
  get data() {
2899
2946
  return {
2900
2947
  visits: this.visits,
2901
- visitNumber: this.visitNumber,
2948
+ visitNumber: this._visitNumber,
2902
2949
  type: KameleoonData.VisitsData,
2903
2950
  status: this.status,
2904
2951
  };
@@ -2915,11 +2962,11 @@ class VisitsData {
2915
2962
  list: this.visits,
2916
2963
  visit,
2917
2964
  });
2918
- this.visitNumber = this.visits.length ? this.visits.length - 1 : 0;
2965
+ this._visitNumber = this.visits.length ? this.visits.length - 1 : 0;
2919
2966
  }
2920
2967
  updateVisitNumber(visitNumber) {
2921
- if (visitNumber > this.visitNumber) {
2922
- this.visitNumber = visitNumber;
2968
+ if (visitNumber > this._visitNumber) {
2969
+ this._visitNumber = visitNumber;
2923
2970
  }
2924
2971
  }
2925
2972
  }
@@ -3126,10 +3173,12 @@ class VisitProcessor {
3126
3173
  timeLastActivity: (_a = visit.timeLastEvent) !== null && _a !== void 0 ? _a : visit.timeStarted,
3127
3174
  };
3128
3175
  }
3129
- processVisitNumber(visit, isCurrentVisit) {
3130
- if (visit.staticDataEvent) {
3131
- const visitNumber = visit.staticDataEvent.data.visitNumber + (isCurrentVisit ? 0 : 1);
3132
- this.visitsData.updateVisitNumber(visitNumber);
3176
+ processVisitNumber(visit, visitOffset) {
3177
+ var _a, _b, _c;
3178
+ if (this.visitsData.visitNumber <= visitOffset &&
3179
+ ((_a = visit.staticDataEvent) === null || _a === void 0 ? void 0 : _a.data.visitNumber)) {
3180
+ const visitNumber = (_c = (_b = visit.staticDataEvent) === null || _b === void 0 ? void 0 : _b.data.visitNumber) !== null && _c !== void 0 ? _c : 0;
3181
+ this.visitsData.updateVisitNumber(visitNumber + visitOffset);
3133
3182
  }
3134
3183
  }
3135
3184
  processCbs(cbsData) {
@@ -3303,12 +3352,12 @@ class Parser {
3303
3352
  }
3304
3353
  if (currentVisit) {
3305
3354
  visitProcessor.processVisit(currentVisit);
3306
- visitProcessor.processVisitNumber(currentVisit, true);
3355
+ visitProcessor.processVisitNumber(currentVisit, 0);
3307
3356
  }
3308
- previousVisits === null || previousVisits === void 0 ? void 0 : previousVisits.forEach((visit) => {
3357
+ previousVisits === null || previousVisits === void 0 ? void 0 : previousVisits.forEach((visit, index) => {
3309
3358
  visitProcessor.processVisit(visit);
3310
3359
  visitProcessor.processVisitsData(visit);
3311
- visitProcessor.processVisitNumber(visit, false);
3360
+ visitProcessor.processVisitNumber(visit, index + 1);
3312
3361
  });
3313
3362
  visitProcessor.processKcs(kcs);
3314
3363
  visitProcessor.processCbs(cbs);
@@ -3549,7 +3598,7 @@ class DataProcessor {
3549
3598
  this.cleanupInterval = cleanupInterval;
3550
3599
  this.packageInfo = packageInfo;
3551
3600
  }
3552
- mutUpdateData({ infoData, visitorCode, mutData, dataItem, }) {
3601
+ mutUpdateData({ infoData, visitorCode, mutData, dataItem, extendTtl, }) {
3553
3602
  let { visitorReference, data, isReference } = this.dereferenceData(mutData, visitorCode);
3554
3603
  if (this.packageInfo.isServer &&
3555
3604
  isReference &&
@@ -3560,9 +3609,12 @@ class DataProcessor {
3560
3609
  delete mutData[visitorCode];
3561
3610
  visitorReference = visitorCode;
3562
3611
  }
3563
- const expirationTime = this.cleanupInterval
3564
- ? Date.now() + this.cleanupInterval
3565
- : 0;
3612
+ let expirationTime;
3613
+ if (extendTtl) {
3614
+ expirationTime = this.cleanupInterval
3615
+ ? Date.now() + this.cleanupInterval
3616
+ : 0;
3617
+ }
3566
3618
  switch (dataItem.data.type) {
3567
3619
  case KameleoonData.CustomData: {
3568
3620
  this.updateCustomData({
@@ -3917,13 +3969,17 @@ class DataProcessor {
3917
3969
  return closestCleanupTime;
3918
3970
  }
3919
3971
  updateField({ key, value, data, visitorCode, expirationTime, }) {
3920
- data[visitorCode][key] = Object.assign(Object.assign({}, value), { expirationTime });
3972
+ var _a;
3973
+ const existing = data[visitorCode][key];
3974
+ 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
3975
  }
3922
3976
  createField({ key, value, data, visitorCode, expirationTime, }) {
3923
3977
  data[visitorCode] = Object.assign(Object.assign({}, data[visitorCode]), { [key]: Object.assign(Object.assign({}, value), { expirationTime }) });
3924
3978
  }
3925
3979
  updateNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3926
- data[visitorCode][key][nestedKey] = Object.assign(Object.assign({}, value), { expirationTime });
3980
+ var _a;
3981
+ const existing = data[visitorCode][key][nestedKey];
3982
+ 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
3983
  }
3928
3984
  createNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3929
3985
  var _a;
@@ -5189,15 +5245,7 @@ var KameleoonStorageKey;
5189
5245
  KameleoonStorageKey["ForcedExperimentVariation"] = "kameleoonForcedExperimentVariation";
5190
5246
  })(KameleoonStorageKey || (KameleoonStorageKey = {}));
5191
5247
  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
- },
5248
+ data: DEFAULT_DATA_FILE_CONFIGURATION,
5201
5249
  lastUpdate: '',
5202
5250
  };
5203
5251
  // --- Note ---
@@ -5709,7 +5757,7 @@ class DataManager {
5709
5757
  return resultData;
5710
5758
  }
5711
5759
  storeTrackedData(data) {
5712
- this.storeData(data);
5760
+ this.storeData(data, false);
5713
5761
  const infoResult = this.infoStorage.read();
5714
5762
  if (!infoResult.ok) {
5715
5763
  return;
@@ -5761,15 +5809,18 @@ class DataManager {
5761
5809
  targetingData,
5762
5810
  visitorCode: firstParameter,
5763
5811
  kameleoonData: secondParameter,
5812
+ extendTtl: true,
5764
5813
  });
5765
5814
  }
5766
5815
  else {
5767
5816
  for (const [visitorCode, kameleoonData] of Object.entries(firstParameter)) {
5817
+ const extendTtl = typeof secondParameter[0] === 'boolean' ? secondParameter[0] : true;
5768
5818
  this.mutUpdateTargetingData({
5769
5819
  infoData,
5770
5820
  targetingData,
5771
5821
  visitorCode,
5772
5822
  kameleoonData,
5823
+ extendTtl,
5773
5824
  });
5774
5825
  }
5775
5826
  }
@@ -5886,8 +5937,8 @@ class DataManager {
5886
5937
  }
5887
5938
  return null;
5888
5939
  }
5889
- mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, }) {
5890
- var _a;
5940
+ mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, extendTtl, }) {
5941
+ var _a, _b, _c;
5891
5942
  for (const dataItem of kameleoonData) {
5892
5943
  // process custom data
5893
5944
  if (dataItem.data.type === KameleoonData.CustomData) {
@@ -5903,16 +5954,21 @@ class DataManager {
5903
5954
  }
5904
5955
  // process metadata of conversions
5905
5956
  if (dataItem.data.type === KameleoonData.Conversion) {
5906
- (_a = dataItem._metadata) === null || _a === void 0 ? void 0 : _a.forEach((item) => this.trySetCustomDataIndexByName(item));
5957
+ const conversion = dataItem;
5958
+ if (((_b = (_a = conversion._metadata) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
5959
+ conversion._metadata = (_c = conversion._metadata) === null || _c === void 0 ? void 0 : _c.filter((item) => this.trySetCustomDataIndexByName(item));
5960
+ }
5907
5961
  }
5908
5962
  const expirationTime = this.dataProcessor.mutUpdateData({
5909
5963
  infoData,
5910
5964
  visitorCode,
5911
5965
  mutData: targetingData,
5912
5966
  dataItem,
5967
+ extendTtl,
5913
5968
  });
5914
5969
  const nextCleanup = infoData.nextDataCleanup;
5915
- if (!nextCleanup || (nextCleanup && expirationTime < nextCleanup)) {
5970
+ if (!nextCleanup ||
5971
+ (nextCleanup && expirationTime && expirationTime < nextCleanup)) {
5916
5972
  infoData.nextDataCleanup = expirationTime;
5917
5973
  }
5918
5974
  if (dataItem.data.status === TrackingStatus.Unsent) {
@@ -5944,7 +6000,9 @@ class DataManager {
5944
6000
  var _a;
5945
6001
  const { data } = customData;
5946
6002
  const isDataValid = Boolean(data.value.length && data.value[0].length);
5947
- this.trySetCustomDataIndexByName(customData);
6003
+ if (!this.trySetCustomDataIndexByName(customData)) {
6004
+ return false;
6005
+ }
5948
6006
  if (data.index == CustomData$1.UNDEFINED_INDEX) {
5949
6007
  data.index = customData.index;
5950
6008
  }
@@ -5974,13 +6032,15 @@ class DataManager {
5974
6032
  return true;
5975
6033
  }
5976
6034
  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
- }
6035
+ if (customData.index !== CustomData$1.UNDEFINED_INDEX)
6036
+ return true;
6037
+ if (!customData.name)
6038
+ return false;
6039
+ const cdIndex = this.customDataIndexByName.get(customData.name);
6040
+ if (cdIndex == null)
6041
+ return false;
6042
+ customData.index = cdIndex;
6043
+ return true;
5984
6044
  }
5985
6045
  get unsentDataVisitors() {
5986
6046
  const infoResult = this.infoStorage.read();
@@ -7093,6 +7153,13 @@ class Hasher {
7093
7153
  }
7094
7154
  }
7095
7155
 
7156
+ var LegalConsent;
7157
+ (function (LegalConsent) {
7158
+ LegalConsent[LegalConsent["Unknown"] = 0] = "Unknown";
7159
+ LegalConsent[LegalConsent["Given"] = 1] = "Given";
7160
+ LegalConsent[LegalConsent["NotGiven"] = 2] = "NotGiven";
7161
+ })(LegalConsent || (LegalConsent = {}));
7162
+
7096
7163
  class VariationConfiguration {
7097
7164
  constructor(externalStorage, externalStorageForcedExperimentVariations, externalStorageForcedFeatureVariations, visitorCodeManager, clientConfiguration) {
7098
7165
  this.storage = externalStorage;
@@ -7151,9 +7218,12 @@ class VariationConfiguration {
7151
7218
  }
7152
7219
  return buildExports.Ok(featureFlagVariations);
7153
7220
  }
7154
- getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, track = true, withAssignment = false, }) {
7221
+ getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, legalConsent, track = true, withAssignment = false, }) {
7155
7222
  KameleoonLogger.debug `CALL: VariationConfiguration.getVariation(visitorCode: ${visitorCode}, visitorIdentifier: ${visitorIdentifier}, featureFlag: ${featureFlag}, targetingData: ${targetingData}, packageInfo: ${packageInfo}, clientConfiguration, dataManager, withAssignment: ${withAssignment})`;
7156
7223
  const { rules, featureKey, id: featureFlagId, defaultVariationKey, } = featureFlag;
7224
+ const consent = clientConfiguration.isConsentRequired
7225
+ ? legalConsent
7226
+ : LegalConsent.Given;
7157
7227
  for (const rule of rules) {
7158
7228
  const { segment, experimentId, id, exposition, respoolTime, variationByExposition, } = rule;
7159
7229
  const forcedVariationData = this.getForcedExperimentVariation(visitorCode, rule.experimentId);
@@ -7200,6 +7270,16 @@ class VariationConfiguration {
7200
7270
  });
7201
7271
  KameleoonLogger.debug `Calculated ruleHash: ${ruleHash} for code: ${visitorIdentifier}`;
7202
7272
  if (ruleHash <= exposition) {
7273
+ // Checking if the evaluation is blocked due to the consent policy
7274
+ if (consent == LegalConsent.NotGiven &&
7275
+ rule.type == RuleType.EXPERIMENTATION) {
7276
+ const behaviour = clientConfiguration.consentBlockingBehaviour;
7277
+ if (behaviour == ConsentBlockingBehaviour.PartiallyBlocked) {
7278
+ break;
7279
+ }
7280
+ return buildExports.Err(new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of ${rule} is blocked because consent is not provided for visitor '${visitorCode}'`));
7281
+ }
7282
+ // evaluate with CB scores if applicable
7203
7283
  let variation = this.evaluateCBScores(rule, visitorIdentifier, targetingData);
7204
7284
  if (!variation) {
7205
7285
  const variationHash = Hasher.getHashDouble({
@@ -7561,7 +7641,6 @@ class KameleoonEventSource {
7561
7641
  const VISITOR_CODE_LENGTH = 16;
7562
7642
  const VISITOR_CODE_MAX_LENGTH = 255;
7563
7643
  const DEFAULT_MAX_AGE = 60 * 60 * 24 * 365;
7564
- const ZERO_MAX_AGE = 0;
7565
7644
  const PATH = '/';
7566
7645
 
7567
7646
  /**
@@ -8029,7 +8108,7 @@ class Tracker {
8029
8108
  this.dataManager.storeTrackedData(updatedData);
8030
8109
  }
8031
8110
  else {
8032
- this.dataManager.storeData(updatedData);
8111
+ this.dataManager.storeData(updatedData, false);
8033
8112
  }
8034
8113
  }
8035
8114
  getUnsentVisitorData(visitorCode, isConsentProvided) {
@@ -8381,16 +8460,23 @@ class KameleoonClient {
8381
8460
  const variations = new Map();
8382
8461
  const featureFlags = this.clientConfiguration.featureFlags;
8383
8462
  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) {
8463
+ try {
8464
+ const variation = this._getFeatureVariation({
8465
+ visitorCode,
8466
+ featureKey: featureFlag.featureKey,
8467
+ track,
8468
+ });
8469
+ if (variation.ok &&
8470
+ (!onlyActive || variation.data.key !== OFF_VARIATION_KEY)) {
8391
8471
  variations.set(featureFlag.featureKey, variation.data);
8392
8472
  }
8393
8473
  }
8474
+ catch (err) {
8475
+ if (err instanceof KameleoonError &&
8476
+ err.type !== KameleoonException.FeatureFlagEnvironmentDisabled) {
8477
+ throw err;
8478
+ }
8479
+ }
8394
8480
  }
8395
8481
  KameleoonLogger.info `RETURN: KameleoonClient.getVariations(visitorCode: ${visitorCode}, onlyActive: ${onlyActive}, track: ${track}) -> (variations: ${variations})`;
8396
8482
  return variations;
@@ -8710,25 +8796,16 @@ class KameleoonClient {
8710
8796
  path: PATH,
8711
8797
  });
8712
8798
  }
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
8799
  KameleoonLogger.info `RETURN: KameleoonClient.setUserConsent(visitorCode: ${visitorCode}, consent: ${consent}, setData: ${setData})`;
8724
8800
  }
8725
8801
  updateConsentData(visitorCode, consent) {
8726
8802
  const readResult = this.consentDataStorage.read();
8803
+ const legalConsent = consent ? LegalConsent.Given : LegalConsent.NotGiven;
8727
8804
  if (!readResult.ok) {
8728
8805
  if (readResult.error.type === KameleoonException.StorageEmpty) {
8729
8806
  this.consentDataStorage.write({
8730
8807
  [visitorCode]: {
8731
- consent,
8808
+ consent: legalConsent,
8732
8809
  },
8733
8810
  });
8734
8811
  }
@@ -8736,28 +8813,36 @@ class KameleoonClient {
8736
8813
  }
8737
8814
  const data = readResult.data;
8738
8815
  data[visitorCode] = {
8739
- consent,
8816
+ consent: legalConsent,
8740
8817
  };
8741
8818
  this.consentDataStorage.write(data);
8742
8819
  }
8820
+ getLegalConsent(visitorCode) {
8821
+ KameleoonLogger.debug `CALL: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode})`;
8822
+ let legalConsent;
8823
+ const consentDataResult = this.consentDataStorage.read();
8824
+ legalConsent = consentDataResult.ok
8825
+ ? this.extractLegalConsent(consentDataResult.data[visitorCode])
8826
+ : LegalConsent.Unknown;
8827
+ KameleoonLogger.debug `RETURN: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode}) -> (legalConsent: ${legalConsent})`;
8828
+ return legalConsent;
8829
+ }
8830
+ extractLegalConsent(consentData) {
8831
+ if (consentData === undefined)
8832
+ return LegalConsent.Unknown;
8833
+ if (typeof consentData === 'boolean') {
8834
+ return consentData ? LegalConsent.Given : LegalConsent.NotGiven;
8835
+ }
8836
+ const value = consentData.consent;
8837
+ if (typeof value === 'boolean')
8838
+ return value ? LegalConsent.Given : LegalConsent.NotGiven;
8839
+ return value;
8840
+ }
8743
8841
  _isConsentProvided(visitorCode) {
8744
8842
  KameleoonLogger.debug `CALL: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode})`;
8745
8843
  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
- }
8844
+ const isConsentProvided = !isConsentRequired ||
8845
+ this.getLegalConsent(visitorCode) == LegalConsent.Given;
8761
8846
  KameleoonLogger.debug `RETURN: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode}) -> (isConsentProvided: ${isConsentProvided})`;
8762
8847
  return isConsentProvided;
8763
8848
  }
@@ -8785,22 +8870,32 @@ class KameleoonClient {
8785
8870
  if (!featureFlag.environmentEnabled) {
8786
8871
  continue;
8787
8872
  }
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,
8873
+ try {
8874
+ const evalExp = this._evaluate({
8875
+ visitorCode,
8876
+ featureFlag,
8877
+ track: false,
8878
+ save: false,
8803
8879
  });
8880
+ if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8881
+ activeVariations.push({
8882
+ variationKey: evalExp.variationKey,
8883
+ variationId: evalExp.variationId,
8884
+ experimentId: evalExp.experimentId,
8885
+ featureFlagId: featureFlag.id,
8886
+ featureKey: featureFlag.featureKey,
8887
+ rule: null,
8888
+ isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8889
+ });
8890
+ }
8891
+ }
8892
+ catch (err) {
8893
+ if (err instanceof KameleoonError) {
8894
+ if (err.type != KameleoonException.FeatureFlagEnvironmentDisabled) {
8895
+ KameleoonLogger.error `Unexpected error: ${err}`;
8896
+ }
8897
+ continue;
8898
+ }
8804
8899
  }
8805
8900
  }
8806
8901
  KameleoonLogger.debug `RETURN: KameleoonClient._getActiveFeatureVariations(visitorCode: ${visitorCode}) -> (activeVariations: ${activeVariations})`;
@@ -8821,6 +8916,7 @@ class KameleoonClient {
8821
8916
  else if (this._isVisitorNotInHoldout(visitorCode, track, save, featureFlag, visitorData) &&
8822
8917
  this._isFFUnrestrictedByMEGroup(visitorCode, featureFlag, visitorData)) {
8823
8918
  const visitorIdentifier = this._getCodeForHash(visitorCode, featureFlag.bucketingCustomDataIndex, visitorData);
8919
+ const legalConsent = this.getLegalConsent(visitorCode);
8824
8920
  const variationData = this.variationConfiguration
8825
8921
  .getVariation({
8826
8922
  visitorCode,
@@ -8832,6 +8928,7 @@ class KameleoonClient {
8832
8928
  clientConfiguration: this.clientConfiguration,
8833
8929
  dataManager: this.dataManager,
8834
8930
  packageInfo: this.externalPackageInfo,
8931
+ legalConsent,
8835
8932
  })
8836
8933
  .throw();
8837
8934
  evalExp =
@@ -8950,6 +9047,16 @@ class KameleoonClient {
8950
9047
  }
8951
9048
  KameleoonLogger.debug `CALL: KameleoonClient._isVisitorNotInHoldout(visitorCode: ${visitorCode}, track: ${track}, save: ${save}, featureFlag: ${featureFlag}, visitorData: ${visitorData})`;
8952
9049
  let isNotInHoldout = true;
9050
+ // Checking if the evaluation is blocked due to the consent policy
9051
+ const legalConsent = this.clientConfiguration.isConsentRequired
9052
+ ? this.getLegalConsent(visitorCode)
9053
+ : LegalConsent.Given;
9054
+ if (legalConsent == LegalConsent.NotGiven) {
9055
+ const behaviour = this.clientConfiguration.consentBlockingBehaviour;
9056
+ if (behaviour == ConsentBlockingBehaviour.CompletelyBlocked) {
9057
+ throw new KameleoonError(KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of holdout is blocked because consent is not provided for visitor '${visitorCode}'`);
9058
+ }
9059
+ }
8953
9060
  const codeForHash = this._getCodeForHash(visitorCode, featureFlag === null || featureFlag === void 0 ? void 0 : featureFlag.bucketingCustomDataIndex, visitorData);
8954
9061
  const holdoutHash = Hasher.getHashDouble({
8955
9062
  visitorIdentifier: codeForHash,