@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.
- package/.jsii +5397 -252
- package/API.md +1048 -24
- package/README.md +52 -0
- package/assets/delete-failed-runner.lambda/index.js +105 -9
- package/assets/idle-runner-repear.lambda/index.js +136 -14
- package/assets/image-builders/aws-image-builder/delete-resources.lambda/index.js +1 -1
- package/assets/image-builders/build-image.lambda/index.js +1 -1
- package/assets/providers/ami-root-device.lambda/index.js +1 -1
- package/assets/setup.lambda/index.html +7 -7
- package/assets/setup.lambda/index.js +101 -8
- package/assets/status.lambda/index.js +104 -8
- package/assets/token-retriever.lambda/index.js +104 -8
- package/assets/warm-runner-manager.lambda/index.js +5892 -0
- package/assets/webhook-handler.lambda/index.js +109 -11
- package/assets/webhook-redelivery.lambda/index.js +122 -24
- package/lib/access.js +1 -1
- package/lib/delete-failed-runner.lambda.js +2 -2
- package/lib/idle-runner-repear.lambda.js +33 -7
- package/lib/image-builders/api.js +1 -1
- package/lib/image-builders/aws-image-builder/base-image.d.ts +13 -0
- package/lib/image-builders/aws-image-builder/base-image.js +36 -3
- package/lib/image-builders/aws-image-builder/builder.js +4 -4
- package/lib/image-builders/aws-image-builder/delete-resources.lambda.js +2 -2
- package/lib/image-builders/aws-image-builder/deprecated/ami.js +1 -1
- package/lib/image-builders/aws-image-builder/deprecated/container.js +1 -1
- package/lib/image-builders/aws-image-builder/deprecated/linux-components.js +1 -1
- package/lib/image-builders/aws-image-builder/deprecated/windows-components.js +1 -1
- package/lib/image-builders/build-image.lambda.js +2 -2
- package/lib/image-builders/codebuild-deprecated.js +1 -1
- package/lib/image-builders/components.js +3 -3
- package/lib/image-builders/static.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -1
- package/lib/lambda-github.d.ts +1 -1
- package/lib/lambda-github.js +3 -2
- package/lib/lambda-helpers.js +4 -4
- package/lib/providers/ami-root-device.lambda.js +2 -2
- package/lib/providers/codebuild.d.ts +16 -0
- package/lib/providers/codebuild.js +16 -5
- package/lib/providers/common.js +3 -3
- package/lib/providers/composite.js +1 -1
- package/lib/providers/ec2.d.ts +5 -0
- package/lib/providers/ec2.js +42 -29
- package/lib/providers/ecs.d.ts +17 -0
- package/lib/providers/ecs.js +43 -38
- package/lib/providers/fargate.js +10 -32
- package/lib/providers/lambda.js +2 -2
- package/lib/runner.d.ts +25 -2
- package/lib/runner.js +119 -17
- package/lib/secrets.js +1 -1
- package/lib/setup.lambda.js +2 -2
- package/lib/utils.d.ts +10 -1
- package/lib/utils.js +15 -1
- package/lib/warm-runner-manager-function.d.ts +18 -0
- package/lib/warm-runner-manager-function.js +24 -0
- package/lib/warm-runner-manager.lambda.d.ts +41 -0
- package/lib/warm-runner-manager.lambda.js +487 -0
- package/lib/warm-runner.d.ts +147 -0
- package/lib/warm-runner.js +210 -0
- package/lib/webhook-handler.lambda.js +5 -3
- package/lib/webhook-redelivery.lambda.js +17 -16
- package/lib/webhook.d.ts +4 -0
- package/lib/webhook.js +2 -1
- package/node_modules/cron-parser/LICENSE +21 -0
- package/node_modules/cron-parser/README.md +408 -0
- package/node_modules/cron-parser/dist/CronDate.js +518 -0
- package/node_modules/cron-parser/dist/CronExpression.js +520 -0
- package/node_modules/cron-parser/dist/CronExpressionParser.js +382 -0
- package/node_modules/cron-parser/dist/CronFieldCollection.js +371 -0
- package/node_modules/cron-parser/dist/CronFileParser.js +109 -0
- package/node_modules/cron-parser/dist/fields/CronDayOfMonth.js +44 -0
- package/node_modules/cron-parser/dist/fields/CronDayOfWeek.js +51 -0
- package/node_modules/cron-parser/dist/fields/CronField.js +214 -0
- package/node_modules/cron-parser/dist/fields/CronHour.js +40 -0
- package/node_modules/cron-parser/dist/fields/CronMinute.js +40 -0
- package/node_modules/cron-parser/dist/fields/CronMonth.js +44 -0
- package/node_modules/cron-parser/dist/fields/CronSecond.js +40 -0
- package/node_modules/cron-parser/dist/fields/index.js +24 -0
- package/node_modules/cron-parser/dist/fields/types.js +2 -0
- package/node_modules/cron-parser/dist/index.js +31 -0
- package/node_modules/cron-parser/dist/types/CronDate.d.ts +288 -0
- package/node_modules/cron-parser/dist/types/CronExpression.d.ts +118 -0
- package/node_modules/cron-parser/dist/types/CronExpressionParser.d.ts +70 -0
- package/node_modules/cron-parser/dist/types/CronFieldCollection.d.ts +153 -0
- package/node_modules/cron-parser/dist/types/CronFileParser.d.ts +30 -0
- package/node_modules/cron-parser/dist/types/fields/CronDayOfMonth.d.ts +25 -0
- package/node_modules/cron-parser/dist/types/fields/CronDayOfWeek.d.ts +30 -0
- package/node_modules/cron-parser/dist/types/fields/CronField.d.ts +130 -0
- package/node_modules/cron-parser/dist/types/fields/CronHour.d.ts +23 -0
- package/node_modules/cron-parser/dist/types/fields/CronMinute.d.ts +23 -0
- package/node_modules/cron-parser/dist/types/fields/CronMonth.d.ts +24 -0
- package/node_modules/cron-parser/dist/types/fields/CronSecond.d.ts +23 -0
- package/node_modules/cron-parser/dist/types/fields/index.d.ts +8 -0
- package/node_modules/cron-parser/dist/types/fields/types.d.ts +18 -0
- package/node_modules/cron-parser/dist/types/index.d.ts +8 -0
- package/node_modules/cron-parser/dist/types/utils/random.d.ts +10 -0
- package/node_modules/cron-parser/dist/utils/random.js +38 -0
- package/node_modules/cron-parser/package.json +117 -0
- package/node_modules/luxon/LICENSE.md +7 -0
- package/node_modules/luxon/README.md +55 -0
- package/node_modules/luxon/build/amd/luxon.js +8741 -0
- package/node_modules/luxon/build/amd/luxon.js.map +1 -0
- package/node_modules/luxon/build/cjs-browser/luxon.js +8739 -0
- package/node_modules/luxon/build/cjs-browser/luxon.js.map +1 -0
- package/node_modules/luxon/build/es6/luxon.mjs +8133 -0
- package/node_modules/luxon/build/es6/luxon.mjs.map +1 -0
- package/node_modules/luxon/build/global/luxon.js +8744 -0
- package/node_modules/luxon/build/global/luxon.js.map +1 -0
- package/node_modules/luxon/build/global/luxon.min.js +1 -0
- package/node_modules/luxon/build/global/luxon.min.js.map +1 -0
- package/node_modules/luxon/build/node/luxon.js +7792 -0
- package/node_modules/luxon/build/node/luxon.js.map +1 -0
- package/node_modules/luxon/package.json +87 -0
- package/node_modules/luxon/src/datetime.js +2603 -0
- package/node_modules/luxon/src/duration.js +1009 -0
- package/node_modules/luxon/src/errors.js +61 -0
- package/node_modules/luxon/src/impl/conversions.js +206 -0
- package/node_modules/luxon/src/impl/diff.js +95 -0
- package/node_modules/luxon/src/impl/digits.js +94 -0
- package/node_modules/luxon/src/impl/english.js +233 -0
- package/node_modules/luxon/src/impl/formats.js +176 -0
- package/node_modules/luxon/src/impl/formatter.js +434 -0
- package/node_modules/luxon/src/impl/invalid.js +14 -0
- package/node_modules/luxon/src/impl/locale.js +569 -0
- package/node_modules/luxon/src/impl/regexParser.js +335 -0
- package/node_modules/luxon/src/impl/tokenParser.js +505 -0
- package/node_modules/luxon/src/impl/util.js +330 -0
- package/node_modules/luxon/src/impl/zoneUtil.js +34 -0
- package/node_modules/luxon/src/info.js +205 -0
- package/node_modules/luxon/src/interval.js +669 -0
- package/node_modules/luxon/src/luxon.js +26 -0
- package/node_modules/luxon/src/package.json +4 -0
- package/node_modules/luxon/src/settings.js +180 -0
- package/node_modules/luxon/src/zone.js +97 -0
- package/node_modules/luxon/src/zones/IANAZone.js +235 -0
- package/node_modules/luxon/src/zones/fixedOffsetZone.js +150 -0
- package/node_modules/luxon/src/zones/invalidZone.js +53 -0
- package/node_modules/luxon/src/zones/systemZone.js +61 -0
- 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;
|