@cloudsnorkel/cdk-github-runners 0.14.23 → 0.15.0

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 (139) hide show
  1. package/.jsii +5397 -252
  2. package/API.md +1048 -24
  3. package/README.md +52 -0
  4. package/assets/delete-failed-runner.lambda/index.js +105 -9
  5. package/assets/idle-runner-repear.lambda/index.js +136 -14
  6. package/assets/image-builders/aws-image-builder/delete-resources.lambda/index.js +1 -1
  7. package/assets/image-builders/build-image.lambda/index.js +1 -1
  8. package/assets/providers/ami-root-device.lambda/index.js +1 -1
  9. package/assets/setup.lambda/index.html +7 -7
  10. package/assets/setup.lambda/index.js +101 -8
  11. package/assets/status.lambda/index.js +104 -8
  12. package/assets/token-retriever.lambda/index.js +104 -8
  13. package/assets/warm-runner-manager.lambda/index.js +5892 -0
  14. package/assets/webhook-handler.lambda/index.js +109 -11
  15. package/assets/webhook-redelivery.lambda/index.js +122 -24
  16. package/lib/access.js +1 -1
  17. package/lib/delete-failed-runner.lambda.js +2 -2
  18. package/lib/idle-runner-repear.lambda.js +33 -7
  19. package/lib/image-builders/api.js +1 -1
  20. package/lib/image-builders/aws-image-builder/base-image.d.ts +13 -0
  21. package/lib/image-builders/aws-image-builder/base-image.js +36 -3
  22. package/lib/image-builders/aws-image-builder/builder.js +4 -4
  23. package/lib/image-builders/aws-image-builder/delete-resources.lambda.js +2 -2
  24. package/lib/image-builders/aws-image-builder/deprecated/ami.js +1 -1
  25. package/lib/image-builders/aws-image-builder/deprecated/container.js +1 -1
  26. package/lib/image-builders/aws-image-builder/deprecated/linux-components.js +1 -1
  27. package/lib/image-builders/aws-image-builder/deprecated/windows-components.js +1 -1
  28. package/lib/image-builders/build-image.lambda.js +2 -2
  29. package/lib/image-builders/codebuild-deprecated.js +1 -1
  30. package/lib/image-builders/components.js +3 -3
  31. package/lib/image-builders/static.js +1 -1
  32. package/lib/index.d.ts +1 -0
  33. package/lib/index.js +2 -1
  34. package/lib/lambda-github.d.ts +1 -1
  35. package/lib/lambda-github.js +3 -2
  36. package/lib/lambda-helpers.js +4 -4
  37. package/lib/providers/ami-root-device.lambda.js +2 -2
  38. package/lib/providers/codebuild.d.ts +16 -0
  39. package/lib/providers/codebuild.js +16 -5
  40. package/lib/providers/common.js +3 -3
  41. package/lib/providers/composite.js +1 -1
  42. package/lib/providers/ec2.d.ts +5 -0
  43. package/lib/providers/ec2.js +42 -29
  44. package/lib/providers/ecs.d.ts +17 -0
  45. package/lib/providers/ecs.js +43 -38
  46. package/lib/providers/fargate.js +10 -32
  47. package/lib/providers/lambda.js +2 -2
  48. package/lib/runner.d.ts +25 -2
  49. package/lib/runner.js +119 -17
  50. package/lib/secrets.js +1 -1
  51. package/lib/setup.lambda.js +2 -2
  52. package/lib/utils.d.ts +10 -1
  53. package/lib/utils.js +15 -1
  54. package/lib/warm-runner-manager-function.d.ts +18 -0
  55. package/lib/warm-runner-manager-function.js +24 -0
  56. package/lib/warm-runner-manager.lambda.d.ts +41 -0
  57. package/lib/warm-runner-manager.lambda.js +487 -0
  58. package/lib/warm-runner.d.ts +147 -0
  59. package/lib/warm-runner.js +210 -0
  60. package/lib/webhook-handler.lambda.js +5 -3
  61. package/lib/webhook-redelivery.lambda.js +17 -16
  62. package/lib/webhook.d.ts +4 -0
  63. package/lib/webhook.js +2 -1
  64. package/node_modules/cron-parser/LICENSE +21 -0
  65. package/node_modules/cron-parser/README.md +408 -0
  66. package/node_modules/cron-parser/dist/CronDate.js +518 -0
  67. package/node_modules/cron-parser/dist/CronExpression.js +520 -0
  68. package/node_modules/cron-parser/dist/CronExpressionParser.js +382 -0
  69. package/node_modules/cron-parser/dist/CronFieldCollection.js +371 -0
  70. package/node_modules/cron-parser/dist/CronFileParser.js +109 -0
  71. package/node_modules/cron-parser/dist/fields/CronDayOfMonth.js +44 -0
  72. package/node_modules/cron-parser/dist/fields/CronDayOfWeek.js +51 -0
  73. package/node_modules/cron-parser/dist/fields/CronField.js +214 -0
  74. package/node_modules/cron-parser/dist/fields/CronHour.js +40 -0
  75. package/node_modules/cron-parser/dist/fields/CronMinute.js +40 -0
  76. package/node_modules/cron-parser/dist/fields/CronMonth.js +44 -0
  77. package/node_modules/cron-parser/dist/fields/CronSecond.js +40 -0
  78. package/node_modules/cron-parser/dist/fields/index.js +24 -0
  79. package/node_modules/cron-parser/dist/fields/types.js +2 -0
  80. package/node_modules/cron-parser/dist/index.js +31 -0
  81. package/node_modules/cron-parser/dist/types/CronDate.d.ts +288 -0
  82. package/node_modules/cron-parser/dist/types/CronExpression.d.ts +118 -0
  83. package/node_modules/cron-parser/dist/types/CronExpressionParser.d.ts +70 -0
  84. package/node_modules/cron-parser/dist/types/CronFieldCollection.d.ts +153 -0
  85. package/node_modules/cron-parser/dist/types/CronFileParser.d.ts +30 -0
  86. package/node_modules/cron-parser/dist/types/fields/CronDayOfMonth.d.ts +25 -0
  87. package/node_modules/cron-parser/dist/types/fields/CronDayOfWeek.d.ts +30 -0
  88. package/node_modules/cron-parser/dist/types/fields/CronField.d.ts +130 -0
  89. package/node_modules/cron-parser/dist/types/fields/CronHour.d.ts +23 -0
  90. package/node_modules/cron-parser/dist/types/fields/CronMinute.d.ts +23 -0
  91. package/node_modules/cron-parser/dist/types/fields/CronMonth.d.ts +24 -0
  92. package/node_modules/cron-parser/dist/types/fields/CronSecond.d.ts +23 -0
  93. package/node_modules/cron-parser/dist/types/fields/index.d.ts +8 -0
  94. package/node_modules/cron-parser/dist/types/fields/types.d.ts +18 -0
  95. package/node_modules/cron-parser/dist/types/index.d.ts +8 -0
  96. package/node_modules/cron-parser/dist/types/utils/random.d.ts +10 -0
  97. package/node_modules/cron-parser/dist/utils/random.js +38 -0
  98. package/node_modules/cron-parser/package.json +117 -0
  99. package/node_modules/luxon/LICENSE.md +7 -0
  100. package/node_modules/luxon/README.md +55 -0
  101. package/node_modules/luxon/build/amd/luxon.js +8741 -0
  102. package/node_modules/luxon/build/amd/luxon.js.map +1 -0
  103. package/node_modules/luxon/build/cjs-browser/luxon.js +8739 -0
  104. package/node_modules/luxon/build/cjs-browser/luxon.js.map +1 -0
  105. package/node_modules/luxon/build/es6/luxon.mjs +8133 -0
  106. package/node_modules/luxon/build/es6/luxon.mjs.map +1 -0
  107. package/node_modules/luxon/build/global/luxon.js +8744 -0
  108. package/node_modules/luxon/build/global/luxon.js.map +1 -0
  109. package/node_modules/luxon/build/global/luxon.min.js +1 -0
  110. package/node_modules/luxon/build/global/luxon.min.js.map +1 -0
  111. package/node_modules/luxon/build/node/luxon.js +7792 -0
  112. package/node_modules/luxon/build/node/luxon.js.map +1 -0
  113. package/node_modules/luxon/package.json +87 -0
  114. package/node_modules/luxon/src/datetime.js +2603 -0
  115. package/node_modules/luxon/src/duration.js +1009 -0
  116. package/node_modules/luxon/src/errors.js +61 -0
  117. package/node_modules/luxon/src/impl/conversions.js +206 -0
  118. package/node_modules/luxon/src/impl/diff.js +95 -0
  119. package/node_modules/luxon/src/impl/digits.js +94 -0
  120. package/node_modules/luxon/src/impl/english.js +233 -0
  121. package/node_modules/luxon/src/impl/formats.js +176 -0
  122. package/node_modules/luxon/src/impl/formatter.js +434 -0
  123. package/node_modules/luxon/src/impl/invalid.js +14 -0
  124. package/node_modules/luxon/src/impl/locale.js +569 -0
  125. package/node_modules/luxon/src/impl/regexParser.js +335 -0
  126. package/node_modules/luxon/src/impl/tokenParser.js +505 -0
  127. package/node_modules/luxon/src/impl/util.js +330 -0
  128. package/node_modules/luxon/src/impl/zoneUtil.js +34 -0
  129. package/node_modules/luxon/src/info.js +205 -0
  130. package/node_modules/luxon/src/interval.js +669 -0
  131. package/node_modules/luxon/src/luxon.js +26 -0
  132. package/node_modules/luxon/src/package.json +4 -0
  133. package/node_modules/luxon/src/settings.js +180 -0
  134. package/node_modules/luxon/src/zone.js +97 -0
  135. package/node_modules/luxon/src/zones/IANAZone.js +235 -0
  136. package/node_modules/luxon/src/zones/fixedOffsetZone.js +150 -0
  137. package/node_modules/luxon/src/zones/invalidZone.js +53 -0
  138. package/node_modules/luxon/src/zones/systemZone.js +61 -0
  139. package/package.json +33 -24
@@ -0,0 +1,520 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CronExpression = exports.LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE = exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE = void 0;
4
+ const CronDate_1 = require("./CronDate");
5
+ /**
6
+ * Error message for when the current date is outside the specified time span.
7
+ */
8
+ exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE = 'Out of the time span range';
9
+ /**
10
+ * Error message for when the loop limit is exceeded during iteration.
11
+ */
12
+ exports.LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE = 'Invalid expression, loop limit exceeded';
13
+ /**
14
+ * Cron iteration loop safety limit
15
+ */
16
+ const LOOP_LIMIT = 10000;
17
+ /**
18
+ * Class representing a Cron expression.
19
+ */
20
+ class CronExpression {
21
+ #options;
22
+ #tz;
23
+ #currentDate;
24
+ #startDate;
25
+ #endDate;
26
+ #fields;
27
+ #dstTransitionDayKey = null;
28
+ #isDstTransitionDay = false;
29
+ /**
30
+ * Creates a new CronExpression instance.
31
+ *
32
+ * @param {CronFieldCollection} fields - Cron fields.
33
+ * @param {CronExpressionOptions} options - Parser options.
34
+ */
35
+ constructor(fields, options) {
36
+ this.#options = options;
37
+ this.#tz = options.tz;
38
+ this.#startDate = options.startDate ? new CronDate_1.CronDate(options.startDate, this.#tz) : null;
39
+ this.#endDate = options.endDate ? new CronDate_1.CronDate(options.endDate, this.#tz) : null;
40
+ let currentDateValue = options.currentDate ?? options.startDate;
41
+ if (currentDateValue) {
42
+ const tempCurrentDate = new CronDate_1.CronDate(currentDateValue, this.#tz);
43
+ if (this.#startDate && tempCurrentDate.getTime() < this.#startDate.getTime()) {
44
+ currentDateValue = this.#startDate;
45
+ }
46
+ else if (this.#endDate && tempCurrentDate.getTime() > this.#endDate.getTime()) {
47
+ currentDateValue = this.#endDate;
48
+ }
49
+ }
50
+ this.#currentDate = new CronDate_1.CronDate(currentDateValue, this.#tz);
51
+ this.#fields = fields;
52
+ }
53
+ /**
54
+ * Getter for the cron fields.
55
+ *
56
+ * @returns {CronFieldCollection} Cron fields.
57
+ */
58
+ get fields() {
59
+ return this.#fields;
60
+ }
61
+ /**
62
+ * Converts cron fields back to a CronExpression instance.
63
+ *
64
+ * @public
65
+ * @param {Record<string, number[]>} fields - The input cron fields object.
66
+ * @param {CronExpressionOptions} [options] - Optional parsing options.
67
+ * @returns {CronExpression} - A new CronExpression instance.
68
+ */
69
+ static fieldsToExpression(fields, options) {
70
+ return new CronExpression(fields, options || {});
71
+ }
72
+ /**
73
+ * Checks if the given value matches any element in the sequence.
74
+ *
75
+ * @param {number} value - The value to be matched.
76
+ * @param {number[]} sequence - The sequence to be checked against.
77
+ * @returns {boolean} - True if the value matches an element in the sequence; otherwise, false.
78
+ * @memberof CronExpression
79
+ * @private
80
+ */
81
+ static #matchSchedule(value, sequence) {
82
+ return sequence.some((element) => element === value);
83
+ }
84
+ /**
85
+ * Returns the minimum or maximum value from the given array of numbers.
86
+ *
87
+ * @param {number[]} values - An array of numbers.
88
+ * @param {boolean} reverse - If true, returns the maximum value; otherwise, returns the minimum value.
89
+ * @returns {number} - The minimum or maximum value.
90
+ */
91
+ #getMinOrMax(values, reverse) {
92
+ return values[reverse ? values.length - 1 : 0];
93
+ }
94
+ /**
95
+ * Checks whether the given date falls on a DST transition day in its timezone.
96
+ *
97
+ * This is used to disable certain “direct set” fast paths on DST days, because setting the hour
98
+ * directly may land on a non-existent or repeated local time. We cache the result per calendar day
99
+ * to keep iteration overhead low.
100
+ *
101
+ * @param {CronDate} currentDate - Date to check (in the cron timezone)
102
+ * @returns {boolean} True when the day has a DST transition
103
+ * @private
104
+ */
105
+ #checkDstTransition(currentDate) {
106
+ // Cache per calendar day (in the cron date's timezone) to avoid repeated work inside the iteration loop.
107
+ const key = `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`;
108
+ if (this.#dstTransitionDayKey === key) {
109
+ return this.#isDstTransitionDay;
110
+ }
111
+ const startOfDay = new CronDate_1.CronDate(currentDate);
112
+ startOfDay.setStartOfDay();
113
+ const endOfDay = new CronDate_1.CronDate(currentDate);
114
+ endOfDay.setEndOfDay();
115
+ this.#dstTransitionDayKey = key;
116
+ this.#isDstTransitionDay = startOfDay.getUTCOffset() !== endOfDay.getUTCOffset();
117
+ return this.#isDstTransitionDay;
118
+ }
119
+ /**
120
+ * Moves the date to the next/previous allowed second value. If there is no remaining allowed second
121
+ * within the current minute, rolls to the next/previous minute and resets seconds to the min/max allowed.
122
+ *
123
+ * @param {CronDate} currentDate - Mutable date being iterated
124
+ * @param {DateMathOp} dateMathVerb - Add/Subtract depending on direction
125
+ * @param {boolean} reverse - When true, iterating backwards
126
+ * @private
127
+ */
128
+ #moveToNextSecond(currentDate, dateMathVerb, reverse) {
129
+ const seconds = this.#fields.second.values;
130
+ const currentSecond = currentDate.getSeconds();
131
+ const nextSecond = this.#fields.second.findNearestValue(currentSecond, reverse);
132
+ if (nextSecond !== null) {
133
+ currentDate.setSeconds(nextSecond);
134
+ return;
135
+ }
136
+ // Roll over to the next/previous minute and start from the min/max allowed second.
137
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Minute, this.#fields.hour.values.length);
138
+ currentDate.setSeconds(this.#getMinOrMax(seconds, reverse));
139
+ }
140
+ /**
141
+ * Moves the date to the next/previous allowed minute value and resets seconds to the min/max allowed.
142
+ * If there is no remaining allowed minute within the current hour, rolls to the next/previous hour and
143
+ * resets minutes/seconds to their extrema.
144
+ *
145
+ * @param {CronDate} currentDate - Mutable date being iterated
146
+ * @param {DateMathOp} dateMathVerb - Add/Subtract depending on direction
147
+ * @param {boolean} reverse - When true, iterating backwards
148
+ * @private
149
+ */
150
+ #moveToNextMinute(currentDate, dateMathVerb, reverse) {
151
+ const minutes = this.#fields.minute.values;
152
+ const seconds = this.#fields.second.values;
153
+ const currentMinute = currentDate.getMinutes();
154
+ const nextMinute = this.#fields.minute.findNearestValue(currentMinute, reverse);
155
+ if (nextMinute !== null) {
156
+ currentDate.setMinutes(nextMinute);
157
+ currentDate.setSeconds(this.#getMinOrMax(seconds, reverse));
158
+ return;
159
+ }
160
+ // Roll over to the next/previous hour and start from the min/max allowed minute/second.
161
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Hour, this.#fields.hour.values.length);
162
+ currentDate.setMinutes(this.#getMinOrMax(minutes, reverse));
163
+ currentDate.setSeconds(this.#getMinOrMax(seconds, reverse));
164
+ }
165
+ /**
166
+ * Determines if the current date matches the last specified weekday of the month.
167
+ *
168
+ * @param {Array<(number|string)>} expressions - An array of expressions containing weekdays and "L" for the last weekday.
169
+ * @param {CronDate} currentDate - The current date object.
170
+ * @returns {boolean} - True if the current date matches the last specified weekday of the month; otherwise, false.
171
+ * @memberof CronExpression
172
+ * @private
173
+ */
174
+ static #isLastWeekdayOfMonthMatch(expressions, currentDate) {
175
+ const isLastWeekdayOfMonth = currentDate.isLastWeekdayOfMonth();
176
+ return expressions.some((expression) => {
177
+ // The first character represents the weekday
178
+ const weekday = parseInt(expression.toString().charAt(0), 10) % 7;
179
+ if (Number.isNaN(weekday)) {
180
+ throw new Error(`Invalid last weekday of the month expression: ${expression}`);
181
+ }
182
+ // Check if the current date matches the last specified weekday of the month
183
+ return currentDate.getDay() === weekday && isLastWeekdayOfMonth;
184
+ });
185
+ }
186
+ /**
187
+ * Find the next scheduled date based on the cron expression.
188
+ * @returns {CronDate} - The next scheduled date or an ES6 compatible iterator object.
189
+ * @memberof CronExpression
190
+ * @public
191
+ */
192
+ next() {
193
+ return this.#findSchedule();
194
+ }
195
+ /**
196
+ * Find the previous scheduled date based on the cron expression.
197
+ * @returns {CronDate} - The previous scheduled date or an ES6 compatible iterator object.
198
+ * @memberof CronExpression
199
+ * @public
200
+ */
201
+ prev() {
202
+ return this.#findSchedule(true);
203
+ }
204
+ /**
205
+ * Check if there is a next scheduled date based on the current date and cron expression.
206
+ * @returns {boolean} - Returns true if there is a next scheduled date, false otherwise.
207
+ * @memberof CronExpression
208
+ * @public
209
+ */
210
+ hasNext() {
211
+ const current = this.#currentDate;
212
+ try {
213
+ this.#findSchedule();
214
+ return true;
215
+ }
216
+ catch {
217
+ return false;
218
+ }
219
+ finally {
220
+ this.#currentDate = current;
221
+ }
222
+ }
223
+ /**
224
+ * Check if there is a previous scheduled date based on the current date and cron expression.
225
+ * @returns {boolean} - Returns true if there is a previous scheduled date, false otherwise.
226
+ * @memberof CronExpression
227
+ * @public
228
+ */
229
+ hasPrev() {
230
+ const current = this.#currentDate;
231
+ try {
232
+ this.#findSchedule(true);
233
+ return true;
234
+ }
235
+ catch {
236
+ return false;
237
+ }
238
+ finally {
239
+ this.#currentDate = current;
240
+ }
241
+ }
242
+ /**
243
+ * Iterate over a specified number of steps and optionally execute a callback function for each step.
244
+ * @param {number} steps - The number of steps to iterate. Positive value iterates forward, negative value iterates backward.
245
+ * @returns {CronDate[]} - An array of iterator fields or CronDate objects.
246
+ * @memberof CronExpression
247
+ * @public
248
+ */
249
+ take(limit) {
250
+ const items = [];
251
+ if (limit >= 0) {
252
+ for (let i = 0; i < limit; i++) {
253
+ try {
254
+ items.push(this.next());
255
+ }
256
+ catch {
257
+ return items;
258
+ }
259
+ }
260
+ }
261
+ else {
262
+ for (let i = 0; i > limit; i--) {
263
+ try {
264
+ items.push(this.prev());
265
+ }
266
+ catch {
267
+ return items;
268
+ }
269
+ }
270
+ }
271
+ return items;
272
+ }
273
+ /**
274
+ * Reset the iterators current date to a new date or the initial date.
275
+ * @param {Date | CronDate} [newDate] - Optional new date to reset to. If not provided, it will reset to the initial date.
276
+ * @memberof CronExpression
277
+ * @public
278
+ */
279
+ reset(newDate) {
280
+ this.#currentDate = new CronDate_1.CronDate(newDate || this.#options.currentDate);
281
+ }
282
+ /**
283
+ * Generate a string representation of the cron expression.
284
+ * @param {boolean} [includeSeconds=false] - Whether to include the seconds field in the string representation.
285
+ * @returns {string} - The string representation of the cron expression.
286
+ * @memberof CronExpression
287
+ * @public
288
+ */
289
+ stringify(includeSeconds = false) {
290
+ return this.#fields.stringify(includeSeconds);
291
+ }
292
+ /**
293
+ * Check if the cron expression includes the given date
294
+ * @param {Date|CronDate} date
295
+ * @returns {boolean}
296
+ */
297
+ includesDate(date) {
298
+ const { second, minute, hour, month } = this.#fields;
299
+ const dt = new CronDate_1.CronDate(date, this.#tz);
300
+ // Check basic time fields first
301
+ if (!second.values.includes(dt.getSeconds()) ||
302
+ !minute.values.includes(dt.getMinutes()) ||
303
+ !hour.values.includes(dt.getHours()) ||
304
+ !month.values.includes((dt.getMonth() + 1))) {
305
+ return false;
306
+ }
307
+ // Check day of month and day of week using the same logic as #findSchedule
308
+ if (!this.#matchDayOfMonth(dt)) {
309
+ return false;
310
+ }
311
+ // Check nth day of week if specified
312
+ if (this.#fields.dayOfWeek.nthDay > 0) {
313
+ const weekInMonth = Math.ceil(dt.getDate() / 7);
314
+ if (weekInMonth !== this.#fields.dayOfWeek.nthDay) {
315
+ return false;
316
+ }
317
+ }
318
+ return true;
319
+ }
320
+ /**
321
+ * Returns the string representation of the cron expression.
322
+ * @returns {CronDate} - The next schedule date.
323
+ */
324
+ toString() {
325
+ /* istanbul ignore next - should be impossible under normal use to trigger the or branch */
326
+ return this.#options.expression || this.stringify(true);
327
+ }
328
+ /**
329
+ * Determines if the given date matches the cron expression's day of month and day of week fields.
330
+ *
331
+ * The function checks the following rules:
332
+ * Rule 1: If both "day of month" and "day of week" are restricted (not wildcard), then one or both must match the current day.
333
+ * Rule 2: If "day of month" is restricted and "day of week" is not restricted, then "day of month" must match the current day.
334
+ * Rule 3: If "day of month" is a wildcard, "day of week" is not a wildcard, and "day of week" matches the current day, then the match is accepted.
335
+ * If none of the rules match, the match is rejected.
336
+ *
337
+ * @param {CronDate} currentDate - The current date to be evaluated against the cron expression.
338
+ * @returns {boolean} Returns true if the current date matches the cron expression's day of month and day of week fields, otherwise false.
339
+ * @memberof CronExpression
340
+ * @private
341
+ */
342
+ #matchDayOfMonth(currentDate) {
343
+ // Check if day of month and day of week fields are wildcards or restricted (not wildcard).
344
+ const isDayOfMonthWildcardMatch = this.#fields.dayOfMonth.isWildcard;
345
+ const isRestrictedDayOfMonth = !isDayOfMonthWildcardMatch;
346
+ const isDayOfWeekWildcardMatch = this.#fields.dayOfWeek.isWildcard;
347
+ const isRestrictedDayOfWeek = !isDayOfWeekWildcardMatch;
348
+ // Calculate if the current date matches the day of month and day of week fields.
349
+ const matchedDOM = CronExpression.#matchSchedule(currentDate.getDate(), this.#fields.dayOfMonth.values) ||
350
+ (this.#fields.dayOfMonth.hasLastChar && currentDate.isLastDayOfMonth());
351
+ const matchedDOW = CronExpression.#matchSchedule(currentDate.getDay(), this.#fields.dayOfWeek.values) ||
352
+ (this.#fields.dayOfWeek.hasLastChar &&
353
+ CronExpression.#isLastWeekdayOfMonthMatch(this.#fields.dayOfWeek.values, currentDate));
354
+ // Rule 1: Both "day of month" and "day of week" are restricted; one or both must match the current day.
355
+ if (isRestrictedDayOfMonth && isRestrictedDayOfWeek && (matchedDOM || matchedDOW)) {
356
+ return true;
357
+ }
358
+ // Rule 2: "day of month" restricted and "day of week" not restricted; "day of month" must match the current day.
359
+ if (matchedDOM && !isRestrictedDayOfWeek) {
360
+ return true;
361
+ }
362
+ // Rule 3: "day of month" is a wildcard, "day of week" is not a wildcard, and "day of week" matches the current day.
363
+ if (isDayOfMonthWildcardMatch && !isDayOfWeekWildcardMatch && matchedDOW) {
364
+ return true;
365
+ }
366
+ // If none of the rules match, the match is rejected.
367
+ return false;
368
+ }
369
+ /**
370
+ * Determines if the current hour matches the cron expression.
371
+ *
372
+ * @param {CronDate} currentDate - The current date object.
373
+ * @param {DateMathOp} dateMathVerb - The date math operation enumeration value.
374
+ * @param {boolean} reverse - A flag indicating whether the matching should be done in reverse order.
375
+ * @returns {boolean} - True if the current hour matches the cron expression; otherwise, false.
376
+ */
377
+ #matchHour(currentDate, dateMathVerb, reverse) {
378
+ const hourValues = this.#fields.hour.values;
379
+ const hours = hourValues;
380
+ const currentHour = currentDate.getHours();
381
+ const isMatch = CronExpression.#matchSchedule(currentHour, hourValues);
382
+ const isDstStart = currentDate.dstStart === currentHour;
383
+ const isDstEnd = currentDate.dstEnd === currentHour;
384
+ // DST start: if the scheduled hour is skipped (e.g. 03:00 doesn't exist),
385
+ // accept the next existing hour when it corresponds to the skipped one.
386
+ if (isDstStart) {
387
+ if (CronExpression.#matchSchedule(currentHour - 1, hourValues)) {
388
+ return true;
389
+ }
390
+ currentDate.invokeDateOperation(dateMathVerb, CronDate_1.TimeUnit.Hour);
391
+ return false;
392
+ }
393
+ // DST end: avoid returning the repeated hour twice when searching forward.
394
+ if (isDstEnd && !reverse) {
395
+ currentDate.dstEnd = null;
396
+ currentDate.applyDateOperation(CronDate_1.DateMathOp.Add, CronDate_1.TimeUnit.Hour, hours.length);
397
+ return false;
398
+ }
399
+ if (isMatch) {
400
+ return true;
401
+ }
402
+ // Normal mismatch: if there's no remaining matching hour in this day, jump a whole day first
403
+ // to avoid scanning hour-by-hour across the day boundary.
404
+ currentDate.dstStart = null;
405
+ const nextHour = this.#fields.hour.findNearestValue(currentHour, reverse);
406
+ if (nextHour === null) {
407
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Day, hours.length);
408
+ return false;
409
+ }
410
+ // Fast path: jump directly to the next/previous matching hour and reset lower units.
411
+ //
412
+ // On DST transition days, setting the hour directly can land on a non-existent/repeated local time,
413
+ // which breaks the existing DST handling that relies on `applyDateOperation()` to set `dstStart/dstEnd`.
414
+ // For those days, fall back to stepping hour-by-hour to preserve correctness.
415
+ if (this.#checkDstTransition(currentDate)) {
416
+ const steps = reverse ? currentHour - nextHour : nextHour - currentHour;
417
+ for (let i = 0; i < steps; i++) {
418
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Hour, hours.length);
419
+ }
420
+ }
421
+ else {
422
+ currentDate.setHours(nextHour);
423
+ }
424
+ currentDate.setMinutes(this.#getMinOrMax(this.#fields.minute.values, reverse));
425
+ currentDate.setSeconds(this.#getMinOrMax(this.#fields.second.values, reverse));
426
+ return false;
427
+ }
428
+ /**
429
+ * Validates the current date against the start and end dates of the cron expression.
430
+ * If the current date is outside the specified time span, an error is thrown.
431
+ *
432
+ * @param currentDate {CronDate} - The current date to validate.
433
+ * @throws {Error} If the current date is outside the specified time span.
434
+ * @private
435
+ */
436
+ #validateTimeSpan(currentDate) {
437
+ if (!this.#startDate && !this.#endDate) {
438
+ return;
439
+ }
440
+ const currentTime = currentDate.getTime();
441
+ if (this.#startDate && currentTime < this.#startDate.getTime()) {
442
+ throw new Error(exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE);
443
+ }
444
+ if (this.#endDate && currentTime > this.#endDate.getTime()) {
445
+ throw new Error(exports.TIME_SPAN_OUT_OF_BOUNDS_ERROR_MESSAGE);
446
+ }
447
+ }
448
+ /**
449
+ * Finds the next or previous schedule based on the cron expression.
450
+ *
451
+ * @param {boolean} [reverse=false] - If true, finds the previous schedule; otherwise, finds the next schedule.
452
+ * @returns {CronDate} - The next or previous schedule date.
453
+ * @private
454
+ */
455
+ #findSchedule(reverse = false) {
456
+ const dateMathVerb = reverse ? CronDate_1.DateMathOp.Subtract : CronDate_1.DateMathOp.Add;
457
+ const currentDate = new CronDate_1.CronDate(this.#currentDate);
458
+ const startTimestamp = currentDate.getTime();
459
+ let stepCount = 0;
460
+ while (++stepCount < LOOP_LIMIT) {
461
+ this.#validateTimeSpan(currentDate);
462
+ if (!this.#matchDayOfMonth(currentDate)) {
463
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Day, this.#fields.hour.values.length);
464
+ continue;
465
+ }
466
+ if (!(this.#fields.dayOfWeek.nthDay <= 0 || Math.ceil(currentDate.getDate() / 7) === this.#fields.dayOfWeek.nthDay)) {
467
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Day, this.#fields.hour.values.length);
468
+ continue;
469
+ }
470
+ if (!CronExpression.#matchSchedule(currentDate.getMonth() + 1, this.#fields.month.values)) {
471
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Month, this.#fields.hour.values.length);
472
+ continue;
473
+ }
474
+ if (!this.#matchHour(currentDate, dateMathVerb, reverse)) {
475
+ continue;
476
+ }
477
+ if (!CronExpression.#matchSchedule(currentDate.getMinutes(), this.#fields.minute.values)) {
478
+ this.#moveToNextMinute(currentDate, dateMathVerb, reverse);
479
+ continue;
480
+ }
481
+ if (!CronExpression.#matchSchedule(currentDate.getSeconds(), this.#fields.second.values)) {
482
+ this.#moveToNextSecond(currentDate, dateMathVerb, reverse);
483
+ continue;
484
+ }
485
+ if (startTimestamp === currentDate.getTime()) {
486
+ if (dateMathVerb === 'Add' || currentDate.getMilliseconds() === 0) {
487
+ currentDate.applyDateOperation(dateMathVerb, CronDate_1.TimeUnit.Second, this.#fields.hour.values.length);
488
+ }
489
+ continue;
490
+ }
491
+ break;
492
+ }
493
+ /* istanbul ignore next - should be impossible under normal use to trigger the branch */
494
+ if (stepCount > LOOP_LIMIT) {
495
+ throw new Error(exports.LOOPS_LIMIT_EXCEEDED_ERROR_MESSAGE);
496
+ }
497
+ if (currentDate.getMilliseconds() !== 0) {
498
+ currentDate.setMilliseconds(0);
499
+ }
500
+ this.#currentDate = currentDate;
501
+ return currentDate;
502
+ }
503
+ /**
504
+ * Returns an iterator for iterating through future CronDate instances
505
+ *
506
+ * @name Symbol.iterator
507
+ * @memberof CronExpression
508
+ * @returns {Iterator<CronDate>} An iterator object for CronExpression that returns CronDate values.
509
+ */
510
+ [Symbol.iterator]() {
511
+ return {
512
+ next: () => {
513
+ const schedule = this.#findSchedule();
514
+ return { value: schedule, done: !this.hasNext() };
515
+ },
516
+ };
517
+ }
518
+ }
519
+ exports.CronExpression = CronExpression;
520
+ exports.default = CronExpression;