expf-sigma-node.js 3.2.3 → 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 +124 -106
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expf-sigma-node.js",
3
- "version": "3.2.3",
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
@@ -783,9 +783,37 @@ var SHA256 = new sigmaHashes_default.SHA256();
783
783
  var MD5 = new sigmaHashes_default.MD5();
784
784
  var expirationDate = 24 * 60 * 60 * 1e3;
785
785
  var secondsIn24Hours = 24 * 60 * 60;
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";
786
811
 
787
812
  // src/modules/sigmaCache.js
788
- var sigmaCache = new import_node_cache.default({ stdTTL: secondsIn24Hours });
813
+ var sigmaCache = new import_node_cache.default({
814
+ stdTTL: cacheStandardTimeToLive,
815
+ checkperiod: cacheCheckPeriod
816
+ });
789
817
  var SigmaCache = class {
790
818
  constructor(postfix) {
791
819
  this.postfix = "";
@@ -824,14 +852,16 @@ var SigmaCache = class {
824
852
 
825
853
  // src/modules/sigmaGeoCache.js
826
854
  var import_node_cache2 = __toESM(require("node-cache"));
827
- var defaultGeoCacheTTL = 5 * 60;
828
- var maxGeoCacheTTL = 120 * 60;
829
- var minGeoCacheTTL = 1 * 60;
830
- var sigmaGeoCache = new import_node_cache2.default({ stdTTL: secondsIn24Hours });
855
+ var sigmaGeoCache = new import_node_cache2.default({
856
+ stdTTL: cacheStandardTimeToLive,
857
+ checkperiod: cacheCheckPeriod
858
+ });
831
859
  var SigmaGeoCache = class {
860
+ maxGeoCacheTTL = 120 * 60;
861
+ minGeoCacheTTL = 1 * 60;
832
862
  constructor(postfix, geoCacheTTL) {
833
863
  this.geoCacheTTL = geoCacheTTL || defaultGeoCacheTTL;
834
- if (geoCacheTTL > maxGeoCacheTTL || geoCacheTTL < minGeoCacheTTL) {
864
+ if (geoCacheTTL > this.maxGeoCacheTTL || geoCacheTTL < this.minGeoCacheTTL) {
835
865
  this.geoCacheTTL = defaultGeoCacheTTL;
836
866
  }
837
867
  this.postfix = "";
@@ -855,6 +885,12 @@ var SigmaGeoCache = class {
855
885
  keys() {
856
886
  return sigmaGeoCache.keys();
857
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
+ }
858
894
  };
859
895
 
860
896
  // src/helpers/checkBoolValue.js
@@ -909,20 +945,20 @@ function conditionOperation(userValue, conditionValues, operation, date, time) {
909
945
  return false;
910
946
  userValue = String(userValue);
911
947
  if (!Array.isArray(conditionValues))
912
- conditionValues = String(conditionValues);
948
+ conditionValues = String(conditionValues).toLowerCase().trim();
913
949
  switch (operation.trim()) {
914
950
  case "equal":
915
- return userValue.toLowerCase().trim() == conditionValues.toLowerCase().trim();
951
+ return userValue.toLowerCase().trim() == conditionValues;
916
952
  case "not equal":
917
- return userValue.toLowerCase().trim() != conditionValues.toLowerCase().trim();
953
+ return userValue.toLowerCase().trim() != conditionValues;
918
954
  case "greater":
919
- return compareVersions(userValue.trim(), conditionValues.trim());
955
+ return compareVersions(userValue.trim(), conditionValues);
920
956
  case "less":
921
- return compareVersions(conditionValues.trim(), userValue.trim());
957
+ return compareVersions(conditionValues, userValue.trim());
922
958
  case "greater equal":
923
- return compareVersions(userValue.trim(), conditionValues.trim(), false);
959
+ return compareVersions(userValue.trim(), conditionValues, false);
924
960
  case "less equal":
925
- return compareVersions(conditionValues.trim(), userValue.trim(), false);
961
+ return compareVersions(conditionValues, userValue.trim(), false);
926
962
  case "any of":
927
963
  return conditionValues.includes(userValue);
928
964
  case "none of":
@@ -942,13 +978,11 @@ function conditionOperation(userValue, conditionValues, operation, date, time) {
942
978
  }
943
979
  return true;
944
980
  case "on date":
945
- return Math.floor(
946
- new Date(conditionValues.trim()).getTime() / (1e3 * 60 * 60 * 24)
947
- ) == date;
981
+ return Math.floor(new Date(conditionValues.toString()).getTime() / (1e3 * 60 * 60 * 24)) == date;
948
982
  case "after time":
949
- return new Date(conditionValues.trim()).getTime() / (1e3 * 60 * 60) < time;
983
+ return new Date(conditionValues.toString()).getTime() / (1e3 * 60 * 60) < time;
950
984
  case "before time":
951
- return new Date(conditionValues.trim()).getTime() / (1e3 * 60 * 60) > time;
985
+ return new Date(conditionValues.toString()).getTime() / (1e3 * 60 * 60) > time;
952
986
  case "starts with":
953
987
  return userValue.startsWith(conditionValues);
954
988
  case "not starts with":
@@ -1069,15 +1103,16 @@ var Sigma = class {
1069
1103
  geoCacheTTL: null
1070
1104
  }) {
1071
1105
  if (!token) {
1072
- throw new Error("Please specify a token");
1106
+ throw new Error(errorMessages.emptyToken);
1073
1107
  }
1074
1108
  this.token = token;
1075
1109
  this.userData = userData || {};
1076
1110
  cacheTTL < 10 ? this.cacheTTL = 10 : this.cacheTTL = cacheTTL;
1077
- this.api = options.api || `${defaultApi}`;
1078
- this.retries = options.retries || 3;
1079
- this.sigmaUserData = new SigmaUserData();
1111
+ this.api = options.api || defaultApi;
1112
+ this.retries = options.retries || 2;
1080
1113
  this.postfix = options.postfix || "";
1114
+ this.geoCacheTTL = options.geoCacheTTL || defaultGeoCacheTTL;
1115
+ this.sigmaUserData = new SigmaUserData();
1081
1116
  this.cache = new SigmaCache(this.postfix);
1082
1117
  this.sigmaUserData.init({
1083
1118
  userId: this.userData.userId || null,
@@ -1096,9 +1131,9 @@ var Sigma = class {
1096
1131
  query: this.userData.query || null,
1097
1132
  pathname: this.userData.pathname || null
1098
1133
  });
1099
- this.geoCache = new SigmaGeoCache(this.token, options.geoCacheTTL);
1100
1134
  this.setSplitIdsToCache(this.userData);
1101
1135
  this.makePrivateIdInCache();
1136
+ this.geoCache = new SigmaGeoCache(`${this.cache.get(sigmaPrivateId)}__${this.token}`, options.geoCacheTTL);
1102
1137
  }
1103
1138
  setSplitIdsToCache(userData) {
1104
1139
  for (let [key, value] of Object.entries(userData)) {
@@ -1119,12 +1154,9 @@ var Sigma = class {
1119
1154
  }
1120
1155
  }
1121
1156
  makePrivateIdInCache() {
1122
- let privateId = this.cache.get(sigmaPrivateId);
1123
- if (!privateId) {
1124
- privateId = generateRandomId();
1125
- this.cache.set(sigmaPrivateId, privateId);
1157
+ if (!this.cache.get(sigmaPrivateId)) {
1158
+ this.cache.set(sigmaPrivateId, generateRandomId());
1126
1159
  }
1127
- return privateId;
1128
1160
  }
1129
1161
  retryFetch(url, fetchOptions, retries = 3, timeout) {
1130
1162
  return new Promise((resolve, reject) => {
@@ -1133,7 +1165,7 @@ var Sigma = class {
1133
1165
  const wrapper = (n) => {
1134
1166
  (0, import_node_fetch.default)(url, fetchOptions).then((res) => resolve(res)).catch((err) => {
1135
1167
  if (n > 0) {
1136
- delay(1e3).then(() => {
1168
+ delay(0).then(() => {
1137
1169
  wrapper(--n);
1138
1170
  });
1139
1171
  } else {
@@ -1156,84 +1188,70 @@ var Sigma = class {
1156
1188
  "user-id": this.cache.get(sigmaPrivateId)
1157
1189
  }
1158
1190
  };
1159
- if (method !== "GET") {
1160
- options.headers["Content-Type"] = "application/json; charset=utf-8";
1161
- }
1162
1191
  const data = await this.retryFetch(
1163
1192
  url,
1164
1193
  options,
1165
1194
  this.retries
1166
1195
  );
1167
- if (data.status !== 200) {
1168
- throw new Error(`${data.status} Token: ${this.token}`);
1169
- }
1170
1196
  return data.json();
1171
1197
  }
1172
- async getUserGeoData() {
1173
- try {
1174
- const data = await this.getDataFile(`${this.api}/geo`);
1175
- return data;
1176
- } catch (error) {
1177
- throw new Error(`Get geo: ${error}`);
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));
1178
1210
  }
1179
1211
  }
1180
- async setUserGeoData() {
1181
- if (!this.userData.ip)
1182
- return;
1183
- const geoKey = `${this.userData.ip}__${this.token}`;
1184
- const foundingGeoKey = this.geoCache.keys().find((k) => k === geoKey);
1185
- if (foundingGeoKey) {
1186
- const userGeoData = this.geoCache.get(this.userData.ip);
1187
- this.sigmaUserData.setGeo(userGeoData);
1188
- return;
1212
+ async getUserGeoData() {
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));
1189
1218
  } else {
1190
- const userGeoData = await this.getUserGeoData();
1191
- this.geoCache.set(this.userData.ip, userGeoData);
1192
- this.sigmaUserData.setGeo(userGeoData);
1219
+ this.geoCache.set(sigmaGeoData, JSON.stringify({ location: "none" }));
1193
1220
  }
1194
1221
  }
1222
+ recordLastUpdateInCache() {
1223
+ const timeRecord = Math.floor(Date.now() / 1e3);
1224
+ this.cache.set(sigmaDataFileLastUpdate, timeRecord);
1225
+ }
1195
1226
  async saveToCache() {
1196
- const { api } = this;
1197
- const localStorageHash = this.cache.get(sigmaHash);
1198
- try {
1199
- const timeRecord = Math.floor(Date.now() / 1e3);
1200
- this.cache.set(sigmaDataFileLastUpdate, timeRecord);
1201
- } catch (error) {
1202
- throw new Error(`save last update to cache: ${error},
1203
- token: ${this.token}
1204
- `);
1205
- }
1206
- let data;
1207
1227
  try {
1208
- data = await this.getDataFile(`${api}/config.json`);
1209
- } catch (error) {
1228
+ await this.getConfig();
1229
+ } catch {
1230
+ errorMessages.APIRequestErrorConfig.userData = JSON.stringify(this.userData);
1231
+ errorMessages.APIRequestErrorConfig.token = this.token;
1210
1232
  if (!this.cache.get(sigmaDataFile)) {
1211
- throw new Error(` Get config: ${error}`);
1233
+ errorMessages.APIRequestErrorConfig.cache = false;
1234
+ console.error(errorMessages.APIRequestErrorConfig);
1235
+ throw new Error(JSON.stringify(errorMessages.APIRequestErrorConfig));
1212
1236
  } else {
1213
- console.error(`Get config: ${error}`);
1214
- return true;
1237
+ errorMessages.APIRequestErrorConfig.cache = true;
1238
+ console.error(errorMessages.APIRequestErrorConfig);
1215
1239
  }
1216
1240
  }
1217
- let hash;
1218
1241
  try {
1219
- if (typeof data !== "object") {
1220
- throw new Error(`typeof config.json is not an object`);
1221
- }
1222
- data = filterByPlatform(data, this.userData.platform || this.sigmaUserData.platform);
1223
- hash = await SHA256.hex(JSON.stringify(data));
1224
- if (!localStorageHash || localStorageHash !== hash) {
1225
- this.cache.set(sigmaHash, hash);
1226
- this.cache.set(sigmaDataFile, JSON.stringify(data));
1227
- this.sigmaUserData.clearFlags();
1242
+ await this.getUserGeoData();
1243
+ } catch {
1244
+ if (!this.geoCache.get(sigmaGeoData)) {
1245
+ this.geoCache.set(sigmaGeoData, JSON.stringify({ location: "none" }));
1228
1246
  }
1229
- } catch (error) {
1230
- throw new Error(`Save data to local storage`);
1247
+ errorMessages.APIRequestErrorGeoData.token = this.token;
1248
+ errorMessages.APIRequestErrorGeoData.userData = JSON.stringify(this.userData);
1249
+ console.error(errorMessages.APIRequestErrorGeoData);
1231
1250
  }
1251
+ this.recordLastUpdateInCache();
1232
1252
  }
1233
1253
  hasExpireCache() {
1234
- const lastUpdate = parseInt(
1235
- this.cache.get(sigmaDataFileLastUpdate)
1236
- );
1254
+ const lastUpdate = Number(this.cache.get(sigmaDataFileLastUpdate));
1237
1255
  const currentTime = Math.floor(Date.now() / 1e3);
1238
1256
  if (currentTime > lastUpdate + this.cacheTTL) {
1239
1257
  return true;
@@ -1254,14 +1272,13 @@ var Sigma = class {
1254
1272
  return flags.feature_flags;
1255
1273
  }
1256
1274
  async checkFlag(flagName) {
1257
- if (!flagName) {
1275
+ if (!flagName)
1258
1276
  return null;
1259
- }
1260
1277
  await this.updateCache();
1261
- await this.setUserGeoData();
1262
1278
  const cacheKey = this.cache.parse(sigmaDataFile);
1263
1279
  if (!cacheKey.feature_flags)
1264
1280
  return;
1281
+ this.sigmaUserData.setGeo(this.geoCache.parse(sigmaGeoData));
1265
1282
  let flag = null;
1266
1283
  const expName = this.getExperimentByFeatureFlag(cacheKey, flagName);
1267
1284
  if (expName) {
@@ -1320,9 +1337,8 @@ var Sigma = class {
1320
1337
  if (!child)
1321
1338
  return false;
1322
1339
  const isSetParentInUserData = Object.keys(user).find((i) => i === parent);
1323
- if (!user[isSetParentInUserData]) {
1340
+ if (!user[isSetParentInUserData])
1324
1341
  return false;
1325
- }
1326
1342
  const userObject = Object.entries(user[isSetParentInUserData]);
1327
1343
  for (let [userParamKey, userParamValue] of userObject) {
1328
1344
  if (userParamKey == child) {
@@ -1367,17 +1383,13 @@ var Sigma = class {
1367
1383
  }
1368
1384
  return null;
1369
1385
  }
1370
- getSplitById(experiment) {
1371
- if (!experiment)
1372
- return;
1386
+ switchSplitById(splitBy) {
1373
1387
  let id = null;
1374
- if (!experiment.split_by) {
1375
- experiment.split_by = "userId";
1376
- }
1377
- switch (experiment.split_by) {
1388
+ const cacheSigmaExpiration = this.cache.get(sigmaExpiration);
1389
+ switch (splitBy) {
1378
1390
  case "userId":
1379
1391
  id = this.cache.get(sigmaUserId) || null;
1380
- if (this.cache.get(sigmaExpiration) && parseInt(new Date().getTime()) >= parseInt(this.cache.get(sigmaExpiration))) {
1392
+ if (typeof cacheSigmaExpiration !== "undefined" && new Date().getTime() >= Number(cacheSigmaExpiration)) {
1381
1393
  id = null;
1382
1394
  }
1383
1395
  break;
@@ -1390,6 +1402,14 @@ var Sigma = class {
1390
1402
  }
1391
1403
  return id;
1392
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
+ }
1393
1413
  checkTargetConditions(experiment) {
1394
1414
  if (experiment.targets && experiment.targets.conditions && experiment.targets.conditions.length > 0) {
1395
1415
  const targets = experiment.targets;
@@ -1415,7 +1435,7 @@ var Sigma = class {
1415
1435
  return false;
1416
1436
  }
1417
1437
  checkConditionSignForHash(condition) {
1418
- return ["equal", "not equal", "any of", "none of"].includes(condition);
1438
+ return signsForHash.includes(condition);
1419
1439
  }
1420
1440
  experimentDefinition(experiment, forcedUserInGroup) {
1421
1441
  if (forcedUserInGroup) {
@@ -1428,15 +1448,13 @@ var Sigma = class {
1428
1448
  return null;
1429
1449
  }
1430
1450
  async getExperiment(experimentName) {
1431
- if (!experimentName) {
1451
+ if (!experimentName)
1432
1452
  return null;
1433
- }
1434
1453
  await this.updateCache();
1435
- await this.setUserGeoData();
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;