@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.
@@ -249,10 +249,17 @@ class KameleoonError extends Error {
249
249
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
250
250
  break;
251
251
  case exports.KameleoonException.FeatureFlagVariationNotFound:
252
- case exports.KameleoonException.FeatureFlagEnvironmentDisabled:
253
252
  case exports.KameleoonException.FeatureFlagVariableNotFound:
254
253
  this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
255
254
  break;
255
+ case exports.KameleoonException.FeatureFlagEnvironmentDisabled:
256
+ if (thirdParam !== undefined) {
257
+ this.message = ERROR_MESSAGES[type](secondParam, thirdParam);
258
+ }
259
+ else {
260
+ this.message = secondParam;
261
+ }
262
+ break;
256
263
  case exports.KameleoonException.StorageWrite:
257
264
  case exports.KameleoonException.JSONParse:
258
265
  this.message = ERROR_MESSAGES[type](secondParam);
@@ -308,7 +315,7 @@ exports.RequestType = void 0;
308
315
  RequestType["RemoteData"] = "remoteData";
309
316
  })(exports.RequestType || (exports.RequestType = {}));
310
317
 
311
- const NUMBER_OF_RETRIES = 2;
318
+ const NUMBER_OF_RETRIES = 1;
312
319
  exports.Header = void 0;
313
320
  (function (Header) {
314
321
  Header["UserAgent"] = "User-Agent";
@@ -887,6 +894,9 @@ class UrlProvider {
887
894
  const currentDomain = this.domains[UrlType.DataApi];
888
895
  this.domains[UrlType.DataApi] = currentDomain.replace(/^[^.]+/, subDomain);
889
896
  }
897
+ get dataApiDomain() {
898
+ return this.domains[UrlType.DataApi];
899
+ }
890
900
  getClientConfigurationUrl(timeStamp) {
891
901
  this.isInitialized();
892
902
  const baseUrl = `https://${this.domains[UrlType.ClientConfiguration]}/v3/`;
@@ -1146,6 +1156,11 @@ var ConsentType;
1146
1156
  ConsentType["Required"] = "REQUIRED";
1147
1157
  ConsentType["NotRequired"] = "NOT_REQUIRED";
1148
1158
  })(ConsentType || (ConsentType = {}));
1159
+ var ConsentBlockingBehaviour;
1160
+ (function (ConsentBlockingBehaviour) {
1161
+ ConsentBlockingBehaviour["PartiallyBlocked"] = "PARTIALLY_BLOCK";
1162
+ ConsentBlockingBehaviour["CompletelyBlocked"] = "FULLY_BLOCK";
1163
+ })(ConsentBlockingBehaviour || (ConsentBlockingBehaviour = {}));
1149
1164
 
1150
1165
  class EventManager {
1151
1166
  addEventHandler(eventType, callback) {
@@ -1173,17 +1188,14 @@ exports.EventType = void 0;
1173
1188
  EventType["ConfigurationUpdate"] = "configurationUpdate";
1174
1189
  })(exports.EventType || (exports.EventType = {}));
1175
1190
 
1176
- ({
1177
- configuration: {
1178
- consentType: ConsentType.NotRequired},
1179
- });
1180
- const DEFAULT_CLIENT_CONFIGURATION$1 = {
1191
+ const DEFAULT_DATA_FILE_CONFIGURATION = {
1181
1192
  customData: [],
1182
1193
  featureFlags: [],
1183
1194
  configuration: {
1184
1195
  realTimeUpdate: false,
1185
1196
  consentType: ConsentType.NotRequired,
1186
1197
  dataApiDomain: 'data.kameleoon.io',
1198
+ consentOptOutBehavior: ConsentBlockingBehaviour.PartiallyBlocked,
1187
1199
  },
1188
1200
  };
1189
1201
 
@@ -1207,7 +1219,7 @@ class ClientConfiguration {
1207
1219
  constructor({ updateInterval, urlProvider, storage, requester, dataManager, eventSource, externalVisitorCodeManager, eventManager, externalPackageInfo, defaultDataFile, }) {
1208
1220
  this.updateConfigurationIntervalId = null;
1209
1221
  this.updateType = UpdateType.Polling;
1210
- this.configurationData = DEFAULT_CLIENT_CONFIGURATION$1;
1222
+ this.configurationData = DEFAULT_DATA_FILE_CONFIGURATION;
1211
1223
  this.featureFlagsData = new Map();
1212
1224
  this.isTargetedDeliveryRule = null;
1213
1225
  this.segmentsData = null;
@@ -1216,6 +1228,7 @@ class ClientConfiguration {
1216
1228
  this.mappedRules = null;
1217
1229
  this.mappedExperiments = null;
1218
1230
  this.usedDefaultDataFile = false;
1231
+ this.blockingBehaviourMode = ConsentBlockingBehaviour.PartiallyBlocked;
1219
1232
  this.CACHE_REVALIDATE_PERIOD = 90 * exports.Milliseconds.Minute;
1220
1233
  this.urlProvider = urlProvider;
1221
1234
  this.requester = requester;
@@ -1390,6 +1403,9 @@ class ClientConfiguration {
1390
1403
  get isConsentRequired() {
1391
1404
  return this.configuration.consentType === ConsentType.Required;
1392
1405
  }
1406
+ get consentBlockingBehaviour() {
1407
+ return this.blockingBehaviourMode;
1408
+ }
1393
1409
  get hasAnyTargetedDeliveryRule() {
1394
1410
  if (this.isTargetedDeliveryRule !== null) {
1395
1411
  return this.isTargetedDeliveryRule;
@@ -1518,6 +1534,7 @@ class ClientConfiguration {
1518
1534
  KameleoonLogger.info `Configuration update type was toggled to ${UpdateType[updateType]}`;
1519
1535
  yield this.initialize();
1520
1536
  }
1537
+ this.blockingBehaviourMode = this.consentBlockingBehaviourFromStr(clientConfigurationData.configuration.consentOptOutBehavior);
1521
1538
  return buildExports.Ok(toggleUpdateType);
1522
1539
  });
1523
1540
  }
@@ -1624,6 +1641,14 @@ class ClientConfiguration {
1624
1641
  this.visitorCodeManager.consentRequired =
1625
1642
  this.isConsentRequired && !this.hasAnyTargetedDeliveryRule;
1626
1643
  }
1644
+ consentBlockingBehaviourFromStr(str) {
1645
+ if (str === ConsentBlockingBehaviour.PartiallyBlocked ||
1646
+ str === ConsentBlockingBehaviour.CompletelyBlocked) {
1647
+ return str;
1648
+ }
1649
+ KameleoonLogger.error(`Unexpected consent blocking type '${str}'`);
1650
+ return ConsentBlockingBehaviour.PartiallyBlocked;
1651
+ }
1627
1652
  }
1628
1653
 
1629
1654
  function constructTypeMap(indexMap) {
@@ -2418,6 +2443,13 @@ let CustomData$1 = class CustomData {
2418
2443
  get name() {
2419
2444
  return this._name;
2420
2445
  }
2446
+ /**
2447
+ * @private
2448
+ * @method name - an internal getter for a name of custom data
2449
+ * */
2450
+ get values() {
2451
+ return this.value;
2452
+ }
2421
2453
  };
2422
2454
  CustomData$1.UNDEFINED_INDEX = -1;
2423
2455
 
@@ -2435,7 +2467,7 @@ let Conversion$1 = class Conversion {
2435
2467
  this.negative = negative;
2436
2468
  this.status = exports.TrackingStatus.Unsent;
2437
2469
  this.id = Math.floor(Math.random() * 1000000);
2438
- this.metadata = metadata;
2470
+ this._metadata = metadata;
2439
2471
  }
2440
2472
  set _id(id) {
2441
2473
  this.id = id;
@@ -2456,9 +2488,21 @@ let Conversion$1 = class Conversion {
2456
2488
  ? UrlParameter.Metadata + this._encodeMetadata()
2457
2489
  : ''));
2458
2490
  }
2491
+ /**
2492
+ * @private
2493
+ * @method metadata - an internal getter for a metadata of conversion
2494
+ * */
2459
2495
  get _metadata() {
2460
2496
  return this.metadata;
2461
2497
  }
2498
+ /**
2499
+ * @private
2500
+ * @method metadata - an internal setter for setting metadata of conversion
2501
+ * @param {number} value - an index value
2502
+ * */
2503
+ set _metadata(value) {
2504
+ this.metadata = value;
2505
+ }
2462
2506
  get data() {
2463
2507
  var _a;
2464
2508
  return {
@@ -2892,7 +2936,10 @@ class VisitsData {
2892
2936
  constructor(visits) {
2893
2937
  this.status = exports.TrackingStatus.Sent;
2894
2938
  this.visits = visits;
2895
- this.visitNumber = visits.length ? visits.length - 1 : 0;
2939
+ this._visitNumber = visits.length ? visits.length - 1 : 0;
2940
+ }
2941
+ get visitNumber() {
2942
+ return this._visitNumber;
2896
2943
  }
2897
2944
  get url() {
2898
2945
  return '';
@@ -2900,7 +2947,7 @@ class VisitsData {
2900
2947
  get data() {
2901
2948
  return {
2902
2949
  visits: this.visits,
2903
- visitNumber: this.visitNumber,
2950
+ visitNumber: this._visitNumber,
2904
2951
  type: exports.KameleoonData.VisitsData,
2905
2952
  status: this.status,
2906
2953
  };
@@ -2917,11 +2964,11 @@ class VisitsData {
2917
2964
  list: this.visits,
2918
2965
  visit,
2919
2966
  });
2920
- this.visitNumber = this.visits.length ? this.visits.length - 1 : 0;
2967
+ this._visitNumber = this.visits.length ? this.visits.length - 1 : 0;
2921
2968
  }
2922
2969
  updateVisitNumber(visitNumber) {
2923
- if (visitNumber > this.visitNumber) {
2924
- this.visitNumber = visitNumber;
2970
+ if (visitNumber > this._visitNumber) {
2971
+ this._visitNumber = visitNumber;
2925
2972
  }
2926
2973
  }
2927
2974
  }
@@ -3128,10 +3175,12 @@ class VisitProcessor {
3128
3175
  timeLastActivity: (_a = visit.timeLastEvent) !== null && _a !== void 0 ? _a : visit.timeStarted,
3129
3176
  };
3130
3177
  }
3131
- processVisitNumber(visit, isCurrentVisit) {
3132
- if (visit.staticDataEvent) {
3133
- const visitNumber = visit.staticDataEvent.data.visitNumber + (isCurrentVisit ? 0 : 1);
3134
- this.visitsData.updateVisitNumber(visitNumber);
3178
+ processVisitNumber(visit, visitOffset) {
3179
+ var _a, _b, _c;
3180
+ if (this.visitsData.visitNumber <= visitOffset &&
3181
+ ((_a = visit.staticDataEvent) === null || _a === void 0 ? void 0 : _a.data.visitNumber)) {
3182
+ const visitNumber = (_c = (_b = visit.staticDataEvent) === null || _b === void 0 ? void 0 : _b.data.visitNumber) !== null && _c !== void 0 ? _c : 0;
3183
+ this.visitsData.updateVisitNumber(visitNumber + visitOffset);
3135
3184
  }
3136
3185
  }
3137
3186
  processCbs(cbsData) {
@@ -3305,12 +3354,12 @@ class Parser {
3305
3354
  }
3306
3355
  if (currentVisit) {
3307
3356
  visitProcessor.processVisit(currentVisit);
3308
- visitProcessor.processVisitNumber(currentVisit, true);
3357
+ visitProcessor.processVisitNumber(currentVisit, 0);
3309
3358
  }
3310
- previousVisits === null || previousVisits === void 0 ? void 0 : previousVisits.forEach((visit) => {
3359
+ previousVisits === null || previousVisits === void 0 ? void 0 : previousVisits.forEach((visit, index) => {
3311
3360
  visitProcessor.processVisit(visit);
3312
3361
  visitProcessor.processVisitsData(visit);
3313
- visitProcessor.processVisitNumber(visit, false);
3362
+ visitProcessor.processVisitNumber(visit, index + 1);
3314
3363
  });
3315
3364
  visitProcessor.processKcs(kcs);
3316
3365
  visitProcessor.processCbs(cbs);
@@ -3551,7 +3600,7 @@ class DataProcessor {
3551
3600
  this.cleanupInterval = cleanupInterval;
3552
3601
  this.packageInfo = packageInfo;
3553
3602
  }
3554
- mutUpdateData({ infoData, visitorCode, mutData, dataItem, }) {
3603
+ mutUpdateData({ infoData, visitorCode, mutData, dataItem, extendTtl, }) {
3555
3604
  let { visitorReference, data, isReference } = this.dereferenceData(mutData, visitorCode);
3556
3605
  if (this.packageInfo.isServer &&
3557
3606
  isReference &&
@@ -3562,9 +3611,12 @@ class DataProcessor {
3562
3611
  delete mutData[visitorCode];
3563
3612
  visitorReference = visitorCode;
3564
3613
  }
3565
- const expirationTime = this.cleanupInterval
3566
- ? Date.now() + this.cleanupInterval
3567
- : 0;
3614
+ let expirationTime;
3615
+ if (extendTtl) {
3616
+ expirationTime = this.cleanupInterval
3617
+ ? Date.now() + this.cleanupInterval
3618
+ : 0;
3619
+ }
3568
3620
  switch (dataItem.data.type) {
3569
3621
  case exports.KameleoonData.CustomData: {
3570
3622
  this.updateCustomData({
@@ -3919,13 +3971,17 @@ class DataProcessor {
3919
3971
  return closestCleanupTime;
3920
3972
  }
3921
3973
  updateField({ key, value, data, visitorCode, expirationTime, }) {
3922
- data[visitorCode][key] = Object.assign(Object.assign({}, value), { expirationTime });
3974
+ var _a;
3975
+ const existing = data[visitorCode][key];
3976
+ 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() });
3923
3977
  }
3924
3978
  createField({ key, value, data, visitorCode, expirationTime, }) {
3925
3979
  data[visitorCode] = Object.assign(Object.assign({}, data[visitorCode]), { [key]: Object.assign(Object.assign({}, value), { expirationTime }) });
3926
3980
  }
3927
3981
  updateNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3928
- data[visitorCode][key][nestedKey] = Object.assign(Object.assign({}, value), { expirationTime });
3982
+ var _a;
3983
+ const existing = data[visitorCode][key][nestedKey];
3984
+ 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() });
3929
3985
  }
3930
3986
  createNestedField({ key, nestedKey, value, data, visitorCode, expirationTime, }) {
3931
3987
  var _a;
@@ -5191,15 +5247,7 @@ exports.KameleoonStorageKey = void 0;
5191
5247
  KameleoonStorageKey["ForcedExperimentVariation"] = "kameleoonForcedExperimentVariation";
5192
5248
  })(exports.KameleoonStorageKey || (exports.KameleoonStorageKey = {}));
5193
5249
  const DEFAULT_CLIENT_CONFIGURATION = {
5194
- data: {
5195
- customData: [],
5196
- featureFlags: [],
5197
- configuration: {
5198
- realTimeUpdate: false,
5199
- consentType: ConsentType.NotRequired,
5200
- dataApiDomain: 'data.kameleoon.io',
5201
- },
5202
- },
5250
+ data: DEFAULT_DATA_FILE_CONFIGURATION,
5203
5251
  lastUpdate: '',
5204
5252
  };
5205
5253
  // --- Note ---
@@ -5711,7 +5759,7 @@ class DataManager {
5711
5759
  return resultData;
5712
5760
  }
5713
5761
  storeTrackedData(data) {
5714
- this.storeData(data);
5762
+ this.storeData(data, false);
5715
5763
  const infoResult = this.infoStorage.read();
5716
5764
  if (!infoResult.ok) {
5717
5765
  return;
@@ -5763,15 +5811,18 @@ class DataManager {
5763
5811
  targetingData,
5764
5812
  visitorCode: firstParameter,
5765
5813
  kameleoonData: secondParameter,
5814
+ extendTtl: true,
5766
5815
  });
5767
5816
  }
5768
5817
  else {
5769
5818
  for (const [visitorCode, kameleoonData] of Object.entries(firstParameter)) {
5819
+ const extendTtl = typeof secondParameter[0] === 'boolean' ? secondParameter[0] : true;
5770
5820
  this.mutUpdateTargetingData({
5771
5821
  infoData,
5772
5822
  targetingData,
5773
5823
  visitorCode,
5774
5824
  kameleoonData,
5825
+ extendTtl,
5775
5826
  });
5776
5827
  }
5777
5828
  }
@@ -5888,8 +5939,8 @@ class DataManager {
5888
5939
  }
5889
5940
  return null;
5890
5941
  }
5891
- mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, }) {
5892
- var _a;
5942
+ mutUpdateTargetingData({ infoData, visitorCode, kameleoonData, targetingData, extendTtl, }) {
5943
+ var _a, _b, _c;
5893
5944
  for (const dataItem of kameleoonData) {
5894
5945
  // process custom data
5895
5946
  if (dataItem.data.type === exports.KameleoonData.CustomData) {
@@ -5905,16 +5956,21 @@ class DataManager {
5905
5956
  }
5906
5957
  // process metadata of conversions
5907
5958
  if (dataItem.data.type === exports.KameleoonData.Conversion) {
5908
- (_a = dataItem._metadata) === null || _a === void 0 ? void 0 : _a.forEach((item) => this.trySetCustomDataIndexByName(item));
5959
+ const conversion = dataItem;
5960
+ if (((_b = (_a = conversion._metadata) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
5961
+ conversion._metadata = (_c = conversion._metadata) === null || _c === void 0 ? void 0 : _c.filter((item) => this.trySetCustomDataIndexByName(item));
5962
+ }
5909
5963
  }
5910
5964
  const expirationTime = this.dataProcessor.mutUpdateData({
5911
5965
  infoData,
5912
5966
  visitorCode,
5913
5967
  mutData: targetingData,
5914
5968
  dataItem,
5969
+ extendTtl,
5915
5970
  });
5916
5971
  const nextCleanup = infoData.nextDataCleanup;
5917
- if (!nextCleanup || (nextCleanup && expirationTime < nextCleanup)) {
5972
+ if (!nextCleanup ||
5973
+ (nextCleanup && expirationTime && expirationTime < nextCleanup)) {
5918
5974
  infoData.nextDataCleanup = expirationTime;
5919
5975
  }
5920
5976
  if (dataItem.data.status === exports.TrackingStatus.Unsent) {
@@ -5946,7 +6002,9 @@ class DataManager {
5946
6002
  var _a;
5947
6003
  const { data } = customData;
5948
6004
  const isDataValid = Boolean(data.value.length && data.value[0].length);
5949
- this.trySetCustomDataIndexByName(customData);
6005
+ if (!this.trySetCustomDataIndexByName(customData)) {
6006
+ return false;
6007
+ }
5950
6008
  if (data.index == CustomData$1.UNDEFINED_INDEX) {
5951
6009
  data.index = customData.index;
5952
6010
  }
@@ -5976,13 +6034,15 @@ class DataManager {
5976
6034
  return true;
5977
6035
  }
5978
6036
  trySetCustomDataIndexByName(customData) {
5979
- if (customData.index == CustomData$1.UNDEFINED_INDEX && customData.name) {
5980
- const cdIndex = this.customDataIndexByName.get(customData.name);
5981
- if (cdIndex === undefined) {
5982
- return;
5983
- }
5984
- customData.index = cdIndex;
5985
- }
6037
+ if (customData.index !== CustomData$1.UNDEFINED_INDEX)
6038
+ return true;
6039
+ if (!customData.name)
6040
+ return false;
6041
+ const cdIndex = this.customDataIndexByName.get(customData.name);
6042
+ if (cdIndex == null)
6043
+ return false;
6044
+ customData.index = cdIndex;
6045
+ return true;
5986
6046
  }
5987
6047
  get unsentDataVisitors() {
5988
6048
  const infoResult = this.infoStorage.read();
@@ -7095,6 +7155,13 @@ class Hasher {
7095
7155
  }
7096
7156
  }
7097
7157
 
7158
+ var LegalConsent;
7159
+ (function (LegalConsent) {
7160
+ LegalConsent[LegalConsent["Unknown"] = 0] = "Unknown";
7161
+ LegalConsent[LegalConsent["Given"] = 1] = "Given";
7162
+ LegalConsent[LegalConsent["NotGiven"] = 2] = "NotGiven";
7163
+ })(LegalConsent || (LegalConsent = {}));
7164
+
7098
7165
  class VariationConfiguration {
7099
7166
  constructor(externalStorage, externalStorageForcedExperimentVariations, externalStorageForcedFeatureVariations, visitorCodeManager, clientConfiguration) {
7100
7167
  this.storage = externalStorage;
@@ -7153,9 +7220,12 @@ class VariationConfiguration {
7153
7220
  }
7154
7221
  return buildExports.Ok(featureFlagVariations);
7155
7222
  }
7156
- getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, track = true, withAssignment = false, }) {
7223
+ getVariation({ visitorCode, visitorIdentifier, featureFlag, targetingData, packageInfo, clientConfiguration, dataManager, legalConsent, track = true, withAssignment = false, }) {
7157
7224
  KameleoonLogger.debug `CALL: VariationConfiguration.getVariation(visitorCode: ${visitorCode}, visitorIdentifier: ${visitorIdentifier}, featureFlag: ${featureFlag}, targetingData: ${targetingData}, packageInfo: ${packageInfo}, clientConfiguration, dataManager, withAssignment: ${withAssignment})`;
7158
7225
  const { rules, featureKey, id: featureFlagId, defaultVariationKey, } = featureFlag;
7226
+ const consent = clientConfiguration.isConsentRequired
7227
+ ? legalConsent
7228
+ : LegalConsent.Given;
7159
7229
  for (const rule of rules) {
7160
7230
  const { segment, experimentId, id, exposition, respoolTime, variationByExposition, } = rule;
7161
7231
  const forcedVariationData = this.getForcedExperimentVariation(visitorCode, rule.experimentId);
@@ -7202,6 +7272,16 @@ class VariationConfiguration {
7202
7272
  });
7203
7273
  KameleoonLogger.debug `Calculated ruleHash: ${ruleHash} for code: ${visitorIdentifier}`;
7204
7274
  if (ruleHash <= exposition) {
7275
+ // Checking if the evaluation is blocked due to the consent policy
7276
+ if (consent == LegalConsent.NotGiven &&
7277
+ rule.type == RuleType.EXPERIMENTATION) {
7278
+ const behaviour = clientConfiguration.consentBlockingBehaviour;
7279
+ if (behaviour == ConsentBlockingBehaviour.PartiallyBlocked) {
7280
+ break;
7281
+ }
7282
+ return buildExports.Err(new KameleoonError(exports.KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of ${rule} is blocked because consent is not provided for visitor '${visitorCode}'`));
7283
+ }
7284
+ // evaluate with CB scores if applicable
7205
7285
  let variation = this.evaluateCBScores(rule, visitorIdentifier, targetingData);
7206
7286
  if (!variation) {
7207
7287
  const variationHash = Hasher.getHashDouble({
@@ -7563,7 +7643,6 @@ class KameleoonEventSource {
7563
7643
  const VISITOR_CODE_LENGTH = 16;
7564
7644
  const VISITOR_CODE_MAX_LENGTH = 255;
7565
7645
  const DEFAULT_MAX_AGE = 60 * 60 * 24 * 365;
7566
- const ZERO_MAX_AGE = 0;
7567
7646
  const PATH = '/';
7568
7647
 
7569
7648
  /**
@@ -8031,7 +8110,7 @@ class Tracker {
8031
8110
  this.dataManager.storeTrackedData(updatedData);
8032
8111
  }
8033
8112
  else {
8034
- this.dataManager.storeData(updatedData);
8113
+ this.dataManager.storeData(updatedData, false);
8035
8114
  }
8036
8115
  }
8037
8116
  getUnsentVisitorData(visitorCode, isConsentProvided) {
@@ -8383,16 +8462,23 @@ class KameleoonClient {
8383
8462
  const variations = new Map();
8384
8463
  const featureFlags = this.clientConfiguration.featureFlags;
8385
8464
  for (const featureFlag of featureFlags.values()) {
8386
- const variation = this._getFeatureVariation({
8387
- visitorCode,
8388
- featureKey: featureFlag.featureKey,
8389
- track,
8390
- });
8391
- if (variation.ok) {
8392
- if (!onlyActive || variation.data.key !== OFF_VARIATION_KEY) {
8465
+ try {
8466
+ const variation = this._getFeatureVariation({
8467
+ visitorCode,
8468
+ featureKey: featureFlag.featureKey,
8469
+ track,
8470
+ });
8471
+ if (variation.ok &&
8472
+ (!onlyActive || variation.data.key !== OFF_VARIATION_KEY)) {
8393
8473
  variations.set(featureFlag.featureKey, variation.data);
8394
8474
  }
8395
8475
  }
8476
+ catch (err) {
8477
+ if (err instanceof KameleoonError &&
8478
+ err.type !== exports.KameleoonException.FeatureFlagEnvironmentDisabled) {
8479
+ throw err;
8480
+ }
8481
+ }
8396
8482
  }
8397
8483
  KameleoonLogger.info `RETURN: KameleoonClient.getVariations(visitorCode: ${visitorCode}, onlyActive: ${onlyActive}, track: ${track}) -> (variations: ${variations})`;
8398
8484
  return variations;
@@ -8712,25 +8798,16 @@ class KameleoonClient {
8712
8798
  path: PATH,
8713
8799
  });
8714
8800
  }
8715
- else {
8716
- if (this.visitorCodeManager.consentRequired) {
8717
- setData({
8718
- visitorCode: '',
8719
- key: exports.KameleoonStorageKey.VisitorCode,
8720
- maxAge: ZERO_MAX_AGE,
8721
- path: PATH,
8722
- });
8723
- }
8724
- }
8725
8801
  KameleoonLogger.info `RETURN: KameleoonClient.setUserConsent(visitorCode: ${visitorCode}, consent: ${consent}, setData: ${setData})`;
8726
8802
  }
8727
8803
  updateConsentData(visitorCode, consent) {
8728
8804
  const readResult = this.consentDataStorage.read();
8805
+ const legalConsent = consent ? LegalConsent.Given : LegalConsent.NotGiven;
8729
8806
  if (!readResult.ok) {
8730
8807
  if (readResult.error.type === exports.KameleoonException.StorageEmpty) {
8731
8808
  this.consentDataStorage.write({
8732
8809
  [visitorCode]: {
8733
- consent,
8810
+ consent: legalConsent,
8734
8811
  },
8735
8812
  });
8736
8813
  }
@@ -8738,28 +8815,36 @@ class KameleoonClient {
8738
8815
  }
8739
8816
  const data = readResult.data;
8740
8817
  data[visitorCode] = {
8741
- consent,
8818
+ consent: legalConsent,
8742
8819
  };
8743
8820
  this.consentDataStorage.write(data);
8744
8821
  }
8822
+ getLegalConsent(visitorCode) {
8823
+ KameleoonLogger.debug `CALL: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode})`;
8824
+ let legalConsent;
8825
+ const consentDataResult = this.consentDataStorage.read();
8826
+ legalConsent = consentDataResult.ok
8827
+ ? this.extractLegalConsent(consentDataResult.data[visitorCode])
8828
+ : LegalConsent.Unknown;
8829
+ KameleoonLogger.debug `RETURN: KameleoonClient.getLegalConsent(visitorCode: ${visitorCode}) -> (legalConsent: ${legalConsent})`;
8830
+ return legalConsent;
8831
+ }
8832
+ extractLegalConsent(consentData) {
8833
+ if (consentData === undefined)
8834
+ return LegalConsent.Unknown;
8835
+ if (typeof consentData === 'boolean') {
8836
+ return consentData ? LegalConsent.Given : LegalConsent.NotGiven;
8837
+ }
8838
+ const value = consentData.consent;
8839
+ if (typeof value === 'boolean')
8840
+ return value ? LegalConsent.Given : LegalConsent.NotGiven;
8841
+ return value;
8842
+ }
8745
8843
  _isConsentProvided(visitorCode) {
8746
8844
  KameleoonLogger.debug `CALL: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode})`;
8747
8845
  const { isConsentRequired } = this.clientConfiguration;
8748
- const consentDataResult = this.consentDataStorage.read();
8749
- let isConsentProvided = false;
8750
- if (!isConsentRequired) {
8751
- isConsentProvided = true;
8752
- }
8753
- else if (consentDataResult.ok) {
8754
- const consentData = consentDataResult.data[visitorCode];
8755
- // for consistency with the old data in the storage
8756
- if (typeof consentData === 'boolean') {
8757
- isConsentProvided = consentData;
8758
- }
8759
- else {
8760
- isConsentProvided = consentData && consentData.consent;
8761
- }
8762
- }
8846
+ const isConsentProvided = !isConsentRequired ||
8847
+ this.getLegalConsent(visitorCode) == LegalConsent.Given;
8763
8848
  KameleoonLogger.debug `RETURN: KameleoonClient._isConsentProvided(visitorCode: ${visitorCode}) -> (isConsentProvided: ${isConsentProvided})`;
8764
8849
  return isConsentProvided;
8765
8850
  }
@@ -8787,22 +8872,32 @@ class KameleoonClient {
8787
8872
  if (!featureFlag.environmentEnabled) {
8788
8873
  continue;
8789
8874
  }
8790
- const evalExp = this._evaluate({
8791
- visitorCode,
8792
- featureFlag,
8793
- track: false,
8794
- save: false,
8795
- });
8796
- if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8797
- activeVariations.push({
8798
- variationKey: evalExp.variationKey,
8799
- variationId: evalExp.variationId,
8800
- experimentId: evalExp.experimentId,
8801
- featureFlagId: featureFlag.id,
8802
- featureKey: featureFlag.featureKey,
8803
- rule: null,
8804
- isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8875
+ try {
8876
+ const evalExp = this._evaluate({
8877
+ visitorCode,
8878
+ featureFlag,
8879
+ track: false,
8880
+ save: false,
8805
8881
  });
8882
+ if (evalExp.variationKey !== OFF_VARIATION_KEY) {
8883
+ activeVariations.push({
8884
+ variationKey: evalExp.variationKey,
8885
+ variationId: evalExp.variationId,
8886
+ experimentId: evalExp.experimentId,
8887
+ featureFlagId: featureFlag.id,
8888
+ featureKey: featureFlag.featureKey,
8889
+ rule: null,
8890
+ isTargetedRule: evalExp.ruleType === RuleType.TARGETED_DELIVERY,
8891
+ });
8892
+ }
8893
+ }
8894
+ catch (err) {
8895
+ if (err instanceof KameleoonError) {
8896
+ if (err.type != exports.KameleoonException.FeatureFlagEnvironmentDisabled) {
8897
+ KameleoonLogger.error `Unexpected error: ${err}`;
8898
+ }
8899
+ continue;
8900
+ }
8806
8901
  }
8807
8902
  }
8808
8903
  KameleoonLogger.debug `RETURN: KameleoonClient._getActiveFeatureVariations(visitorCode: ${visitorCode}) -> (activeVariations: ${activeVariations})`;
@@ -8823,6 +8918,7 @@ class KameleoonClient {
8823
8918
  else if (this._isVisitorNotInHoldout(visitorCode, track, save, featureFlag, visitorData) &&
8824
8919
  this._isFFUnrestrictedByMEGroup(visitorCode, featureFlag, visitorData)) {
8825
8920
  const visitorIdentifier = this._getCodeForHash(visitorCode, featureFlag.bucketingCustomDataIndex, visitorData);
8921
+ const legalConsent = this.getLegalConsent(visitorCode);
8826
8922
  const variationData = this.variationConfiguration
8827
8923
  .getVariation({
8828
8924
  visitorCode,
@@ -8834,6 +8930,7 @@ class KameleoonClient {
8834
8930
  clientConfiguration: this.clientConfiguration,
8835
8931
  dataManager: this.dataManager,
8836
8932
  packageInfo: this.externalPackageInfo,
8933
+ legalConsent,
8837
8934
  })
8838
8935
  .throw();
8839
8936
  evalExp =
@@ -8952,6 +9049,16 @@ class KameleoonClient {
8952
9049
  }
8953
9050
  KameleoonLogger.debug `CALL: KameleoonClient._isVisitorNotInHoldout(visitorCode: ${visitorCode}, track: ${track}, save: ${save}, featureFlag: ${featureFlag}, visitorData: ${visitorData})`;
8954
9051
  let isNotInHoldout = true;
9052
+ // Checking if the evaluation is blocked due to the consent policy
9053
+ const legalConsent = this.clientConfiguration.isConsentRequired
9054
+ ? this.getLegalConsent(visitorCode)
9055
+ : LegalConsent.Given;
9056
+ if (legalConsent == LegalConsent.NotGiven) {
9057
+ const behaviour = this.clientConfiguration.consentBlockingBehaviour;
9058
+ if (behaviour == ConsentBlockingBehaviour.CompletelyBlocked) {
9059
+ throw new KameleoonError(exports.KameleoonException.FeatureFlagEnvironmentDisabled, `Evaluation of holdout is blocked because consent is not provided for visitor '${visitorCode}'`);
9060
+ }
9061
+ }
8955
9062
  const codeForHash = this._getCodeForHash(visitorCode, featureFlag === null || featureFlag === void 0 ? void 0 : featureFlag.bucketingCustomDataIndex, visitorData);
8956
9063
  const holdoutHash = Hasher.getHashDouble({
8957
9064
  visitorIdentifier: codeForHash,