@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.
@@ -1,11 +1,16 @@
1
1
  import {
2
- evaluateTimeCondition
3
- } from "../chunk-CEK3SCQO.js";
2
+ convertTimeConditionLegacyToV2,
3
+ evaluateTimeCondition,
4
+ isTimeConditionDataLegacy,
5
+ isTimeConditionDataV2,
6
+ normalizeTimeConditionData
7
+ } from "../chunk-JQWKLXW6.js";
8
+ import "../chunk-GFH3VWOC.js";
4
9
  import "../chunk-XEO3YXBM.js";
5
10
 
6
11
  // src/__tests__/time.test.ts
7
12
  describe("Time Condition Evaluation", () => {
8
- const mockNow = /* @__PURE__ */ new Date("2024-01-15T12:00:00Z");
13
+ const mockNow = new Date(2024, 0, 15, 12, 0, 0);
9
14
  beforeAll(() => {
10
15
  jest.useFakeTimers();
11
16
  jest.setSystemTime(mockNow);
@@ -13,36 +18,352 @@ describe("Time Condition Evaluation", () => {
13
18
  afterAll(() => {
14
19
  jest.useRealTimers();
15
20
  });
16
- test("should return true when current time is after start time and no end time", () => {
17
- const rules = {
18
- id: "condition-1",
19
- type: "condition",
20
- operators: "and",
21
- data: {
22
- startDate: "2024-01-15",
21
+ describe("Type Guards", () => {
22
+ test("isTimeConditionDataV2 should return true for new format", () => {
23
+ expect(isTimeConditionDataV2({ startTime: "2024-01-15T10:00:00Z" })).toBe(true);
24
+ expect(isTimeConditionDataV2({ endTime: "2024-01-15T14:00:00Z" })).toBe(true);
25
+ expect(
26
+ isTimeConditionDataV2({
27
+ startTime: "2024-01-15T10:00:00Z",
28
+ endTime: "2024-01-15T14:00:00Z"
29
+ })
30
+ ).toBe(true);
31
+ });
32
+ test("isTimeConditionDataV2 should return false for legacy format", () => {
33
+ expect(isTimeConditionDataV2({ startDate: "01/15/2024" })).toBe(false);
34
+ expect(isTimeConditionDataV2({})).toBe(false);
35
+ });
36
+ test("isTimeConditionDataLegacy should return true for legacy format", () => {
37
+ expect(isTimeConditionDataLegacy({ startDate: "01/15/2024" })).toBe(true);
38
+ expect(isTimeConditionDataLegacy({ startDateHour: "10" })).toBe(true);
39
+ expect(
40
+ isTimeConditionDataLegacy({
41
+ startDate: "01/15/2024",
42
+ startDateHour: "10",
43
+ startDateMinute: "00"
44
+ })
45
+ ).toBe(true);
46
+ });
47
+ test("isTimeConditionDataLegacy should return false for new format", () => {
48
+ expect(isTimeConditionDataLegacy({ startTime: "2024-01-15T10:00:00Z" })).toBe(false);
49
+ expect(isTimeConditionDataLegacy({})).toBe(false);
50
+ });
51
+ });
52
+ describe("Format Conversion", () => {
53
+ test("convertTimeConditionLegacyToV2 should convert valid legacy data", () => {
54
+ const legacyData = {
55
+ startDate: "01/15/2024",
56
+ startDateHour: "10",
57
+ startDateMinute: "00",
58
+ endDate: "01/15/2024",
59
+ endDateHour: "14",
60
+ endDateMinute: "00"
61
+ };
62
+ const result = convertTimeConditionLegacyToV2(legacyData);
63
+ expect(result).not.toBeNull();
64
+ expect(result == null ? void 0 : result.startTime).toBeDefined();
65
+ expect(result == null ? void 0 : result.endTime).toBeDefined();
66
+ expect(result == null ? void 0 : result.startTime).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
67
+ });
68
+ test("convertTimeConditionLegacyToV2 should convert legacy data without end time", () => {
69
+ const legacyData = {
70
+ startDate: "01/15/2024",
23
71
  startDateHour: "10",
24
72
  startDateMinute: "00",
25
73
  endDate: "",
26
74
  endDateHour: "",
27
75
  endDateMinute: ""
28
- }
29
- };
30
- expect(evaluateTimeCondition(rules)).toBe(true);
31
- });
32
- test("should return true when current time is within start and end time range", () => {
33
- const rules = {
34
- id: "condition-2",
35
- type: "condition",
36
- operators: "and",
37
- data: {
38
- startDate: "2024-01-15",
76
+ };
77
+ const result = convertTimeConditionLegacyToV2(legacyData);
78
+ expect(result).not.toBeNull();
79
+ expect(result == null ? void 0 : result.startTime).toBeDefined();
80
+ expect(result == null ? void 0 : result.endTime).toBeUndefined();
81
+ });
82
+ test("convertTimeConditionLegacyToV2 should return null for invalid data", () => {
83
+ expect(
84
+ convertTimeConditionLegacyToV2({
85
+ startDate: "",
86
+ startDateHour: "10",
87
+ startDateMinute: "00"
88
+ })
89
+ ).toBeNull();
90
+ expect(
91
+ convertTimeConditionLegacyToV2({
92
+ startDate: "01/15/2024",
93
+ startDateHour: "",
94
+ startDateMinute: "00"
95
+ })
96
+ ).toBeNull();
97
+ expect(
98
+ convertTimeConditionLegacyToV2({
99
+ startDate: "01/15/2024",
100
+ startDateHour: "10",
101
+ startDateMinute: ""
102
+ })
103
+ ).toBeNull();
104
+ });
105
+ test("normalizeTimeConditionData should return new format as-is", () => {
106
+ const newFormat = { startTime: "2024-01-15T10:00:00Z", endTime: "2024-01-15T14:00:00Z" };
107
+ expect(normalizeTimeConditionData(newFormat)).toEqual(newFormat);
108
+ });
109
+ test("normalizeTimeConditionData should convert legacy format", () => {
110
+ const legacyData = {
111
+ startDate: "01/15/2024",
39
112
  startDateHour: "10",
40
- startDateMinute: "00",
41
- endDate: "2024-01-15",
42
- endDateHour: "14",
43
- endDateMinute: "00"
44
- }
45
- };
46
- expect(evaluateTimeCondition(rules)).toBe(false);
113
+ startDateMinute: "00"
114
+ };
115
+ const result = normalizeTimeConditionData(legacyData);
116
+ expect(result).not.toBeNull();
117
+ expect(result == null ? void 0 : result.startTime).toBeDefined();
118
+ });
119
+ test("normalizeTimeConditionData should return null for invalid data", () => {
120
+ expect(normalizeTimeConditionData(null)).toBeNull();
121
+ expect(normalizeTimeConditionData(void 0)).toBeNull();
122
+ expect(normalizeTimeConditionData({})).toBeNull();
123
+ });
124
+ });
125
+ describe("Legacy Format Evaluation", () => {
126
+ test("should return true when current time is after start time and no end time", () => {
127
+ const rules = {
128
+ id: "condition-1",
129
+ type: "time",
130
+ data: {
131
+ startDate: "01/15/2024",
132
+ startDateHour: "10",
133
+ startDateMinute: "00",
134
+ endDate: "",
135
+ endDateHour: "",
136
+ endDateMinute: ""
137
+ }
138
+ };
139
+ expect(evaluateTimeCondition(rules)).toBe(true);
140
+ });
141
+ test("should return true when current time is within start and end time range", () => {
142
+ const rules = {
143
+ id: "condition-2",
144
+ type: "time",
145
+ data: {
146
+ startDate: "01/15/2024",
147
+ startDateHour: "10",
148
+ startDateMinute: "00",
149
+ endDate: "01/15/2024",
150
+ endDateHour: "14",
151
+ endDateMinute: "00"
152
+ }
153
+ };
154
+ expect(evaluateTimeCondition(rules)).toBe(true);
155
+ });
156
+ test("should return false when current time is before start time", () => {
157
+ const rules = {
158
+ id: "condition-3",
159
+ type: "time",
160
+ data: {
161
+ startDate: "01/15/2024",
162
+ startDateHour: "15",
163
+ startDateMinute: "00",
164
+ endDate: "",
165
+ endDateHour: "",
166
+ endDateMinute: ""
167
+ }
168
+ };
169
+ expect(evaluateTimeCondition(rules)).toBe(false);
170
+ });
171
+ test("should return false when current time is after end time", () => {
172
+ const rules = {
173
+ id: "condition-4",
174
+ type: "time",
175
+ data: {
176
+ startDate: "01/15/2024",
177
+ startDateHour: "10",
178
+ startDateMinute: "00",
179
+ endDate: "01/15/2024",
180
+ endDateHour: "11",
181
+ endDateMinute: "00"
182
+ }
183
+ };
184
+ expect(evaluateTimeCondition(rules)).toBe(false);
185
+ });
186
+ test("should return false when start date is missing", () => {
187
+ const rules = {
188
+ id: "condition-5",
189
+ type: "time",
190
+ data: {
191
+ startDate: "",
192
+ startDateHour: "10",
193
+ startDateMinute: "00"
194
+ }
195
+ };
196
+ expect(evaluateTimeCondition(rules)).toBe(false);
197
+ });
198
+ test("should return false when start hour is missing", () => {
199
+ const rules = {
200
+ id: "condition-6",
201
+ type: "time",
202
+ data: {
203
+ startDate: "01/15/2024",
204
+ startDateHour: "",
205
+ startDateMinute: "00"
206
+ }
207
+ };
208
+ expect(evaluateTimeCondition(rules)).toBe(false);
209
+ });
210
+ test("should handle empty endDate string", () => {
211
+ const rules = {
212
+ id: "condition-7",
213
+ type: "time",
214
+ data: {
215
+ startDate: "01/15/2024",
216
+ startDateHour: "10",
217
+ startDateMinute: "00",
218
+ endDate: "",
219
+ endDateHour: "00",
220
+ endDateMinute: "00"
221
+ }
222
+ };
223
+ expect(evaluateTimeCondition(rules)).toBe(true);
224
+ });
225
+ test("should handle cross-day time range", () => {
226
+ const crossDayTime = new Date(2024, 0, 15, 23, 0, 0);
227
+ jest.setSystemTime(crossDayTime);
228
+ const rules = {
229
+ id: "condition-8",
230
+ type: "time",
231
+ data: {
232
+ startDate: "01/15/2024",
233
+ startDateHour: "22",
234
+ startDateMinute: "00",
235
+ endDate: "01/16/2024",
236
+ endDateHour: "02",
237
+ endDateMinute: "00"
238
+ }
239
+ };
240
+ expect(evaluateTimeCondition(rules)).toBe(true);
241
+ jest.setSystemTime(mockNow);
242
+ });
243
+ });
244
+ describe("New Format (ISO 8601) Evaluation", () => {
245
+ test("should return true when current time is after start time and no end time", () => {
246
+ const startTime = new Date(2024, 0, 15, 10, 0, 0).toISOString();
247
+ const rules = {
248
+ id: "condition-v2-1",
249
+ type: "time",
250
+ data: {
251
+ startTime
252
+ }
253
+ };
254
+ expect(evaluateTimeCondition(rules)).toBe(true);
255
+ });
256
+ test("should return true when current time is within start and end time range", () => {
257
+ const startTime = new Date(2024, 0, 15, 10, 0, 0).toISOString();
258
+ const endTime = new Date(2024, 0, 15, 14, 0, 0).toISOString();
259
+ const rules = {
260
+ id: "condition-v2-2",
261
+ type: "time",
262
+ data: {
263
+ startTime,
264
+ endTime
265
+ }
266
+ };
267
+ expect(evaluateTimeCondition(rules)).toBe(true);
268
+ });
269
+ test("should return false when current time is before start time", () => {
270
+ const startTime = new Date(2024, 0, 15, 15, 0, 0).toISOString();
271
+ const rules = {
272
+ id: "condition-v2-3",
273
+ type: "time",
274
+ data: {
275
+ startTime
276
+ }
277
+ };
278
+ expect(evaluateTimeCondition(rules)).toBe(false);
279
+ });
280
+ test("should return false when current time is after end time", () => {
281
+ const startTime = new Date(2024, 0, 15, 10, 0, 0).toISOString();
282
+ const endTime = new Date(2024, 0, 15, 11, 0, 0).toISOString();
283
+ const rules = {
284
+ id: "condition-v2-4",
285
+ type: "time",
286
+ data: {
287
+ startTime,
288
+ endTime
289
+ }
290
+ };
291
+ expect(evaluateTimeCondition(rules)).toBe(false);
292
+ });
293
+ test("should return false when startTime is missing", () => {
294
+ const endTime = new Date(2024, 0, 15, 14, 0, 0).toISOString();
295
+ const rules = {
296
+ id: "condition-v2-5",
297
+ type: "time",
298
+ data: {
299
+ endTime
300
+ }
301
+ };
302
+ expect(evaluateTimeCondition(rules)).toBe(false);
303
+ });
304
+ test("should return false when startTime is invalid", () => {
305
+ const rules = {
306
+ id: "condition-v2-6",
307
+ type: "time",
308
+ data: {
309
+ startTime: "invalid-date"
310
+ }
311
+ };
312
+ expect(evaluateTimeCondition(rules)).toBe(false);
313
+ });
314
+ test("should handle timezone in ISO format", () => {
315
+ const rules = {
316
+ id: "condition-v2-7",
317
+ type: "time",
318
+ data: {
319
+ startTime: "2024-01-15T10:00:00+08:00",
320
+ endTime: "2024-01-15T14:00:00+08:00"
321
+ }
322
+ };
323
+ expect(typeof evaluateTimeCondition(rules)).toBe("boolean");
324
+ });
325
+ });
326
+ describe("Edge Cases", () => {
327
+ test("should return false for empty data", () => {
328
+ const rules = {
329
+ id: "condition-edge-1",
330
+ type: "time",
331
+ data: {}
332
+ };
333
+ expect(evaluateTimeCondition(rules)).toBe(false);
334
+ });
335
+ test("should return false for null data", () => {
336
+ const rules = {
337
+ id: "condition-edge-2",
338
+ type: "time",
339
+ data: null
340
+ };
341
+ expect(evaluateTimeCondition(rules)).toBe(false);
342
+ });
343
+ test("should return false for invalid date format in legacy", () => {
344
+ const rules = {
345
+ id: "condition-edge-3",
346
+ type: "time",
347
+ data: {
348
+ startDate: "invalid-date",
349
+ startDateHour: "10",
350
+ startDateMinute: "00"
351
+ }
352
+ };
353
+ expect(evaluateTimeCondition(rules)).toBe(false);
354
+ });
355
+ test("should handle invalid month/day in legacy format", () => {
356
+ const rules = {
357
+ id: "condition-edge-4",
358
+ type: "time",
359
+ data: {
360
+ startDate: "13/32/2024",
361
+ // Invalid month and day
362
+ startDateHour: "10",
363
+ startDateMinute: "00"
364
+ }
365
+ };
366
+ expect(evaluateTimeCondition(rules)).toBe(false);
367
+ });
47
368
  });
48
369
  });
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  evaluateUrlCondition
3
3
  } from "./chunk-YYIGUZNZ.js";
4
- import {
5
- evaluateTimeCondition
6
- } from "./chunk-CEK3SCQO.js";
7
4
  import {
8
5
  evaluateAttributeCondition
9
6
  } from "./chunk-KYDXF7SU.js";
7
+ import {
8
+ evaluateTimeCondition
9
+ } from "./chunk-JQWKLXW6.js";
10
10
  import {
11
11
  cuid
12
12
  } from "./chunk-3KG2HTZ3.js";
@@ -0,0 +1,147 @@
1
+ import {
2
+ isEmptyString,
3
+ isNullish
4
+ } from "./chunk-GFH3VWOC.js";
5
+
6
+ // src/conditions/time.ts
7
+ import { isAfter, isBefore, isValid, parseISO } from "date-fns";
8
+ function isEmpty(value) {
9
+ return isNullish(value) || isEmptyString(value);
10
+ }
11
+ function hasRequiredFields(data, type) {
12
+ if (type === "start") {
13
+ return !isEmpty(data.startDate) && !isEmpty(data.startDateHour) && !isEmpty(data.startDateMinute);
14
+ }
15
+ return !isEmpty(data.endDate) && !isEmpty(data.endDateHour) && !isEmpty(data.endDateMinute);
16
+ }
17
+ function parseLegacyDate(dateStr) {
18
+ const [month, day, year] = dateStr.split("/");
19
+ if (!month || !day || !year) {
20
+ return null;
21
+ }
22
+ return {
23
+ month: Number.parseInt(month),
24
+ day: Number.parseInt(day),
25
+ year: Number.parseInt(year)
26
+ };
27
+ }
28
+ function createLegacyDateTime(data, type) {
29
+ const dateStr = type === "start" ? data.startDate : data.endDate;
30
+ const hourStr = type === "start" ? data.startDateHour : data.endDateHour;
31
+ const minuteStr = type === "start" ? data.startDateMinute : data.endDateMinute;
32
+ if (!dateStr || !hourStr || !minuteStr) {
33
+ return null;
34
+ }
35
+ const dateParts = parseLegacyDate(dateStr);
36
+ if (!dateParts) {
37
+ return null;
38
+ }
39
+ const dateTime = new Date(
40
+ dateParts.year,
41
+ dateParts.month - 1,
42
+ dateParts.day,
43
+ Number.parseInt(hourStr),
44
+ Number.parseInt(minuteStr),
45
+ 0
46
+ );
47
+ return isValid(dateTime) ? dateTime : null;
48
+ }
49
+ function isTimeConditionDataV2(data) {
50
+ return data && ("startTime" in data || "endTime" in data);
51
+ }
52
+ function isTimeConditionDataLegacy(data) {
53
+ return data && ("startDate" in data || "startDateHour" in data);
54
+ }
55
+ function convertTimeConditionLegacyToV2(legacyData) {
56
+ try {
57
+ if (!hasRequiredFields(legacyData, "start")) {
58
+ return null;
59
+ }
60
+ const startDateTime = createLegacyDateTime(legacyData, "start");
61
+ if (!startDateTime) {
62
+ return null;
63
+ }
64
+ const result = {
65
+ startTime: startDateTime.toISOString()
66
+ };
67
+ if (hasRequiredFields(legacyData, "end")) {
68
+ const endDateTime = createLegacyDateTime(legacyData, "end");
69
+ if (endDateTime) {
70
+ result.endTime = endDateTime.toISOString();
71
+ }
72
+ }
73
+ return result;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ function normalizeTimeConditionData(data) {
79
+ if (!data) {
80
+ return null;
81
+ }
82
+ if (isTimeConditionDataV2(data)) {
83
+ return data;
84
+ }
85
+ if (isTimeConditionDataLegacy(data)) {
86
+ return convertTimeConditionLegacyToV2(data);
87
+ }
88
+ return null;
89
+ }
90
+ function evaluateTimeConditionV2(data) {
91
+ if (!data.startTime) {
92
+ return false;
93
+ }
94
+ const startTime = parseISO(data.startTime);
95
+ if (!isValid(startTime)) {
96
+ return false;
97
+ }
98
+ const now = /* @__PURE__ */ new Date();
99
+ if (!data.endTime) {
100
+ return isAfter(now, startTime);
101
+ }
102
+ const endTime = parseISO(data.endTime);
103
+ if (!isValid(endTime)) {
104
+ return false;
105
+ }
106
+ return isAfter(now, startTime) && isBefore(now, endTime);
107
+ }
108
+ function evaluateTimeConditionLegacy(data) {
109
+ if (!hasRequiredFields(data, "start")) {
110
+ return false;
111
+ }
112
+ const startDateTime = createLegacyDateTime(data, "start");
113
+ if (!startDateTime) {
114
+ return false;
115
+ }
116
+ const now = /* @__PURE__ */ new Date();
117
+ if (!hasRequiredFields(data, "end")) {
118
+ return isAfter(now, startDateTime);
119
+ }
120
+ const endDateTime = createLegacyDateTime(data, "end");
121
+ if (!endDateTime) {
122
+ return false;
123
+ }
124
+ return isAfter(now, startDateTime) && isBefore(now, endDateTime);
125
+ }
126
+ var evaluateTimeCondition = (rules) => {
127
+ try {
128
+ const data = rules.data || {};
129
+ if (isTimeConditionDataV2(data)) {
130
+ return evaluateTimeConditionV2(data);
131
+ }
132
+ if (isTimeConditionDataLegacy(data)) {
133
+ return evaluateTimeConditionLegacy(data);
134
+ }
135
+ return false;
136
+ } catch {
137
+ return false;
138
+ }
139
+ };
140
+
141
+ export {
142
+ isTimeConditionDataV2,
143
+ isTimeConditionDataLegacy,
144
+ convertTimeConditionLegacyToV2,
145
+ normalizeTimeConditionData,
146
+ evaluateTimeCondition
147
+ };
@@ -1,3 +1,8 @@
1
+ import {
2
+ isTimeConditionDataLegacy,
3
+ isTimeConditionDataV2
4
+ } from "./chunk-JQWKLXW6.js";
5
+
1
6
  // src/error.ts
2
7
  import {
3
8
  AttributeDataType,
@@ -53,7 +58,21 @@ var getUrlPatternError = (data) => {
53
58
  };
54
59
  var getCurrentTimeError = (data) => {
55
60
  const ret = { showError: false, errorInfo: "" };
56
- if (!data.startDate && !data.endDate) {
61
+ if (isTimeConditionDataV2(data)) {
62
+ if (!data.startTime && !data.endTime) {
63
+ ret.showError = true;
64
+ ret.errorInfo = "Either start or end time be filled in";
65
+ }
66
+ return ret;
67
+ }
68
+ if (isTimeConditionDataLegacy(data)) {
69
+ if (!data.startDate && !data.endDate) {
70
+ ret.showError = true;
71
+ ret.errorInfo = "Either start or end time be filled in";
72
+ }
73
+ return ret;
74
+ }
75
+ if (!data || typeof data === "object" && Object.keys(data).length === 0) {
57
76
  ret.showError = true;
58
77
  ret.errorInfo = "Either start or end time be filled in";
59
78
  }