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.
- package/package.json +1 -1
- 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
|
+
"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({
|
|
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
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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
|
|
951
|
+
return userValue.toLowerCase().trim() == conditionValues;
|
|
916
952
|
case "not equal":
|
|
917
|
-
return userValue.toLowerCase().trim() != conditionValues
|
|
953
|
+
return userValue.toLowerCase().trim() != conditionValues;
|
|
918
954
|
case "greater":
|
|
919
|
-
return compareVersions(userValue.trim(), conditionValues
|
|
955
|
+
return compareVersions(userValue.trim(), conditionValues);
|
|
920
956
|
case "less":
|
|
921
|
-
return compareVersions(conditionValues
|
|
957
|
+
return compareVersions(conditionValues, userValue.trim());
|
|
922
958
|
case "greater equal":
|
|
923
|
-
return compareVersions(userValue.trim(), conditionValues
|
|
959
|
+
return compareVersions(userValue.trim(), conditionValues, false);
|
|
924
960
|
case "less equal":
|
|
925
|
-
return compareVersions(conditionValues
|
|
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.
|
|
983
|
+
return new Date(conditionValues.toString()).getTime() / (1e3 * 60 * 60) < time;
|
|
950
984
|
case "before time":
|
|
951
|
-
return new Date(conditionValues.
|
|
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(
|
|
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 ||
|
|
1078
|
-
this.retries = options.retries ||
|
|
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
|
-
|
|
1123
|
-
|
|
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(
|
|
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
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
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
|
|
1181
|
-
if (
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1209
|
-
} catch
|
|
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
|
-
|
|
1233
|
+
errorMessages.APIRequestErrorConfig.cache = false;
|
|
1234
|
+
console.error(errorMessages.APIRequestErrorConfig);
|
|
1235
|
+
throw new Error(JSON.stringify(errorMessages.APIRequestErrorConfig));
|
|
1212
1236
|
} else {
|
|
1213
|
-
|
|
1214
|
-
|
|
1237
|
+
errorMessages.APIRequestErrorConfig.cache = true;
|
|
1238
|
+
console.error(errorMessages.APIRequestErrorConfig);
|
|
1215
1239
|
}
|
|
1216
1240
|
}
|
|
1217
|
-
let hash;
|
|
1218
1241
|
try {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
-
|
|
1230
|
-
|
|
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 =
|
|
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
|
-
|
|
1371
|
-
if (!experiment)
|
|
1372
|
-
return;
|
|
1386
|
+
switchSplitById(splitBy) {
|
|
1373
1387
|
let id = null;
|
|
1374
|
-
|
|
1375
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
1438
|
-
|
|
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 =
|
|
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(
|
|
1618
|
+
throw new Error(errorMessages.badConfig);
|
|
1601
1619
|
}
|
|
1602
1620
|
for (let flagIndex in flagRules) {
|
|
1603
1621
|
const statement = flagRules[flagIndex].conditional_statement;
|