@usertour/helpers 0.0.35 → 0.0.37

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.
@@ -114,27 +114,117 @@ var evaluateUrlCondition = (rules, url) => {
114
114
 
115
115
  // src/conditions/time.ts
116
116
  var import_date_fns = require("date-fns");
117
+
118
+ // src/type-utils.ts
119
+ var nativeIsArray = Array.isArray;
120
+ var ObjProto = Object.prototype;
121
+ var objToString = ObjProto.toString;
122
+ var objHasOwn = ObjProto.hasOwnProperty;
123
+ var isArray = nativeIsArray || ((obj) => objToString.call(obj) === "[object Array]");
124
+ var isUndefined = (x) => x === void 0;
125
+ var isString = (x) => {
126
+ return objToString.call(x) === "[object String]";
127
+ };
128
+ var isEmptyString = (x) => isString(x) && x.trim().length === 0;
129
+ var isNull = (x) => {
130
+ return x === null;
131
+ };
132
+ var isNullish = (x) => isUndefined(x) || isNull(x);
133
+
134
+ // src/conditions/time.ts
135
+ function isEmpty(value) {
136
+ return isNullish(value) || isEmptyString(value);
137
+ }
138
+ function hasRequiredFields(data, type) {
139
+ if (type === "start") {
140
+ return !isEmpty(data.startDate) && !isEmpty(data.startDateHour) && !isEmpty(data.startDateMinute);
141
+ }
142
+ return !isEmpty(data.endDate) && !isEmpty(data.endDateHour) && !isEmpty(data.endDateMinute);
143
+ }
144
+ function parseLegacyDate(dateStr) {
145
+ const [month, day, year] = dateStr.split("/");
146
+ if (!month || !day || !year) {
147
+ return null;
148
+ }
149
+ return {
150
+ month: Number.parseInt(month),
151
+ day: Number.parseInt(day),
152
+ year: Number.parseInt(year)
153
+ };
154
+ }
155
+ function createLegacyDateTime(data, type) {
156
+ const dateStr = type === "start" ? data.startDate : data.endDate;
157
+ const hourStr = type === "start" ? data.startDateHour : data.endDateHour;
158
+ const minuteStr = type === "start" ? data.startDateMinute : data.endDateMinute;
159
+ if (!dateStr || !hourStr || !minuteStr) {
160
+ return null;
161
+ }
162
+ const dateParts = parseLegacyDate(dateStr);
163
+ if (!dateParts) {
164
+ return null;
165
+ }
166
+ const dateTime = new Date(
167
+ dateParts.year,
168
+ dateParts.month - 1,
169
+ dateParts.day,
170
+ Number.parseInt(hourStr),
171
+ Number.parseInt(minuteStr),
172
+ 0
173
+ );
174
+ return (0, import_date_fns.isValid)(dateTime) ? dateTime : null;
175
+ }
176
+ function isTimeConditionDataV2(data) {
177
+ return data && ("startTime" in data || "endTime" in data);
178
+ }
179
+ function isTimeConditionDataLegacy(data) {
180
+ return data && ("startDate" in data || "startDateHour" in data);
181
+ }
182
+ function evaluateTimeConditionV2(data) {
183
+ if (!data.startTime) {
184
+ return false;
185
+ }
186
+ const startTime = (0, import_date_fns.parseISO)(data.startTime);
187
+ if (!(0, import_date_fns.isValid)(startTime)) {
188
+ return false;
189
+ }
190
+ const now = /* @__PURE__ */ new Date();
191
+ if (!data.endTime) {
192
+ return (0, import_date_fns.isAfter)(now, startTime);
193
+ }
194
+ const endTime = (0, import_date_fns.parseISO)(data.endTime);
195
+ if (!(0, import_date_fns.isValid)(endTime)) {
196
+ return false;
197
+ }
198
+ return (0, import_date_fns.isAfter)(now, startTime) && (0, import_date_fns.isBefore)(now, endTime);
199
+ }
200
+ function evaluateTimeConditionLegacy(data) {
201
+ if (!hasRequiredFields(data, "start")) {
202
+ return false;
203
+ }
204
+ const startDateTime = createLegacyDateTime(data, "start");
205
+ if (!startDateTime) {
206
+ return false;
207
+ }
208
+ const now = /* @__PURE__ */ new Date();
209
+ if (!hasRequiredFields(data, "end")) {
210
+ return (0, import_date_fns.isAfter)(now, startDateTime);
211
+ }
212
+ const endDateTime = createLegacyDateTime(data, "end");
213
+ if (!endDateTime) {
214
+ return false;
215
+ }
216
+ return (0, import_date_fns.isAfter)(now, startDateTime) && (0, import_date_fns.isBefore)(now, endDateTime);
217
+ }
117
218
  var evaluateTimeCondition = (rules) => {
118
219
  try {
119
- const { endDate, endDateHour, endDateMinute, startDate, startDateHour, startDateMinute } = rules.data || {};
120
- if (!startDate || !startDateHour || !startDateMinute) {
121
- return false;
122
- }
123
- const startTimeString = `${startDate}T${startDateHour.padStart(2, "0")}:${startDateMinute.padStart(2, "0")}:00`;
124
- const startTime = (0, import_date_fns.parseISO)(startTimeString);
125
- if (!(0, import_date_fns.isValid)(startTime)) {
126
- return false;
220
+ const data = rules.data || {};
221
+ if (isTimeConditionDataV2(data)) {
222
+ return evaluateTimeConditionV2(data);
127
223
  }
128
- const now = /* @__PURE__ */ new Date();
129
- if (!endDate || !endDateHour || !endDateMinute) {
130
- return (0, import_date_fns.isAfter)(now, startTime);
224
+ if (isTimeConditionDataLegacy(data)) {
225
+ return evaluateTimeConditionLegacy(data);
131
226
  }
132
- const endTimeString = `${endDate}T${endDateHour.padStart(2, "0")}:${endDateMinute.padStart(2, "0")}:00`;
133
- const endTime = (0, import_date_fns.parseISO)(endTimeString);
134
- if (!(0, import_date_fns.isValid)(endTime)) {
135
- return false;
136
- }
137
- return (0, import_date_fns.isAfter)(now, startTime) && (0, import_date_fns.isBefore)(now, endTime);
227
+ return false;
138
228
  } catch {
139
229
  return false;
140
230
  }
@@ -143,15 +233,6 @@ var evaluateTimeCondition = (rules) => {
143
233
  // src/conditions/attribute.ts
144
234
  var import_types = require("@usertour/types");
145
235
  var import_date_fns2 = require("date-fns");
146
-
147
- // src/type-utils.ts
148
- var nativeIsArray = Array.isArray;
149
- var ObjProto = Object.prototype;
150
- var objToString = ObjProto.toString;
151
- var objHasOwn = ObjProto.hasOwnProperty;
152
- var isArray = nativeIsArray || ((obj) => objToString.call(obj) === "[object Array]");
153
-
154
- // src/conditions/attribute.ts
155
236
  function evaluateAttributeCondition(condition, options) {
156
237
  const { data } = condition;
157
238
  if (!data) {
@@ -209,8 +290,8 @@ function evaluateStringCondition(logic, actualValue, expectedValue) {
209
290
  case "endsWith":
210
291
  return stringValue.endsWith(expectedValue);
211
292
  case "empty": {
212
- const isEmpty = !stringValue || stringValue === "";
213
- return isEmpty;
293
+ const isEmpty2 = !stringValue || stringValue === "";
294
+ return isEmpty2;
214
295
  }
215
296
  case "any":
216
297
  return Boolean(stringValue && stringValue !== "");
@@ -696,7 +777,8 @@ describe("evaluateRulesConditions", () => {
696
777
  operators: "and",
697
778
  actived: false,
698
779
  data: {
699
- startDate: "2024-01-01",
780
+ startDate: "01/01/2024",
781
+ // MM/dd/yyyy format
700
782
  startDateHour: "00",
701
783
  startDateMinute: "00"
702
784
  }
@@ -838,6 +920,902 @@ describe("evaluateRulesConditions", () => {
838
920
  });
839
921
  expect(result[2].actived).toBe(true);
840
922
  });
923
+ test("should evaluate String type attribute conditions with all logic operators", async () => {
924
+ const conditions = [
925
+ {
926
+ id: "string-is",
927
+ type: "user-attr",
928
+ operators: "and",
929
+ actived: false,
930
+ data: {
931
+ attrId: "email",
932
+ logic: "is",
933
+ value: "user@example.com"
934
+ }
935
+ },
936
+ {
937
+ id: "string-not",
938
+ type: "user-attr",
939
+ operators: "and",
940
+ actived: false,
941
+ data: {
942
+ attrId: "name",
943
+ logic: "not",
944
+ value: "admin"
945
+ }
946
+ },
947
+ {
948
+ id: "string-contains",
949
+ type: "user-attr",
950
+ operators: "and",
951
+ actived: false,
952
+ data: {
953
+ attrId: "description",
954
+ logic: "contains",
955
+ value: "premium"
956
+ }
957
+ },
958
+ {
959
+ id: "string-notContain",
960
+ type: "user-attr",
961
+ operators: "and",
962
+ actived: false,
963
+ data: {
964
+ attrId: "title",
965
+ logic: "notContain",
966
+ value: "test"
967
+ }
968
+ },
969
+ {
970
+ id: "string-startsWith",
971
+ type: "user-attr",
972
+ operators: "and",
973
+ actived: false,
974
+ data: {
975
+ attrId: "path",
976
+ logic: "startsWith",
977
+ value: "/api"
978
+ }
979
+ },
980
+ {
981
+ id: "string-endsWith",
982
+ type: "user-attr",
983
+ operators: "and",
984
+ actived: false,
985
+ data: {
986
+ attrId: "filename",
987
+ logic: "endsWith",
988
+ value: ".pdf"
989
+ }
990
+ },
991
+ {
992
+ id: "string-empty",
993
+ type: "user-attr",
994
+ operators: "and",
995
+ actived: false,
996
+ data: {
997
+ attrId: "emptyString",
998
+ logic: "empty"
999
+ }
1000
+ },
1001
+ {
1002
+ id: "string-any",
1003
+ type: "user-attr",
1004
+ operators: "and",
1005
+ actived: false,
1006
+ data: {
1007
+ attrId: "hasString",
1008
+ logic: "any"
1009
+ }
1010
+ }
1011
+ ];
1012
+ const options = {
1013
+ clientContext: mockOptions.clientContext,
1014
+ attributes: [
1015
+ {
1016
+ id: "email",
1017
+ codeName: "email",
1018
+ dataType: import_types3.BizAttributeTypes.String,
1019
+ bizType: import_types3.AttributeBizTypes.User
1020
+ },
1021
+ {
1022
+ id: "name",
1023
+ codeName: "name",
1024
+ dataType: import_types3.BizAttributeTypes.String,
1025
+ bizType: import_types3.AttributeBizTypes.User
1026
+ },
1027
+ {
1028
+ id: "description",
1029
+ codeName: "description",
1030
+ dataType: import_types3.BizAttributeTypes.String,
1031
+ bizType: import_types3.AttributeBizTypes.User
1032
+ },
1033
+ {
1034
+ id: "title",
1035
+ codeName: "title",
1036
+ dataType: import_types3.BizAttributeTypes.String,
1037
+ bizType: import_types3.AttributeBizTypes.User
1038
+ },
1039
+ {
1040
+ id: "path",
1041
+ codeName: "path",
1042
+ dataType: import_types3.BizAttributeTypes.String,
1043
+ bizType: import_types3.AttributeBizTypes.User
1044
+ },
1045
+ {
1046
+ id: "filename",
1047
+ codeName: "filename",
1048
+ dataType: import_types3.BizAttributeTypes.String,
1049
+ bizType: import_types3.AttributeBizTypes.User
1050
+ },
1051
+ {
1052
+ id: "emptyString",
1053
+ codeName: "emptyString",
1054
+ dataType: import_types3.BizAttributeTypes.String,
1055
+ bizType: import_types3.AttributeBizTypes.User
1056
+ },
1057
+ {
1058
+ id: "hasString",
1059
+ codeName: "hasString",
1060
+ dataType: import_types3.BizAttributeTypes.String,
1061
+ bizType: import_types3.AttributeBizTypes.User
1062
+ }
1063
+ ],
1064
+ userAttributes: {
1065
+ email: "user@example.com",
1066
+ name: "john",
1067
+ description: "premium user account",
1068
+ title: "Manager",
1069
+ path: "/api/users",
1070
+ filename: "document.pdf",
1071
+ emptyString: "",
1072
+ hasString: "some value"
1073
+ },
1074
+ typeControl: {
1075
+ [import_types3.RulesType.USER_ATTR]: true
1076
+ }
1077
+ };
1078
+ const result = await evaluateRulesConditions(conditions, options);
1079
+ expect(result[0].actived).toBe(true);
1080
+ expect(result[1].actived).toBe(true);
1081
+ expect(result[2].actived).toBe(true);
1082
+ expect(result[3].actived).toBe(true);
1083
+ expect(result[4].actived).toBe(true);
1084
+ expect(result[5].actived).toBe(true);
1085
+ expect(result[6].actived).toBe(true);
1086
+ expect(result[7].actived).toBe(true);
1087
+ });
1088
+ test("should evaluate Number type attribute conditions with all logic operators", async () => {
1089
+ const conditions = [
1090
+ {
1091
+ id: "number-is",
1092
+ type: "user-attr",
1093
+ operators: "and",
1094
+ actived: false,
1095
+ data: {
1096
+ attrId: "age",
1097
+ logic: "is",
1098
+ value: 25
1099
+ }
1100
+ },
1101
+ {
1102
+ id: "number-not",
1103
+ type: "user-attr",
1104
+ operators: "and",
1105
+ actived: false,
1106
+ data: {
1107
+ attrId: "age2",
1108
+ logic: "not",
1109
+ value: 30
1110
+ }
1111
+ },
1112
+ {
1113
+ id: "number-isLessThan",
1114
+ type: "user-attr",
1115
+ operators: "and",
1116
+ actived: false,
1117
+ data: {
1118
+ attrId: "score",
1119
+ logic: "isLessThan",
1120
+ value: 100
1121
+ }
1122
+ },
1123
+ {
1124
+ id: "number-isLessThanOrEqualTo",
1125
+ type: "user-attr",
1126
+ operators: "and",
1127
+ actived: false,
1128
+ data: {
1129
+ attrId: "score2",
1130
+ logic: "isLessThanOrEqualTo",
1131
+ value: 80
1132
+ }
1133
+ },
1134
+ {
1135
+ id: "number-isGreaterThan",
1136
+ type: "user-attr",
1137
+ operators: "and",
1138
+ actived: false,
1139
+ data: {
1140
+ attrId: "score3",
1141
+ logic: "isGreaterThan",
1142
+ value: 50
1143
+ }
1144
+ },
1145
+ {
1146
+ id: "number-isGreaterThanOrEqualTo",
1147
+ type: "user-attr",
1148
+ operators: "and",
1149
+ actived: false,
1150
+ data: {
1151
+ attrId: "score4",
1152
+ logic: "isGreaterThanOrEqualTo",
1153
+ value: 60
1154
+ }
1155
+ },
1156
+ {
1157
+ id: "number-between",
1158
+ type: "user-attr",
1159
+ operators: "and",
1160
+ actived: false,
1161
+ data: {
1162
+ attrId: "price",
1163
+ logic: "between",
1164
+ value: 10,
1165
+ value2: 100
1166
+ }
1167
+ },
1168
+ {
1169
+ id: "number-empty",
1170
+ type: "user-attr",
1171
+ operators: "and",
1172
+ actived: false,
1173
+ data: {
1174
+ attrId: "emptyValue",
1175
+ logic: "empty"
1176
+ }
1177
+ },
1178
+ {
1179
+ id: "number-any",
1180
+ type: "user-attr",
1181
+ operators: "and",
1182
+ actived: false,
1183
+ data: {
1184
+ attrId: "hasValue",
1185
+ logic: "any"
1186
+ }
1187
+ }
1188
+ ];
1189
+ const options = {
1190
+ clientContext: mockOptions.clientContext,
1191
+ attributes: [
1192
+ {
1193
+ id: "age",
1194
+ codeName: "age",
1195
+ dataType: import_types3.BizAttributeTypes.Number,
1196
+ bizType: import_types3.AttributeBizTypes.User
1197
+ },
1198
+ {
1199
+ id: "age2",
1200
+ codeName: "age2",
1201
+ dataType: import_types3.BizAttributeTypes.Number,
1202
+ bizType: import_types3.AttributeBizTypes.User
1203
+ },
1204
+ {
1205
+ id: "score",
1206
+ codeName: "score",
1207
+ dataType: import_types3.BizAttributeTypes.Number,
1208
+ bizType: import_types3.AttributeBizTypes.User
1209
+ },
1210
+ {
1211
+ id: "score2",
1212
+ codeName: "score2",
1213
+ dataType: import_types3.BizAttributeTypes.Number,
1214
+ bizType: import_types3.AttributeBizTypes.User
1215
+ },
1216
+ {
1217
+ id: "score3",
1218
+ codeName: "score3",
1219
+ dataType: import_types3.BizAttributeTypes.Number,
1220
+ bizType: import_types3.AttributeBizTypes.User
1221
+ },
1222
+ {
1223
+ id: "score4",
1224
+ codeName: "score4",
1225
+ dataType: import_types3.BizAttributeTypes.Number,
1226
+ bizType: import_types3.AttributeBizTypes.User
1227
+ },
1228
+ {
1229
+ id: "price",
1230
+ codeName: "price",
1231
+ dataType: import_types3.BizAttributeTypes.Number,
1232
+ bizType: import_types3.AttributeBizTypes.User
1233
+ },
1234
+ {
1235
+ id: "emptyValue",
1236
+ codeName: "emptyValue",
1237
+ dataType: import_types3.BizAttributeTypes.Number,
1238
+ bizType: import_types3.AttributeBizTypes.User
1239
+ },
1240
+ {
1241
+ id: "hasValue",
1242
+ codeName: "hasValue",
1243
+ dataType: import_types3.BizAttributeTypes.Number,
1244
+ bizType: import_types3.AttributeBizTypes.User
1245
+ }
1246
+ ],
1247
+ userAttributes: {
1248
+ age: 25,
1249
+ age2: 25,
1250
+ score: 90,
1251
+ score2: 80,
1252
+ score3: 70,
1253
+ score4: 60,
1254
+ price: 50,
1255
+ emptyValue: null,
1256
+ hasValue: 42
1257
+ },
1258
+ typeControl: {
1259
+ [import_types3.RulesType.USER_ATTR]: true
1260
+ }
1261
+ };
1262
+ const result = await evaluateRulesConditions(conditions, options);
1263
+ expect(result[0].actived).toBe(true);
1264
+ expect(result[1].actived).toBe(true);
1265
+ expect(result[2].actived).toBe(true);
1266
+ expect(result[3].actived).toBe(true);
1267
+ expect(result[4].actived).toBe(true);
1268
+ expect(result[5].actived).toBe(true);
1269
+ expect(result[6].actived).toBe(true);
1270
+ expect(result[7].actived).toBe(true);
1271
+ expect(result[8].actived).toBe(true);
1272
+ });
1273
+ test("should evaluate Boolean type attribute conditions with all logic operators", async () => {
1274
+ const conditions = [
1275
+ {
1276
+ id: "bool-true",
1277
+ type: "user-attr",
1278
+ operators: "and",
1279
+ actived: false,
1280
+ data: {
1281
+ attrId: "isVip",
1282
+ logic: "true"
1283
+ }
1284
+ },
1285
+ {
1286
+ id: "bool-false",
1287
+ type: "user-attr",
1288
+ operators: "and",
1289
+ actived: false,
1290
+ data: {
1291
+ attrId: "isActive",
1292
+ logic: "false"
1293
+ }
1294
+ },
1295
+ {
1296
+ id: "bool-empty",
1297
+ type: "user-attr",
1298
+ operators: "and",
1299
+ actived: false,
1300
+ data: {
1301
+ attrId: "emptyBool",
1302
+ logic: "empty"
1303
+ }
1304
+ },
1305
+ {
1306
+ id: "bool-any",
1307
+ type: "user-attr",
1308
+ operators: "and",
1309
+ actived: false,
1310
+ data: {
1311
+ attrId: "hasBool",
1312
+ logic: "any"
1313
+ }
1314
+ }
1315
+ ];
1316
+ const options = {
1317
+ clientContext: mockOptions.clientContext,
1318
+ attributes: [
1319
+ {
1320
+ id: "isVip",
1321
+ codeName: "isVip",
1322
+ dataType: import_types3.BizAttributeTypes.Boolean,
1323
+ bizType: import_types3.AttributeBizTypes.User
1324
+ },
1325
+ {
1326
+ id: "isActive",
1327
+ codeName: "isActive",
1328
+ dataType: import_types3.BizAttributeTypes.Boolean,
1329
+ bizType: import_types3.AttributeBizTypes.User
1330
+ },
1331
+ {
1332
+ id: "emptyBool",
1333
+ codeName: "emptyBool",
1334
+ dataType: import_types3.BizAttributeTypes.Boolean,
1335
+ bizType: import_types3.AttributeBizTypes.User
1336
+ },
1337
+ {
1338
+ id: "hasBool",
1339
+ codeName: "hasBool",
1340
+ dataType: import_types3.BizAttributeTypes.Boolean,
1341
+ bizType: import_types3.AttributeBizTypes.User
1342
+ }
1343
+ ],
1344
+ userAttributes: {
1345
+ isVip: true,
1346
+ isActive: false,
1347
+ emptyBool: null,
1348
+ hasBool: true
1349
+ },
1350
+ typeControl: {
1351
+ [import_types3.RulesType.USER_ATTR]: true
1352
+ }
1353
+ };
1354
+ const result = await evaluateRulesConditions(conditions, options);
1355
+ expect(result[0].actived).toBe(true);
1356
+ expect(result[1].actived).toBe(true);
1357
+ expect(result[2].actived).toBe(true);
1358
+ expect(result[3].actived).toBe(true);
1359
+ });
1360
+ test("should evaluate List type attribute conditions with all logic operators", async () => {
1361
+ const conditions = [
1362
+ {
1363
+ id: "list-includesAtLeastOne",
1364
+ type: "user-attr",
1365
+ operators: "and",
1366
+ actived: false,
1367
+ data: {
1368
+ attrId: "tags",
1369
+ logic: "includesAtLeastOne",
1370
+ listValues: ["premium", "vip"]
1371
+ }
1372
+ },
1373
+ {
1374
+ id: "list-includesAll",
1375
+ type: "user-attr",
1376
+ operators: "and",
1377
+ actived: false,
1378
+ data: {
1379
+ attrId: "roles",
1380
+ logic: "includesAll",
1381
+ listValues: ["admin", "editor"]
1382
+ }
1383
+ },
1384
+ {
1385
+ id: "list-notIncludesAtLeastOne",
1386
+ type: "user-attr",
1387
+ operators: "and",
1388
+ actived: false,
1389
+ data: {
1390
+ attrId: "permissions",
1391
+ logic: "notIncludesAtLeastOne",
1392
+ listValues: ["delete", "modify"]
1393
+ }
1394
+ },
1395
+ {
1396
+ id: "list-notIncludesAll",
1397
+ type: "user-attr",
1398
+ operators: "and",
1399
+ actived: false,
1400
+ data: {
1401
+ attrId: "categories",
1402
+ logic: "notIncludesAll",
1403
+ listValues: ["tech", "finance"]
1404
+ }
1405
+ },
1406
+ {
1407
+ id: "list-empty",
1408
+ type: "user-attr",
1409
+ operators: "and",
1410
+ actived: false,
1411
+ data: {
1412
+ attrId: "emptyList",
1413
+ logic: "empty"
1414
+ }
1415
+ },
1416
+ {
1417
+ id: "list-any",
1418
+ type: "user-attr",
1419
+ operators: "and",
1420
+ actived: false,
1421
+ data: {
1422
+ attrId: "hasList",
1423
+ logic: "any"
1424
+ }
1425
+ }
1426
+ ];
1427
+ const options = {
1428
+ clientContext: mockOptions.clientContext,
1429
+ attributes: [
1430
+ {
1431
+ id: "tags",
1432
+ codeName: "tags",
1433
+ dataType: import_types3.BizAttributeTypes.List,
1434
+ bizType: import_types3.AttributeBizTypes.User
1435
+ },
1436
+ {
1437
+ id: "roles",
1438
+ codeName: "roles",
1439
+ dataType: import_types3.BizAttributeTypes.List,
1440
+ bizType: import_types3.AttributeBizTypes.User
1441
+ },
1442
+ {
1443
+ id: "permissions",
1444
+ codeName: "permissions",
1445
+ dataType: import_types3.BizAttributeTypes.List,
1446
+ bizType: import_types3.AttributeBizTypes.User
1447
+ },
1448
+ {
1449
+ id: "categories",
1450
+ codeName: "categories",
1451
+ dataType: import_types3.BizAttributeTypes.List,
1452
+ bizType: import_types3.AttributeBizTypes.User
1453
+ },
1454
+ {
1455
+ id: "emptyList",
1456
+ codeName: "emptyList",
1457
+ dataType: import_types3.BizAttributeTypes.List,
1458
+ bizType: import_types3.AttributeBizTypes.User
1459
+ },
1460
+ {
1461
+ id: "hasList",
1462
+ codeName: "hasList",
1463
+ dataType: import_types3.BizAttributeTypes.List,
1464
+ bizType: import_types3.AttributeBizTypes.User
1465
+ }
1466
+ ],
1467
+ userAttributes: {
1468
+ tags: ["premium", "basic"],
1469
+ roles: ["admin", "editor", "viewer"],
1470
+ permissions: ["read", "write"],
1471
+ categories: ["other"],
1472
+ emptyList: [],
1473
+ hasList: ["item1", "item2"]
1474
+ },
1475
+ typeControl: {
1476
+ [import_types3.RulesType.USER_ATTR]: true
1477
+ }
1478
+ };
1479
+ const result = await evaluateRulesConditions(conditions, options);
1480
+ expect(result[0].actived).toBe(true);
1481
+ expect(result[1].actived).toBe(true);
1482
+ expect(result[2].actived).toBe(true);
1483
+ expect(result[3].actived).toBe(true);
1484
+ expect(result[4].actived).toBe(true);
1485
+ expect(result[5].actived).toBe(true);
1486
+ });
1487
+ test("should evaluate DateTime type attribute conditions with all logic operators", async () => {
1488
+ const now = /* @__PURE__ */ new Date();
1489
+ const fiveDaysAgo = new Date(now.getTime() - 5 * 24 * 60 * 60 * 1e3);
1490
+ const tenDaysAgo = new Date(now.getTime() - 10 * 24 * 60 * 60 * 1e3);
1491
+ const threeDaysAgo = new Date(now.getTime() - 3 * 24 * 60 * 60 * 1e3);
1492
+ const fifteenDaysAgo = new Date(now.getTime() - 15 * 24 * 60 * 60 * 1e3);
1493
+ const futureDate = new Date(now.getTime() + 5 * 24 * 60 * 60 * 1e3);
1494
+ const conditions = [
1495
+ {
1496
+ id: "datetime-lessThan",
1497
+ type: "user-attr",
1498
+ operators: "and",
1499
+ actived: false,
1500
+ data: {
1501
+ attrId: "lastLogin",
1502
+ logic: "lessThan",
1503
+ value: 7
1504
+ }
1505
+ },
1506
+ {
1507
+ id: "datetime-exactly",
1508
+ type: "user-attr",
1509
+ operators: "and",
1510
+ actived: false,
1511
+ data: {
1512
+ attrId: "exactDate",
1513
+ logic: "exactly",
1514
+ value: 3
1515
+ }
1516
+ },
1517
+ {
1518
+ id: "datetime-moreThan",
1519
+ type: "user-attr",
1520
+ operators: "and",
1521
+ actived: false,
1522
+ data: {
1523
+ attrId: "oldDate",
1524
+ logic: "moreThan",
1525
+ value: 7
1526
+ }
1527
+ },
1528
+ {
1529
+ id: "datetime-before",
1530
+ type: "user-attr",
1531
+ operators: "and",
1532
+ actived: false,
1533
+ data: {
1534
+ attrId: "createdAt",
1535
+ logic: "before",
1536
+ value: now.toISOString()
1537
+ }
1538
+ },
1539
+ {
1540
+ id: "datetime-on",
1541
+ type: "user-attr",
1542
+ operators: "and",
1543
+ actived: false,
1544
+ data: {
1545
+ attrId: "onDate",
1546
+ logic: "on",
1547
+ value: threeDaysAgo.toISOString()
1548
+ }
1549
+ },
1550
+ {
1551
+ id: "datetime-after",
1552
+ type: "user-attr",
1553
+ operators: "and",
1554
+ actived: false,
1555
+ data: {
1556
+ attrId: "futureDate",
1557
+ logic: "after",
1558
+ value: now.toISOString()
1559
+ }
1560
+ },
1561
+ {
1562
+ id: "datetime-empty",
1563
+ type: "user-attr",
1564
+ operators: "and",
1565
+ actived: false,
1566
+ data: {
1567
+ attrId: "emptyDate",
1568
+ logic: "empty"
1569
+ }
1570
+ },
1571
+ {
1572
+ id: "datetime-any",
1573
+ type: "user-attr",
1574
+ operators: "and",
1575
+ actived: false,
1576
+ data: {
1577
+ attrId: "hasDate",
1578
+ logic: "any"
1579
+ }
1580
+ }
1581
+ ];
1582
+ const options = {
1583
+ clientContext: mockOptions.clientContext,
1584
+ attributes: [
1585
+ {
1586
+ id: "lastLogin",
1587
+ codeName: "lastLogin",
1588
+ dataType: import_types3.BizAttributeTypes.DateTime,
1589
+ bizType: import_types3.AttributeBizTypes.User
1590
+ },
1591
+ {
1592
+ id: "exactDate",
1593
+ codeName: "exactDate",
1594
+ dataType: import_types3.BizAttributeTypes.DateTime,
1595
+ bizType: import_types3.AttributeBizTypes.User
1596
+ },
1597
+ {
1598
+ id: "oldDate",
1599
+ codeName: "oldDate",
1600
+ dataType: import_types3.BizAttributeTypes.DateTime,
1601
+ bizType: import_types3.AttributeBizTypes.User
1602
+ },
1603
+ {
1604
+ id: "createdAt",
1605
+ codeName: "createdAt",
1606
+ dataType: import_types3.BizAttributeTypes.DateTime,
1607
+ bizType: import_types3.AttributeBizTypes.User
1608
+ },
1609
+ {
1610
+ id: "onDate",
1611
+ codeName: "onDate",
1612
+ dataType: import_types3.BizAttributeTypes.DateTime,
1613
+ bizType: import_types3.AttributeBizTypes.User
1614
+ },
1615
+ {
1616
+ id: "futureDate",
1617
+ codeName: "futureDate",
1618
+ dataType: import_types3.BizAttributeTypes.DateTime,
1619
+ bizType: import_types3.AttributeBizTypes.User
1620
+ },
1621
+ {
1622
+ id: "emptyDate",
1623
+ codeName: "emptyDate",
1624
+ dataType: import_types3.BizAttributeTypes.DateTime,
1625
+ bizType: import_types3.AttributeBizTypes.User
1626
+ },
1627
+ {
1628
+ id: "hasDate",
1629
+ codeName: "hasDate",
1630
+ dataType: import_types3.BizAttributeTypes.DateTime,
1631
+ bizType: import_types3.AttributeBizTypes.User
1632
+ }
1633
+ ],
1634
+ userAttributes: {
1635
+ lastLogin: fiveDaysAgo.toISOString(),
1636
+ exactDate: threeDaysAgo.toISOString(),
1637
+ oldDate: fifteenDaysAgo.toISOString(),
1638
+ createdAt: tenDaysAgo.toISOString(),
1639
+ onDate: threeDaysAgo.toISOString(),
1640
+ futureDate: futureDate.toISOString(),
1641
+ emptyDate: "",
1642
+ hasDate: fiveDaysAgo.toISOString()
1643
+ },
1644
+ typeControl: {
1645
+ [import_types3.RulesType.USER_ATTR]: true
1646
+ }
1647
+ };
1648
+ const result = await evaluateRulesConditions(conditions, options);
1649
+ expect(result[0].actived).toBe(true);
1650
+ expect(result[1].actived).toBe(true);
1651
+ expect(result[2].actived).toBe(true);
1652
+ expect(result[3].actived).toBe(true);
1653
+ expect(result[4].actived).toBe(true);
1654
+ expect(result[5].actived).toBe(true);
1655
+ expect(result[6].actived).toBe(false);
1656
+ expect(result[7].actived).toBe(true);
1657
+ });
1658
+ test("should evaluate mixed attribute type conditions correctly", async () => {
1659
+ const conditions = [
1660
+ {
1661
+ id: "mixed-1",
1662
+ type: "user-attr",
1663
+ operators: "and",
1664
+ actived: false,
1665
+ data: {
1666
+ attrId: "email",
1667
+ logic: "is",
1668
+ value: "user@example.com"
1669
+ }
1670
+ },
1671
+ {
1672
+ id: "mixed-2",
1673
+ type: "user-attr",
1674
+ operators: "and",
1675
+ actived: false,
1676
+ data: {
1677
+ attrId: "age",
1678
+ logic: "isGreaterThan",
1679
+ value: 18
1680
+ }
1681
+ },
1682
+ {
1683
+ id: "mixed-3",
1684
+ type: "user-attr",
1685
+ operators: "and",
1686
+ actived: false,
1687
+ data: {
1688
+ attrId: "isVip",
1689
+ logic: "true"
1690
+ }
1691
+ },
1692
+ {
1693
+ id: "mixed-4",
1694
+ type: "user-attr",
1695
+ operators: "and",
1696
+ actived: false,
1697
+ data: {
1698
+ attrId: "tags",
1699
+ logic: "includesAtLeastOne",
1700
+ listValues: ["premium"]
1701
+ }
1702
+ }
1703
+ ];
1704
+ const options = {
1705
+ clientContext: mockOptions.clientContext,
1706
+ attributes: [
1707
+ {
1708
+ id: "email",
1709
+ codeName: "email",
1710
+ dataType: import_types3.BizAttributeTypes.String,
1711
+ bizType: import_types3.AttributeBizTypes.User
1712
+ },
1713
+ {
1714
+ id: "age",
1715
+ codeName: "age",
1716
+ dataType: import_types3.BizAttributeTypes.Number,
1717
+ bizType: import_types3.AttributeBizTypes.User
1718
+ },
1719
+ {
1720
+ id: "isVip",
1721
+ codeName: "isVip",
1722
+ dataType: import_types3.BizAttributeTypes.Boolean,
1723
+ bizType: import_types3.AttributeBizTypes.User
1724
+ },
1725
+ {
1726
+ id: "tags",
1727
+ codeName: "tags",
1728
+ dataType: import_types3.BizAttributeTypes.List,
1729
+ bizType: import_types3.AttributeBizTypes.User
1730
+ }
1731
+ ],
1732
+ userAttributes: {
1733
+ email: "user@example.com",
1734
+ age: 25,
1735
+ isVip: true,
1736
+ tags: ["premium", "basic"]
1737
+ },
1738
+ typeControl: {
1739
+ [import_types3.RulesType.USER_ATTR]: true
1740
+ }
1741
+ };
1742
+ const result = await evaluateRulesConditions(conditions, options);
1743
+ expect(result[0].actived).toBe(true);
1744
+ expect(result[1].actived).toBe(true);
1745
+ expect(result[2].actived).toBe(true);
1746
+ expect(result[3].actived).toBe(true);
1747
+ });
1748
+ test("should evaluate mixed attribute types with OR logic correctly", async () => {
1749
+ const conditions = [
1750
+ {
1751
+ id: "or-1",
1752
+ type: "user-attr",
1753
+ operators: "or",
1754
+ actived: false,
1755
+ data: {
1756
+ attrId: "email",
1757
+ logic: "is",
1758
+ value: "wrong@example.com"
1759
+ }
1760
+ },
1761
+ {
1762
+ id: "or-2",
1763
+ type: "user-attr",
1764
+ operators: "or",
1765
+ actived: false,
1766
+ data: {
1767
+ attrId: "age",
1768
+ logic: "isGreaterThan",
1769
+ value: 18
1770
+ }
1771
+ },
1772
+ {
1773
+ id: "or-3",
1774
+ type: "user-attr",
1775
+ operators: "or",
1776
+ actived: false,
1777
+ data: {
1778
+ attrId: "isVip",
1779
+ logic: "false"
1780
+ }
1781
+ }
1782
+ ];
1783
+ const options = {
1784
+ clientContext: mockOptions.clientContext,
1785
+ attributes: [
1786
+ {
1787
+ id: "email",
1788
+ codeName: "email",
1789
+ dataType: import_types3.BizAttributeTypes.String,
1790
+ bizType: import_types3.AttributeBizTypes.User
1791
+ },
1792
+ {
1793
+ id: "age",
1794
+ codeName: "age",
1795
+ dataType: import_types3.BizAttributeTypes.Number,
1796
+ bizType: import_types3.AttributeBizTypes.User
1797
+ },
1798
+ {
1799
+ id: "isVip",
1800
+ codeName: "isVip",
1801
+ dataType: import_types3.BizAttributeTypes.Boolean,
1802
+ bizType: import_types3.AttributeBizTypes.User
1803
+ }
1804
+ ],
1805
+ userAttributes: {
1806
+ email: "user@example.com",
1807
+ age: 25,
1808
+ isVip: true
1809
+ },
1810
+ typeControl: {
1811
+ [import_types3.RulesType.USER_ATTR]: true
1812
+ }
1813
+ };
1814
+ const result = await evaluateRulesConditions(conditions, options);
1815
+ expect(result[0].actived).toBe(false);
1816
+ expect(result[1].actived).toBe(true);
1817
+ expect(result[2].actived).toBe(false);
1818
+ });
841
1819
  test("should handle rules without ID", async () => {
842
1820
  const conditions = [
843
1821
  {