expf-sigma-node.js 3.2.4 → 3.2.5

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/sigma.js +125 -107
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expf-sigma-node.js",
3
- "version": "3.2.4",
3
+ "version": "3.2.5",
4
4
  "description": "expf-sigma-node.js lets you manage features flags and remote config across web, server side applications. Deliver true Continuous Integration. Get builds out faster. Control who has access to new features.",
5
5
  "main": "public/sigma.js",
6
6
  "keywords": [
package/public/sigma.js CHANGED
@@ -784,9 +784,36 @@ var MD5 = new sigmaHashes_default.MD5();
784
784
  var expirationDate = 24 * 60 * 60 * 1e3;
785
785
  var secondsIn24Hours = 24 * 60 * 60;
786
786
  var sigmaGeoData = "sigmaGeoData";
787
+ var defaultGeoCacheTTL = 5 * 60;
788
+ var cacheStandardTimeToLive = 3600;
789
+ var cacheCheckPeriod = 600;
790
+ var errorMessages = {
791
+ emptyToken: "Please specify a token",
792
+ APIRequestErrorConfig: {
793
+ token: "%token%",
794
+ userData: "%userData%",
795
+ detail: "Invalid request to sigma config json",
796
+ time: new Date().toLocaleString("ru-RU"),
797
+ cache: true
798
+ },
799
+ APIRequestErrorGeoData: {
800
+ token: "%token%",
801
+ userData: "%userData%",
802
+ detail: "Invalid request to sigma geo data",
803
+ time: new Date().toLocaleString("ru-RU")
804
+ },
805
+ saveLastUpdateError: "Save last update to cache",
806
+ isNoExperimentInCache: "Experiments not found in cache",
807
+ badConfig: "Bad config.json"
808
+ };
809
+ var signsForHash = ["equal", "not equal", "any of", "none of"];
810
+ var prefixGroup = "sigma_group";
787
811
 
788
812
  // src/modules/sigmaCache.js
789
- var sigmaCache = new import_node_cache.default({ stdTTL: secondsIn24Hours });
813
+ var sigmaCache = new import_node_cache.default({
814
+ stdTTL: cacheStandardTimeToLive,
815
+ checkperiod: cacheCheckPeriod
816
+ });
790
817
  var SigmaCache = class {
791
818
  constructor(postfix) {
792
819
  this.postfix = "";
@@ -825,14 +852,16 @@ var SigmaCache = class {
825
852
 
826
853
  // src/modules/sigmaGeoCache.js
827
854
  var import_node_cache2 = __toESM(require("node-cache"));
828
- var defaultGeoCacheTTL = 5 * 60;
829
- var maxGeoCacheTTL = 120 * 60;
830
- var minGeoCacheTTL = 1 * 60;
831
- var sigmaGeoCache = new import_node_cache2.default({ stdTTL: secondsIn24Hours });
855
+ var sigmaGeoCache = new import_node_cache2.default({
856
+ stdTTL: cacheStandardTimeToLive,
857
+ checkperiod: cacheCheckPeriod
858
+ });
832
859
  var SigmaGeoCache = class {
860
+ maxGeoCacheTTL = 120 * 60;
861
+ minGeoCacheTTL = 1 * 60;
833
862
  constructor(postfix, geoCacheTTL) {
834
863
  this.geoCacheTTL = geoCacheTTL || defaultGeoCacheTTL;
835
- if (geoCacheTTL > maxGeoCacheTTL || geoCacheTTL < minGeoCacheTTL) {
864
+ if (geoCacheTTL > this.maxGeoCacheTTL || geoCacheTTL < this.minGeoCacheTTL) {
836
865
  this.geoCacheTTL = defaultGeoCacheTTL;
837
866
  }
838
867
  this.postfix = "";
@@ -856,6 +885,12 @@ var SigmaGeoCache = class {
856
885
  keys() {
857
886
  return sigmaGeoCache.keys();
858
887
  }
888
+ parse(data) {
889
+ if (!this.get(data)) {
890
+ throw new Error(`${data} not found in cache`);
891
+ }
892
+ return JSON.parse(String(this.get(data)));
893
+ }
859
894
  };
860
895
 
861
896
  // src/helpers/checkBoolValue.js
@@ -910,20 +945,20 @@ function conditionOperation(userValue, conditionValues, operation, date, time) {
910
945
  return false;
911
946
  userValue = String(userValue);
912
947
  if (!Array.isArray(conditionValues))
913
- conditionValues = String(conditionValues);
948
+ conditionValues = String(conditionValues).toLowerCase().trim();
914
949
  switch (operation.trim()) {
915
950
  case "equal":
916
- return userValue.toLowerCase().trim() == conditionValues.toLowerCase().trim();
951
+ return userValue.toLowerCase().trim() == conditionValues;
917
952
  case "not equal":
918
- return userValue.toLowerCase().trim() != conditionValues.toLowerCase().trim();
953
+ return userValue.toLowerCase().trim() != conditionValues;
919
954
  case "greater":
920
- return compareVersions(userValue.trim(), conditionValues.trim());
955
+ return compareVersions(userValue.trim(), conditionValues);
921
956
  case "less":
922
- return compareVersions(conditionValues.trim(), userValue.trim());
957
+ return compareVersions(conditionValues, userValue.trim());
923
958
  case "greater equal":
924
- return compareVersions(userValue.trim(), conditionValues.trim(), false);
959
+ return compareVersions(userValue.trim(), conditionValues, false);
925
960
  case "less equal":
926
- return compareVersions(conditionValues.trim(), userValue.trim(), false);
961
+ return compareVersions(conditionValues, userValue.trim(), false);
927
962
  case "any of":
928
963
  return conditionValues.includes(userValue);
929
964
  case "none of":
@@ -943,13 +978,11 @@ function conditionOperation(userValue, conditionValues, operation, date, time) {
943
978
  }
944
979
  return true;
945
980
  case "on date":
946
- return Math.floor(
947
- new Date(conditionValues.trim()).getTime() / (1e3 * 60 * 60 * 24)
948
- ) == date;
981
+ return Math.floor(new Date(conditionValues.toString()).getTime() / (1e3 * 60 * 60 * 24)) == date;
949
982
  case "after time":
950
- return new Date(conditionValues.trim()).getTime() / (1e3 * 60 * 60) < time;
983
+ return new Date(conditionValues.toString()).getTime() / (1e3 * 60 * 60) < time;
951
984
  case "before time":
952
- return new Date(conditionValues.trim()).getTime() / (1e3 * 60 * 60) > time;
985
+ return new Date(conditionValues.toString()).getTime() / (1e3 * 60 * 60) > time;
953
986
  case "starts with":
954
987
  return userValue.startsWith(conditionValues);
955
988
  case "not starts with":
@@ -1070,17 +1103,17 @@ var Sigma = class {
1070
1103
  geoCacheTTL: null
1071
1104
  }) {
1072
1105
  if (!token) {
1073
- throw new Error("Please specify a token");
1106
+ throw new Error(errorMessages.emptyToken);
1074
1107
  }
1075
1108
  this.token = token;
1076
1109
  this.userData = userData || {};
1077
1110
  cacheTTL < 10 ? this.cacheTTL = 10 : this.cacheTTL = cacheTTL;
1078
- this.api = options.api || `${defaultApi}`;
1079
- this.retries = options.retries || 3;
1080
- this.sigmaUserData = new SigmaUserData();
1111
+ this.api = options.api || defaultApi;
1112
+ this.retries = options.retries || 2;
1081
1113
  this.postfix = options.postfix || "";
1114
+ this.geoCacheTTL = options.geoCacheTTL || defaultGeoCacheTTL;
1115
+ this.sigmaUserData = new SigmaUserData();
1082
1116
  this.cache = new SigmaCache(this.postfix);
1083
- this.geoCache = new SigmaGeoCache(`${this.userData.ip}__${this.token}`, options.geoCacheTTL);
1084
1117
  this.sigmaUserData.init({
1085
1118
  userId: this.userData.userId || null,
1086
1119
  profileId: this.userData.profileId || null,
@@ -1092,7 +1125,7 @@ var Sigma = class {
1092
1125
  deviceCategory: this.userData.deviceCategory || null,
1093
1126
  browser: this.userData.browser || { version: null, name: null },
1094
1127
  os: this.userData.os || { version: null, name: null },
1095
- geo: this.userData.ip ? this.geoCache.get(sigmaGeoData) : this.userData.geo || this.sigmaUserData.user.geo,
1128
+ geo: this.userData.geo || null,
1096
1129
  domain: this.userData.domain || null,
1097
1130
  url: this.userData.url || null,
1098
1131
  query: this.userData.query || null,
@@ -1100,6 +1133,7 @@ var Sigma = class {
1100
1133
  });
1101
1134
  this.setSplitIdsToCache(this.userData);
1102
1135
  this.makePrivateIdInCache();
1136
+ this.geoCache = new SigmaGeoCache(`${this.cache.get(sigmaPrivateId)}__${this.token}`, options.geoCacheTTL);
1103
1137
  }
1104
1138
  setSplitIdsToCache(userData) {
1105
1139
  for (let [key, value] of Object.entries(userData)) {
@@ -1120,12 +1154,9 @@ var Sigma = class {
1120
1154
  }
1121
1155
  }
1122
1156
  makePrivateIdInCache() {
1123
- let privateId = this.cache.get(sigmaPrivateId);
1124
- if (!privateId) {
1125
- privateId = generateRandomId();
1126
- this.cache.set(sigmaPrivateId, privateId);
1157
+ if (!this.cache.get(sigmaPrivateId)) {
1158
+ this.cache.set(sigmaPrivateId, generateRandomId());
1127
1159
  }
1128
- return privateId;
1129
1160
  }
1130
1161
  retryFetch(url, fetchOptions, retries = 3, timeout) {
1131
1162
  return new Promise((resolve, reject) => {
@@ -1134,7 +1165,7 @@ var Sigma = class {
1134
1165
  const wrapper = (n) => {
1135
1166
  (0, import_node_fetch.default)(url, fetchOptions).then((res) => resolve(res)).catch((err) => {
1136
1167
  if (n > 0) {
1137
- delay(1e3).then(() => {
1168
+ delay(0).then(() => {
1138
1169
  wrapper(--n);
1139
1170
  });
1140
1171
  } else {
@@ -1157,85 +1188,70 @@ var Sigma = class {
1157
1188
  "user-id": this.cache.get(sigmaPrivateId)
1158
1189
  }
1159
1190
  };
1160
- if (method !== "GET") {
1161
- options.headers["Content-Type"] = "application/json; charset=utf-8";
1162
- }
1163
1191
  const data = await this.retryFetch(
1164
1192
  url,
1165
1193
  options,
1166
1194
  this.retries
1167
1195
  );
1168
- if (data.status !== 200) {
1169
- throw new Error(`${data.status} Token: ${this.token}`);
1170
- }
1171
1196
  return data.json();
1172
1197
  }
1198
+ async getConfig() {
1199
+ let data;
1200
+ const localStorageHash = this.cache.get(sigmaHash);
1201
+ let hash;
1202
+ data = await this.getDataFile(`${this.api}/config.json`);
1203
+ if (this.sigmaUserData.platform) {
1204
+ data = filterByPlatform(data, this.sigmaUserData.user.platform);
1205
+ }
1206
+ hash = await SHA256.hex(JSON.stringify(data));
1207
+ if (!localStorageHash || localStorageHash !== hash) {
1208
+ this.cache.set(sigmaHash, hash);
1209
+ this.cache.set(sigmaDataFile, JSON.stringify(data));
1210
+ }
1211
+ }
1173
1212
  async getUserGeoData() {
1174
- try {
1175
- const data = await this.getDataFile(`${this.api}/geo`);
1176
- return data;
1177
- } catch (error) {
1178
- throw new Error(`Get geo: ${error}`);
1213
+ if (this.userData.ip) {
1214
+ const userGeoData = await this.getDataFile(`${this.api}/geo`);
1215
+ this.geoCache.set(sigmaGeoData, JSON.stringify(userGeoData));
1216
+ } else if (this.userData.geo) {
1217
+ this.geoCache.set(sigmaGeoData, JSON.stringify(this.userData.geo));
1218
+ } else {
1219
+ this.geoCache.set(sigmaGeoData, JSON.stringify({ location: "none" }));
1179
1220
  }
1180
1221
  }
1222
+ recordLastUpdateInCache() {
1223
+ const timeRecord = Math.floor(Date.now() / 1e3);
1224
+ this.cache.set(sigmaDataFileLastUpdate, timeRecord);
1225
+ }
1181
1226
  async saveToCache() {
1182
- const { api } = this;
1183
- const localStorageHash = this.cache.get(sigmaHash);
1184
- let data;
1185
1227
  try {
1186
- data = await this.getDataFile(`${api}/config.json`);
1187
- } catch (error) {
1228
+ await this.getConfig();
1229
+ } catch {
1230
+ errorMessages.APIRequestErrorConfig.userData = JSON.stringify(this.userData);
1231
+ errorMessages.APIRequestErrorConfig.token = this.token;
1188
1232
  if (!this.cache.get(sigmaDataFile)) {
1189
- throw new Error(` Get config: ${error}`);
1233
+ errorMessages.APIRequestErrorConfig.cache = false;
1234
+ console.error(errorMessages.APIRequestErrorConfig);
1235
+ throw new Error(JSON.stringify(errorMessages.APIRequestErrorConfig));
1190
1236
  } else {
1191
- console.error(`Get config: ${error}`);
1192
- return true;
1237
+ errorMessages.APIRequestErrorConfig.cache = true;
1238
+ console.error(errorMessages.APIRequestErrorConfig);
1193
1239
  }
1194
1240
  }
1195
- let hash;
1196
- try {
1197
- if (typeof data !== "object") {
1198
- throw new Error(`typeof config.json is not an object`);
1199
- }
1200
- data = filterByPlatform(data, this.userData.platform || this.sigmaUserData.platform);
1201
- hash = await SHA256.hex(JSON.stringify(data));
1202
- if (!localStorageHash || localStorageHash !== hash) {
1203
- this.cache.set(sigmaHash, hash);
1204
- this.cache.set(sigmaDataFile, JSON.stringify(data));
1205
- this.sigmaUserData.clearFlags();
1206
- }
1207
- } catch (error) {
1208
- throw new Error(`Save data to local storage`);
1209
- }
1210
1241
  try {
1211
- if (this.userData.ip) {
1212
- const userGeoData = await this.getUserGeoData();
1213
- this.geoCache.set(sigmaGeoData, JSON.stringify(userGeoData));
1214
- this.sigmaUserData.setGeo(userGeoData);
1215
- } else if (this.userData.geo) {
1216
- this.geoCache.set(sigmaGeoData, JSON.stringify(this.userData.geo));
1217
- this.sigmaUserData.setGeo(this.userData.geo);
1218
- } else {
1242
+ await this.getUserGeoData();
1243
+ } catch {
1244
+ if (!this.geoCache.get(sigmaGeoData)) {
1219
1245
  this.geoCache.set(sigmaGeoData, JSON.stringify({ location: "none" }));
1220
- this.sigmaUserData.setGeo({ location: "none" });
1221
1246
  }
1222
- } catch {
1223
- this.geoCache.set(sigmaGeoData, JSON.stringify({ location: "none" }));
1224
- this.sigmaUserData.setGeo({ location: "none" });
1225
- }
1226
- try {
1227
- const timeRecord = Math.floor(Date.now() / 1e3);
1228
- this.cache.set(sigmaDataFileLastUpdate, timeRecord);
1229
- } catch (error) {
1230
- throw new Error(`save last update to cache: ${error},
1231
- token: ${this.token}
1232
- `);
1247
+ errorMessages.APIRequestErrorGeoData.token = this.token;
1248
+ errorMessages.APIRequestErrorGeoData.userData = JSON.stringify(this.userData);
1249
+ console.error(errorMessages.APIRequestErrorGeoData);
1233
1250
  }
1251
+ this.recordLastUpdateInCache();
1234
1252
  }
1235
1253
  hasExpireCache() {
1236
- const lastUpdate = parseInt(
1237
- this.cache.get(sigmaDataFileLastUpdate)
1238
- );
1254
+ const lastUpdate = Number(this.cache.get(sigmaDataFileLastUpdate));
1239
1255
  const currentTime = Math.floor(Date.now() / 1e3);
1240
1256
  if (currentTime > lastUpdate + this.cacheTTL) {
1241
1257
  return true;
@@ -1256,13 +1272,13 @@ var Sigma = class {
1256
1272
  return flags.feature_flags;
1257
1273
  }
1258
1274
  async checkFlag(flagName) {
1259
- if (!flagName) {
1275
+ if (!flagName)
1260
1276
  return null;
1261
- }
1262
1277
  await this.updateCache();
1263
1278
  const cacheKey = this.cache.parse(sigmaDataFile);
1264
1279
  if (!cacheKey.feature_flags)
1265
1280
  return;
1281
+ this.sigmaUserData.setGeo(this.geoCache.parse(sigmaGeoData));
1266
1282
  let flag = null;
1267
1283
  const expName = this.getExperimentByFeatureFlag(cacheKey, flagName);
1268
1284
  if (expName) {
@@ -1321,9 +1337,8 @@ var Sigma = class {
1321
1337
  if (!child)
1322
1338
  return false;
1323
1339
  const isSetParentInUserData = Object.keys(user).find((i) => i === parent);
1324
- if (!user[isSetParentInUserData]) {
1340
+ if (!user[isSetParentInUserData])
1325
1341
  return false;
1326
- }
1327
1342
  const userObject = Object.entries(user[isSetParentInUserData]);
1328
1343
  for (let [userParamKey, userParamValue] of userObject) {
1329
1344
  if (userParamKey == child) {
@@ -1368,17 +1383,13 @@ var Sigma = class {
1368
1383
  }
1369
1384
  return null;
1370
1385
  }
1371
- getSplitById(experiment) {
1372
- if (!experiment)
1373
- return;
1386
+ switchSplitById(splitBy) {
1374
1387
  let id = null;
1375
- if (!experiment.split_by) {
1376
- experiment.split_by = "userId";
1377
- }
1378
- switch (experiment.split_by) {
1388
+ const cacheSigmaExpiration = this.cache.get(sigmaExpiration);
1389
+ switch (splitBy) {
1379
1390
  case "userId":
1380
1391
  id = this.cache.get(sigmaUserId) || null;
1381
- if (this.cache.get(sigmaExpiration) && parseInt(new Date().getTime()) >= parseInt(this.cache.get(sigmaExpiration))) {
1392
+ if (typeof cacheSigmaExpiration !== "undefined" && new Date().getTime() >= Number(cacheSigmaExpiration)) {
1382
1393
  id = null;
1383
1394
  }
1384
1395
  break;
@@ -1391,6 +1402,14 @@ var Sigma = class {
1391
1402
  }
1392
1403
  return id;
1393
1404
  }
1405
+ getSplitById(experiment) {
1406
+ if (!experiment)
1407
+ return null;
1408
+ if (!experiment.split_by) {
1409
+ experiment.split_by = "userId";
1410
+ }
1411
+ return this.switchSplitById(experiment.split_by);
1412
+ }
1394
1413
  checkTargetConditions(experiment) {
1395
1414
  if (experiment.targets && experiment.targets.conditions && experiment.targets.conditions.length > 0) {
1396
1415
  const targets = experiment.targets;
@@ -1416,7 +1435,7 @@ var Sigma = class {
1416
1435
  return false;
1417
1436
  }
1418
1437
  checkConditionSignForHash(condition) {
1419
- return ["equal", "not equal", "any of", "none of"].includes(condition);
1438
+ return signsForHash.includes(condition);
1420
1439
  }
1421
1440
  experimentDefinition(experiment, forcedUserInGroup) {
1422
1441
  if (forcedUserInGroup) {
@@ -1429,14 +1448,13 @@ var Sigma = class {
1429
1448
  return null;
1430
1449
  }
1431
1450
  async getExperiment(experimentName) {
1432
- if (!experimentName) {
1451
+ if (!experimentName)
1433
1452
  return null;
1434
- }
1435
1453
  await this.updateCache();
1436
1454
  const sigmaDataLs = await this.cache.parse(sigmaDataFile);
1437
- if (!sigmaDataLs.experiments) {
1438
- throw new Error("Experiments not found in cache");
1439
- }
1455
+ this.sigmaUserData.setGeo(this.geoCache.parse(sigmaGeoData));
1456
+ if (!sigmaDataLs.experiments)
1457
+ throw new Error(errorMessages.isNoExperimentInCache);
1440
1458
  let experiment = null;
1441
1459
  let layer = null;
1442
1460
  let bounds = [];
@@ -1457,7 +1475,7 @@ var Sigma = class {
1457
1475
  break;
1458
1476
  }
1459
1477
  }
1460
- const localStorageGroupName = `sigma_group_${experimentName}`;
1478
+ const localStorageGroupName = `${prefixGroup}_${experimentName}`;
1461
1479
  if (experiment) {
1462
1480
  if (!groupName) {
1463
1481
  groupName = this.calcUserInGroup(
@@ -1597,7 +1615,7 @@ var Sigma = class {
1597
1615
  }
1598
1616
  }
1599
1617
  if (!flagDefaultResult) {
1600
- throw new Error("bad config.json");
1618
+ throw new Error(errorMessages.badConfig);
1601
1619
  }
1602
1620
  for (let flagIndex in flagRules) {
1603
1621
  const statement = flagRules[flagIndex].conditional_statement;