semola 0.5.4 → 0.6.1
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/README.md +35 -50
- package/dist/lib/api/index.cjs +1 -523
- package/dist/lib/api/index.d.cts +1 -271
- package/dist/lib/api/index.d.mts +80 -84
- package/dist/lib/api/index.mjs +1 -521
- package/dist/lib/cache/index.cjs +1 -74
- package/dist/lib/cache/index.d.cts +1 -48
- package/dist/lib/cache/index.d.mts +3 -24
- package/dist/lib/cache/index.mjs +1 -73
- package/dist/lib/cron/index.cjs +1 -735
- package/dist/lib/cron/index.d.cts +1 -146
- package/dist/lib/cron/index.d.mts +99 -36
- package/dist/lib/cron/index.mjs +1 -726
- package/dist/lib/errors/index.cjs +1 -30
- package/dist/lib/errors/index.d.cts +1 -2
- package/dist/lib/errors/index.d.mts +12 -1
- package/dist/lib/errors/index.mjs +1 -26
- package/dist/lib/i18n/index.cjs +1 -42
- package/dist/lib/i18n/index.d.cts +1 -28
- package/dist/lib/i18n/index.mjs +1 -41
- package/dist/lib/logging/index.cjs +1 -387
- package/dist/lib/logging/index.d.cts +1 -108
- package/dist/lib/logging/index.mjs +1 -374
- package/dist/lib/orm/index.cjs +1 -0
- package/dist/lib/orm/index.d.cts +1 -0
- package/dist/lib/orm/index.d.mts +404 -0
- package/dist/lib/orm/index.mjs +1 -0
- package/dist/lib/policy/index.cjs +1 -285
- package/dist/lib/policy/index.d.cts +1 -77
- package/dist/lib/policy/index.mjs +1 -265
- package/dist/lib/prompts/index.cjs +6 -769
- package/dist/lib/prompts/index.d.cts +1 -96
- package/dist/lib/prompts/index.d.mts +12 -33
- package/dist/lib/prompts/index.mjs +6 -763
- package/dist/lib/pubsub/index.cjs +1 -117
- package/dist/lib/pubsub/index.d.cts +1 -41
- package/dist/lib/pubsub/index.d.mts +3 -18
- package/dist/lib/pubsub/index.mjs +1 -116
- package/dist/lib/queue/index.cjs +1 -182
- package/dist/lib/queue/index.d.cts +1 -74
- package/dist/lib/queue/index.d.mts +11 -4
- package/dist/lib/queue/index.mjs +1 -181
- package/dist/lib/workflow/index.cjs +1 -534
- package/dist/lib/workflow/index.d.cts +1 -85
- package/dist/lib/workflow/index.d.mts +76 -11
- package/dist/lib/workflow/index.mjs +1 -533
- package/dist/rolldown-runtime-CMqjfN_6.cjs +1 -0
- package/package.json +17 -5
- package/dist/index-BhGNDjPq.d.mts +0 -13
- package/dist/index-DxSbeGP-.d.cts +0 -13
package/dist/lib/cron/index.mjs
CHANGED
|
@@ -1,726 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
//#region src/lib/cron/builder/index.ts
|
|
3
|
-
const CRON_FIELD_ORDER = [
|
|
4
|
-
"second",
|
|
5
|
-
"minute",
|
|
6
|
-
"hour",
|
|
7
|
-
"day",
|
|
8
|
-
"month",
|
|
9
|
-
"weekday"
|
|
10
|
-
];
|
|
11
|
-
var FieldWrapper = class {
|
|
12
|
-
fields = [];
|
|
13
|
-
add(expr) {
|
|
14
|
-
this.fields.push(expr);
|
|
15
|
-
}
|
|
16
|
-
read() {
|
|
17
|
-
return this.fields;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
var CronListBuilder = class {
|
|
21
|
-
wrapper;
|
|
22
|
-
constructor(wrapper) {
|
|
23
|
-
this.wrapper = wrapper;
|
|
24
|
-
}
|
|
25
|
-
any() {
|
|
26
|
-
this.wrapper.add({ type: "any" });
|
|
27
|
-
return this;
|
|
28
|
-
}
|
|
29
|
-
range(options) {
|
|
30
|
-
this.wrapper.add({
|
|
31
|
-
type: "range",
|
|
32
|
-
...options
|
|
33
|
-
});
|
|
34
|
-
return this;
|
|
35
|
-
}
|
|
36
|
-
step(options) {
|
|
37
|
-
this.wrapper.add({
|
|
38
|
-
type: "step",
|
|
39
|
-
...options
|
|
40
|
-
});
|
|
41
|
-
return this;
|
|
42
|
-
}
|
|
43
|
-
number(value) {
|
|
44
|
-
this.wrapper.add({
|
|
45
|
-
type: "value",
|
|
46
|
-
value
|
|
47
|
-
});
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
function range(options) {
|
|
52
|
-
return {
|
|
53
|
-
type: "range",
|
|
54
|
-
...options
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function any() {
|
|
58
|
-
return { type: "any" };
|
|
59
|
-
}
|
|
60
|
-
function step(options) {
|
|
61
|
-
return {
|
|
62
|
-
type: "step",
|
|
63
|
-
...options
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function list(builderFn) {
|
|
67
|
-
const wrapper = new FieldWrapper();
|
|
68
|
-
builderFn(new CronListBuilder(wrapper));
|
|
69
|
-
return {
|
|
70
|
-
type: "list",
|
|
71
|
-
values: wrapper.read()
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function number(value) {
|
|
75
|
-
return {
|
|
76
|
-
type: "value",
|
|
77
|
-
value
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
function cronJobBuilder(buildFn) {
|
|
81
|
-
const fields = {
|
|
82
|
-
second: void 0,
|
|
83
|
-
minute: void 0,
|
|
84
|
-
hour: void 0,
|
|
85
|
-
day: void 0,
|
|
86
|
-
month: void 0,
|
|
87
|
-
weekday: void 0
|
|
88
|
-
};
|
|
89
|
-
const obj = {
|
|
90
|
-
second(expr) {
|
|
91
|
-
fields.second = checkExpr(expr);
|
|
92
|
-
return obj;
|
|
93
|
-
},
|
|
94
|
-
minute(expr) {
|
|
95
|
-
fields.minute = checkExpr(expr);
|
|
96
|
-
return obj;
|
|
97
|
-
},
|
|
98
|
-
hour(expr) {
|
|
99
|
-
fields.hour = checkExpr(expr);
|
|
100
|
-
return obj;
|
|
101
|
-
},
|
|
102
|
-
day(expr) {
|
|
103
|
-
fields.day = checkExpr(expr);
|
|
104
|
-
return obj;
|
|
105
|
-
},
|
|
106
|
-
month(expr) {
|
|
107
|
-
fields.month = checkExpr(expr);
|
|
108
|
-
return obj;
|
|
109
|
-
},
|
|
110
|
-
weekday(expr) {
|
|
111
|
-
fields.weekday = checkExpr(expr);
|
|
112
|
-
return obj;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
buildFn(obj);
|
|
116
|
-
return generate(fields);
|
|
117
|
-
}
|
|
118
|
-
function checkExpr(expr) {
|
|
119
|
-
if (expr.type === "list") {
|
|
120
|
-
const { values } = expr;
|
|
121
|
-
if (values.length === 0) throw new Error("EmptyListError: List expression cannot be empty");
|
|
122
|
-
return values.map((e) => handleSimpleExpression(e)).join(",");
|
|
123
|
-
}
|
|
124
|
-
return handleSimpleExpression(expr);
|
|
125
|
-
}
|
|
126
|
-
function handleSimpleExpression(expr) {
|
|
127
|
-
switch (expr.type) {
|
|
128
|
-
case "any": return "*";
|
|
129
|
-
case "value": return `${expr.value}`;
|
|
130
|
-
case "range": {
|
|
131
|
-
const { min, max } = expr;
|
|
132
|
-
if (min > max) throw new Error(`OutOfBoundError: Expected ${min} <= ${max}`);
|
|
133
|
-
return `${min}-${max}`;
|
|
134
|
-
}
|
|
135
|
-
case "step": {
|
|
136
|
-
const { step, range } = expr;
|
|
137
|
-
if (step === 0) throw new Error(`OutOfBoundError: Expected step value greater than zero`);
|
|
138
|
-
if (!range) return `*/${step}`;
|
|
139
|
-
const { min, max } = range;
|
|
140
|
-
if (max === 0) {
|
|
141
|
-
if (min > max) throw new Error(`OutOfBoundError: Expected max value greater than zero`);
|
|
142
|
-
return `${min}-${max}/${step}`;
|
|
143
|
-
}
|
|
144
|
-
if (!max) return `${min}/${step}`;
|
|
145
|
-
if (min > max) throw new Error(`OutOfBoundError: Expected ${min} <= ${max}`);
|
|
146
|
-
return `${min}-${max}/${step}`;
|
|
147
|
-
}
|
|
148
|
-
default: return "*";
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
function generate(fields) {
|
|
152
|
-
const parts = [];
|
|
153
|
-
for (let index = 0; index < CRON_FIELD_ORDER.length; index++) {
|
|
154
|
-
const key = CRON_FIELD_ORDER[index];
|
|
155
|
-
if (!key) return "";
|
|
156
|
-
if (key === "second" && !fields[key]) continue;
|
|
157
|
-
parts.push(fields[key] ?? "*");
|
|
158
|
-
}
|
|
159
|
-
return parts.join(" ");
|
|
160
|
-
}
|
|
161
|
-
//#endregion
|
|
162
|
-
//#region src/lib/cron/builder/types.ts
|
|
163
|
-
const WeekDay = {
|
|
164
|
-
sun: 0,
|
|
165
|
-
mon: 1,
|
|
166
|
-
tue: 2,
|
|
167
|
-
wed: 3,
|
|
168
|
-
thu: 4,
|
|
169
|
-
fri: 5,
|
|
170
|
-
sat: 6
|
|
171
|
-
};
|
|
172
|
-
const Month = {
|
|
173
|
-
jan: 1,
|
|
174
|
-
feb: 2,
|
|
175
|
-
mar: 3,
|
|
176
|
-
apr: 4,
|
|
177
|
-
may: 5,
|
|
178
|
-
jun: 6,
|
|
179
|
-
jul: 7,
|
|
180
|
-
aug: 8,
|
|
181
|
-
sep: 9,
|
|
182
|
-
oct: 10,
|
|
183
|
-
nov: 11,
|
|
184
|
-
dec: 12
|
|
185
|
-
};
|
|
186
|
-
//#endregion
|
|
187
|
-
//#region src/lib/cron/core/scanner.ts
|
|
188
|
-
const FieldAmount = {
|
|
189
|
-
min: 5,
|
|
190
|
-
max: 6
|
|
191
|
-
};
|
|
192
|
-
var Token = class {
|
|
193
|
-
type;
|
|
194
|
-
component;
|
|
195
|
-
value;
|
|
196
|
-
field;
|
|
197
|
-
constructor(component, type, value, field) {
|
|
198
|
-
this.component = component;
|
|
199
|
-
this.type = type;
|
|
200
|
-
this.value = value;
|
|
201
|
-
this.field = field;
|
|
202
|
-
}
|
|
203
|
-
getComponent() {
|
|
204
|
-
return this.component;
|
|
205
|
-
}
|
|
206
|
-
getTokenType() {
|
|
207
|
-
return this.type;
|
|
208
|
-
}
|
|
209
|
-
getTokenValue() {
|
|
210
|
-
return this.value;
|
|
211
|
-
}
|
|
212
|
-
getField() {
|
|
213
|
-
return this.field;
|
|
214
|
-
}
|
|
215
|
-
toString() {
|
|
216
|
-
return `Token{${`component="${this.component}", type=${this.type}`}, ${`field="${this.field}", value=${this.value}`}}`;
|
|
217
|
-
}
|
|
218
|
-
equals(other) {
|
|
219
|
-
if (!other) return false;
|
|
220
|
-
const isComponentEqual = this.component === other.getComponent();
|
|
221
|
-
const isTokenTypeEqual = this.type === other.getTokenType();
|
|
222
|
-
const isTokenValueEqual = this.value === other.getTokenValue();
|
|
223
|
-
const isFieldEqual = this.field === other.getField();
|
|
224
|
-
return isComponentEqual && isTokenTypeEqual && isTokenValueEqual && isFieldEqual;
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
var Scanner = class {
|
|
228
|
-
expression;
|
|
229
|
-
current;
|
|
230
|
-
start;
|
|
231
|
-
tokens;
|
|
232
|
-
constructor(expression) {
|
|
233
|
-
this.expression = expression;
|
|
234
|
-
this.current = 0;
|
|
235
|
-
this.start = 0;
|
|
236
|
-
this.tokens = [];
|
|
237
|
-
}
|
|
238
|
-
scan() {
|
|
239
|
-
if (this.expression.length === 0) return err("EmptyCronExpressionError", "Cron expression have zero length");
|
|
240
|
-
const fields = this.expression.trim().split(/\s+/);
|
|
241
|
-
const hasMinLen = fields.length === FieldAmount.min;
|
|
242
|
-
const hasMaxLen = fields.length === FieldAmount.max;
|
|
243
|
-
if (!hasMinLen && !hasMaxLen) return err("CronLengthError", `Invalid number of fields for '${this.expression}'. Expected 5 or 6 fields but got ${fields.length} field(s)`);
|
|
244
|
-
const components = this.createComponent(fields);
|
|
245
|
-
for (let idx = 0; idx < components.length; idx++) {
|
|
246
|
-
const component = components[idx];
|
|
247
|
-
if (!component) return err("CronExpressionError", `Invalid cron expression: ${this.expression}`);
|
|
248
|
-
this.current = 0;
|
|
249
|
-
this.start = 0;
|
|
250
|
-
const [error, _] = this.scanComponent(component);
|
|
251
|
-
if (error) return err(error.type, error.message);
|
|
252
|
-
}
|
|
253
|
-
return ok(this.tokens);
|
|
254
|
-
}
|
|
255
|
-
scanComponent(component) {
|
|
256
|
-
const { field, content } = component;
|
|
257
|
-
while (this.current < content.length) {
|
|
258
|
-
let currentCh = this.advance(content);
|
|
259
|
-
switch (currentCh) {
|
|
260
|
-
case "*": {
|
|
261
|
-
const ch = this.peek(content);
|
|
262
|
-
if (this.match(content, "/")) {
|
|
263
|
-
const [error, _] = this.handleStep(component);
|
|
264
|
-
if (error) return err(error.type, error.message);
|
|
265
|
-
} else if (!ch || ch === ",") this.addToken("*", "any", "*", field);
|
|
266
|
-
else return err("CronExpressionError", `Invalid any expression '${content}' for field '${field}'`);
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
case "-":
|
|
270
|
-
currentCh = this.advance(content);
|
|
271
|
-
if (this.isDigit(currentCh)) {
|
|
272
|
-
const [error, _] = this.handleRangeWithStep(component);
|
|
273
|
-
if (error) return err(error.type, error.message);
|
|
274
|
-
} else return err("CronExpressionError", `Invalid range expression '${content}' for field '${field}'`);
|
|
275
|
-
break;
|
|
276
|
-
case ",": {
|
|
277
|
-
if (this.current === 1 && this.start === 0) return err("CronExpressionError", `Invalid list expression '${content}' for field '${field}'`);
|
|
278
|
-
const next = this.peek(content);
|
|
279
|
-
if (!next || next === ",") return err("CronExpressionError", `Invalid list expression '${content}' for field '${field}'`);
|
|
280
|
-
break;
|
|
281
|
-
}
|
|
282
|
-
default:
|
|
283
|
-
if (this.isDigit(currentCh)) {
|
|
284
|
-
const [error, _] = this.handleNumber(component);
|
|
285
|
-
if (error) return err(error.type, error.message);
|
|
286
|
-
} else return err("CronExpressionError", `Invalid cron expression '${this.expression}' in field '${field}'`);
|
|
287
|
-
break;
|
|
288
|
-
}
|
|
289
|
-
this.start = this.current;
|
|
290
|
-
}
|
|
291
|
-
return ok(true);
|
|
292
|
-
}
|
|
293
|
-
addToken(component, type, value, field) {
|
|
294
|
-
const token = new Token(component, type, value, field);
|
|
295
|
-
this.tokens.push(token);
|
|
296
|
-
}
|
|
297
|
-
advance(content) {
|
|
298
|
-
const currentCh = content.charAt(this.current);
|
|
299
|
-
this.current += 1;
|
|
300
|
-
return currentCh;
|
|
301
|
-
}
|
|
302
|
-
match(content, expected) {
|
|
303
|
-
if (this.current >= content.length) return false;
|
|
304
|
-
if (content.charAt(this.current) !== expected) return false;
|
|
305
|
-
this.current += 1;
|
|
306
|
-
return true;
|
|
307
|
-
}
|
|
308
|
-
peek(content) {
|
|
309
|
-
if (this.current >= content.length) return void 0;
|
|
310
|
-
return content.charAt(this.current);
|
|
311
|
-
}
|
|
312
|
-
handleStep(component) {
|
|
313
|
-
const { field, content } = component;
|
|
314
|
-
let ch = this.peek(content);
|
|
315
|
-
const slashIdx = this.current - 1;
|
|
316
|
-
while (ch && this.isDigit(ch)) {
|
|
317
|
-
this.advance(content);
|
|
318
|
-
ch = this.peek(content);
|
|
319
|
-
}
|
|
320
|
-
if (ch && ch !== ",") return err("CronExpressionError", `Invalid step expression '${content}' for field '${field}'`);
|
|
321
|
-
const tokenContent = content.substring(this.start, this.current);
|
|
322
|
-
const value = content.slice(slashIdx + 1, this.current);
|
|
323
|
-
if (value.length === 0) return err("CronExpressionError", `Invalid step expression '${content}' for field '${field}'`);
|
|
324
|
-
this.addToken(tokenContent, "step", Number(value), field);
|
|
325
|
-
return ok(true);
|
|
326
|
-
}
|
|
327
|
-
handleRangeWithStep(component) {
|
|
328
|
-
const { field, content } = component;
|
|
329
|
-
let ch = this.peek(content);
|
|
330
|
-
while (ch && this.isDigit(ch)) {
|
|
331
|
-
this.advance(content);
|
|
332
|
-
ch = this.peek(content);
|
|
333
|
-
}
|
|
334
|
-
if (!ch) return err("CronExpressionError", `Invalid range expression '${content}' for field '${field}'`);
|
|
335
|
-
if (this.match(content, "/")) {
|
|
336
|
-
const [error, _] = this.handleStep(component);
|
|
337
|
-
if (error) return err(error.type, error.message);
|
|
338
|
-
return ok(true);
|
|
339
|
-
}
|
|
340
|
-
return err("CronExpressionError", `Invalid range expression '${content}' for field '${field}'`);
|
|
341
|
-
}
|
|
342
|
-
handleNumber(component) {
|
|
343
|
-
const { field, content } = component;
|
|
344
|
-
let ch = this.peek(content);
|
|
345
|
-
this.start = this.current - 1;
|
|
346
|
-
while (ch && this.isDigit(ch)) {
|
|
347
|
-
this.advance(content);
|
|
348
|
-
ch = this.peek(content);
|
|
349
|
-
}
|
|
350
|
-
if (!ch) {
|
|
351
|
-
const item = content.substring(this.start);
|
|
352
|
-
this.addToken(item, "number", Number(item), field);
|
|
353
|
-
return ok(true);
|
|
354
|
-
}
|
|
355
|
-
if (this.match(content, "-")) {
|
|
356
|
-
const [error, _] = this.handleRange(component);
|
|
357
|
-
if (error) return err(error.type, error.message);
|
|
358
|
-
return ok(true);
|
|
359
|
-
}
|
|
360
|
-
if (this.match(content, "/")) {
|
|
361
|
-
const [error, _] = this.handleStep(component);
|
|
362
|
-
if (error) return err(error.type, error.message);
|
|
363
|
-
return ok(true);
|
|
364
|
-
}
|
|
365
|
-
if (!this.isDigit(ch) && ch !== ",") return err("CronExpressionError", `Invalid number '${content}' for field '${field}'`);
|
|
366
|
-
const item = content.substring(this.start, this.current);
|
|
367
|
-
this.addToken(item, "number", Number(item), field);
|
|
368
|
-
return ok(true);
|
|
369
|
-
}
|
|
370
|
-
handleRange(component) {
|
|
371
|
-
const { field, content } = component;
|
|
372
|
-
let ch = this.peek(content);
|
|
373
|
-
if (!ch) return err("CronExpressionError", `Invalid range expression '${content}' for field '${field}'`);
|
|
374
|
-
while (ch && this.isDigit(ch)) {
|
|
375
|
-
this.advance(content);
|
|
376
|
-
ch = this.peek(content);
|
|
377
|
-
}
|
|
378
|
-
if (!ch) {
|
|
379
|
-
const tokenContent = content.substring(this.start);
|
|
380
|
-
this.addToken(tokenContent, "range", tokenContent, field);
|
|
381
|
-
return ok(true);
|
|
382
|
-
}
|
|
383
|
-
if (this.match(content, "/")) {
|
|
384
|
-
const [error, _] = this.handleStep(component);
|
|
385
|
-
if (error) return err(error.type, error.message);
|
|
386
|
-
return ok(true);
|
|
387
|
-
}
|
|
388
|
-
if (ch && ch !== ",") return err("CronExpressionError", `Invalid range expression '${content}' for field '${field}'`);
|
|
389
|
-
const tokenContent = content.substring(this.start, this.current);
|
|
390
|
-
this.addToken(tokenContent, "range", tokenContent, field);
|
|
391
|
-
return ok(true);
|
|
392
|
-
}
|
|
393
|
-
isDigit(ch) {
|
|
394
|
-
return ch >= "0" && ch <= "9";
|
|
395
|
-
}
|
|
396
|
-
createComponent(fields) {
|
|
397
|
-
const fieldNames = [
|
|
398
|
-
"second",
|
|
399
|
-
"minute",
|
|
400
|
-
"hour",
|
|
401
|
-
"day",
|
|
402
|
-
"month",
|
|
403
|
-
"weekday"
|
|
404
|
-
];
|
|
405
|
-
const components = [];
|
|
406
|
-
let offset = 1;
|
|
407
|
-
if (fields.length === FieldAmount.max) offset = 0;
|
|
408
|
-
for (let idx = 0; idx < fields.length; idx++) {
|
|
409
|
-
const fieldName = fieldNames[idx + offset];
|
|
410
|
-
const content = fields[idx];
|
|
411
|
-
if (!fieldName || !content) break;
|
|
412
|
-
components.push({
|
|
413
|
-
content,
|
|
414
|
-
field: fieldName
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
return components;
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
//#endregion
|
|
421
|
-
//#region src/lib/cron/core/index.ts
|
|
422
|
-
const RETRY_DELAY_MS = 3600 * 1e3;
|
|
423
|
-
const MAX_YEARS = 4;
|
|
424
|
-
const ALIASES = {
|
|
425
|
-
"@yearly": "0 0 1 1 *",
|
|
426
|
-
"@monthly": "0 0 1 * *",
|
|
427
|
-
"@weekly": "0 0 * * 0",
|
|
428
|
-
"@daily": "0 0 * * *",
|
|
429
|
-
"@hourly": "0 * * * *",
|
|
430
|
-
"@minutely": "* * * * *"
|
|
431
|
-
};
|
|
432
|
-
const CronSecondRange = {
|
|
433
|
-
min: 0,
|
|
434
|
-
max: 59
|
|
435
|
-
};
|
|
436
|
-
const CronMinuteRange = {
|
|
437
|
-
min: 0,
|
|
438
|
-
max: 59
|
|
439
|
-
};
|
|
440
|
-
const CronHourRange = {
|
|
441
|
-
min: 0,
|
|
442
|
-
max: 23
|
|
443
|
-
};
|
|
444
|
-
const CronDayRange = {
|
|
445
|
-
min: 1,
|
|
446
|
-
max: 31
|
|
447
|
-
};
|
|
448
|
-
const CronMonthRange = {
|
|
449
|
-
min: 1,
|
|
450
|
-
max: 12
|
|
451
|
-
};
|
|
452
|
-
const CronDayOfWeekRange = {
|
|
453
|
-
min: 0,
|
|
454
|
-
max: 6
|
|
455
|
-
};
|
|
456
|
-
var Cron = class {
|
|
457
|
-
options;
|
|
458
|
-
status = "idle";
|
|
459
|
-
timeoutId = null;
|
|
460
|
-
second = Array(CronSecondRange.max + 1).fill(0);
|
|
461
|
-
minute = Array(CronMinuteRange.max + 1).fill(0);
|
|
462
|
-
hour = Array(CronHourRange.max + 1).fill(0);
|
|
463
|
-
day = Array(CronDayRange.max + 1).fill(0);
|
|
464
|
-
month = Array(CronMonthRange.max + 1).fill(0);
|
|
465
|
-
dayOfWeek = Array(CronDayOfWeekRange.max + 1).fill(0);
|
|
466
|
-
hasSeconds;
|
|
467
|
-
_dayWildcard = false;
|
|
468
|
-
_dowWildcard = false;
|
|
469
|
-
fillRange(values, min, max) {
|
|
470
|
-
for (let i = min; i <= max; i++) values[i] = 1;
|
|
471
|
-
}
|
|
472
|
-
handleStep(part, values, min, max) {
|
|
473
|
-
const [rangePart, stepStr] = part.split("/");
|
|
474
|
-
if (!rangePart) return err("InvalidValueError", `'${rangePart}' is empty`);
|
|
475
|
-
if (!stepStr) return err("InvalidValueError", `'${stepStr}' is empty`);
|
|
476
|
-
const step = Number(stepStr);
|
|
477
|
-
if (!Number.isInteger(step)) return err("InvalidValueError", `'${step}' is not a valid number`);
|
|
478
|
-
if (step <= 0) return err("OutOfBoundError", `Expected ${step} > 0`);
|
|
479
|
-
if (rangePart === "*") {
|
|
480
|
-
for (let i = min; i <= max; i += step) values[i] = 1;
|
|
481
|
-
return ok(true);
|
|
482
|
-
}
|
|
483
|
-
if (rangePart.includes("-")) return this.handleStepRange(rangePart, step, values, min, max);
|
|
484
|
-
return this.handleStepSingle(rangePart, step, values, min, max);
|
|
485
|
-
}
|
|
486
|
-
handleStepRange(range, step, values, min, max) {
|
|
487
|
-
const [startStr, endStr] = range.split("-");
|
|
488
|
-
if (!endStr) return err("InvalidValueError", `'${endStr}' is empty`);
|
|
489
|
-
let start = min;
|
|
490
|
-
if (startStr && startStr.length > 0) start = Number(startStr);
|
|
491
|
-
const end = Number(endStr);
|
|
492
|
-
if (!Number.isInteger(start)) return err("InvalidValueError", `'${start}' is not a valid number`);
|
|
493
|
-
if (!Number.isInteger(end)) return err("InvalidValueError", `'${end}' is not a valid number`);
|
|
494
|
-
if (start < min) return err("OutOfBoundError", `Expected ${start} >= ${min}`);
|
|
495
|
-
if (end > max) return err("OutOfBoundError", `Expected ${end} <= ${max}`);
|
|
496
|
-
if (start > end) return err("OutOfBoundError", `Expected ${start} <= ${end}`);
|
|
497
|
-
for (let i = start; i <= end; i += step) values[i] = 1;
|
|
498
|
-
return ok(true);
|
|
499
|
-
}
|
|
500
|
-
handleStepSingle(value, step, values, min, max) {
|
|
501
|
-
const start = Number(value);
|
|
502
|
-
if (!Number.isInteger(start)) return err("InvalidValueError", `'${start}' is not a valid number`);
|
|
503
|
-
if (start < min) return err("OutOfBoundError", `Expected ${start} >= ${min}`);
|
|
504
|
-
if (start > max) return err("OutOfBoundError", `Expected ${start} <= ${max}`);
|
|
505
|
-
for (let i = start; i <= max; i += step) values[i] = 1;
|
|
506
|
-
return ok(true);
|
|
507
|
-
}
|
|
508
|
-
handleRange(part, values, min, max) {
|
|
509
|
-
const [startStr, endStr] = part.split("-");
|
|
510
|
-
if (!startStr) return err("InvalidValueError", `'${startStr}' is empty`);
|
|
511
|
-
if (!endStr) return err("InvalidValueError", `'${endStr}' is empty`);
|
|
512
|
-
const start = Number(startStr);
|
|
513
|
-
const end = Number(endStr);
|
|
514
|
-
if (!Number.isInteger(start)) return err("InvalidValueError", `'${start}' is not a valid number`);
|
|
515
|
-
if (!Number.isInteger(end)) return err("InvalidValueError", `'${end}' is not a valid number`);
|
|
516
|
-
if (start < min) return err("OutOfBoundError", `Expected ${start} >= ${min}`);
|
|
517
|
-
if (end > max) return err("OutOfBoundError", `Expected ${end} <= ${max}`);
|
|
518
|
-
if (start > end) return err("OutOfBoundError", `Expected ${start} <= ${end}`);
|
|
519
|
-
for (let i = start; i <= end; i++) values[i] = 1;
|
|
520
|
-
return ok(true);
|
|
521
|
-
}
|
|
522
|
-
handleNumber(value, values, min, max) {
|
|
523
|
-
const n = Number(value);
|
|
524
|
-
if (!Number.isInteger(n)) return err("InvalidValueError", `'${value}' is not a valid number`);
|
|
525
|
-
if (n < min) return err("OutOfBoundError", `Expected ${n} >= ${min}`);
|
|
526
|
-
if (n > max) return err("OutOfBoundError", `Expected ${n} <= ${max}`);
|
|
527
|
-
values[n] = 1;
|
|
528
|
-
return ok(true);
|
|
529
|
-
}
|
|
530
|
-
constructor(options) {
|
|
531
|
-
this.options = options;
|
|
532
|
-
const expr = this.resolveAlias(options.schedule);
|
|
533
|
-
const [error, tokens] = new Scanner(expr).scan();
|
|
534
|
-
if (error) throw new Error(`${error.type}: ${error.message}`);
|
|
535
|
-
this.hasSeconds = expr.trim().split(/\s+/).length === FieldAmount.max;
|
|
536
|
-
const [parsingError, _] = this.parse(tokens);
|
|
537
|
-
if (parsingError) throw new Error(`${parsingError.type}: ${parsingError.message}`);
|
|
538
|
-
}
|
|
539
|
-
resolveAlias(schedule) {
|
|
540
|
-
return ALIASES[schedule] || schedule;
|
|
541
|
-
}
|
|
542
|
-
parse(tokens) {
|
|
543
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
544
|
-
const token = tokens[i];
|
|
545
|
-
if (!token) return err("InvalidValueError", "Undefined token");
|
|
546
|
-
const tokenType = token.getTokenType();
|
|
547
|
-
switch (token.getField()) {
|
|
548
|
-
case "second": {
|
|
549
|
-
const [error, _] = this.handleField(token, this.second, CronSecondRange.min, CronSecondRange.max);
|
|
550
|
-
if (error) return err(error.type, `${error.message} in field '${token.getField()}'`);
|
|
551
|
-
break;
|
|
552
|
-
}
|
|
553
|
-
case "minute": {
|
|
554
|
-
const [error, _] = this.handleField(token, this.minute, CronMinuteRange.min, CronMinuteRange.max);
|
|
555
|
-
if (error) return err(error.type, `${error.message} in field '${token.getField()}'`);
|
|
556
|
-
break;
|
|
557
|
-
}
|
|
558
|
-
case "hour": {
|
|
559
|
-
const [error, _] = this.handleField(token, this.hour, CronHourRange.min, CronHourRange.max);
|
|
560
|
-
if (error) return err(error.type, `${error.message} in field '${token.getField()}'`);
|
|
561
|
-
break;
|
|
562
|
-
}
|
|
563
|
-
case "day": {
|
|
564
|
-
if (tokenType === "any") this._dayWildcard = true;
|
|
565
|
-
const [error, _] = this.handleField(token, this.day, CronDayRange.min, CronDayRange.max);
|
|
566
|
-
if (error) return err(error.type, `${error.message} in field '${token.getField()}'`);
|
|
567
|
-
break;
|
|
568
|
-
}
|
|
569
|
-
case "month": {
|
|
570
|
-
const [error, _] = this.handleField(token, this.month, CronMonthRange.min, CronMonthRange.max);
|
|
571
|
-
if (error) return err(error.type, `${error.message} in field '${token.getField()}'`);
|
|
572
|
-
break;
|
|
573
|
-
}
|
|
574
|
-
case "weekday": {
|
|
575
|
-
if (tokenType === "any") this._dowWildcard = true;
|
|
576
|
-
const [error, _] = this.handleField(token, this.dayOfWeek, CronDayOfWeekRange.min, CronDayOfWeekRange.max);
|
|
577
|
-
if (error) return err(error.type, `${error.message} in field '${token.getField()}'`);
|
|
578
|
-
break;
|
|
579
|
-
}
|
|
580
|
-
default: return err("InvalidValueError", `Invalid field '${token.getField()}'`);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return ok(true);
|
|
584
|
-
}
|
|
585
|
-
handleField(token, field, min, max) {
|
|
586
|
-
switch (token.getTokenType()) {
|
|
587
|
-
case "any":
|
|
588
|
-
this.fillRange(field, min, max);
|
|
589
|
-
break;
|
|
590
|
-
case "number": {
|
|
591
|
-
const [error, _] = this.handleNumber(token.getComponent(), field, min, max);
|
|
592
|
-
if (error) return err(error.type, error.message);
|
|
593
|
-
break;
|
|
594
|
-
}
|
|
595
|
-
case "range": {
|
|
596
|
-
const component = token.getComponent();
|
|
597
|
-
const [error, _] = this.handleRange(component, field, min, max);
|
|
598
|
-
if (error) return err(error.type, error.message);
|
|
599
|
-
break;
|
|
600
|
-
}
|
|
601
|
-
case "step": {
|
|
602
|
-
const component = token.getComponent();
|
|
603
|
-
const [error, _] = this.handleStep(component, field, min, max);
|
|
604
|
-
if (error) return err(error.type, error.message);
|
|
605
|
-
break;
|
|
606
|
-
}
|
|
607
|
-
default: return err("InvalidValueError", `Invalid token type '${token.getTokenType()}'`);
|
|
608
|
-
}
|
|
609
|
-
return ok(true);
|
|
610
|
-
}
|
|
611
|
-
matches(date) {
|
|
612
|
-
const s = date.getSeconds();
|
|
613
|
-
const m = date.getMinutes();
|
|
614
|
-
const h = date.getHours();
|
|
615
|
-
const d = date.getDate();
|
|
616
|
-
const mon = date.getMonth();
|
|
617
|
-
const dow = date.getDay();
|
|
618
|
-
const isSecondMatch = this.hasSeconds ? this.second[s] === 1 : true;
|
|
619
|
-
const isMinuteMatch = this.minute[m] === 1;
|
|
620
|
-
const isHourMatch = this.hour[h] === 1;
|
|
621
|
-
const isMonthMatch = this.month[mon + 1] === 1;
|
|
622
|
-
let isDayOrDowMatch;
|
|
623
|
-
if (!this._dayWildcard && !this._dowWildcard) isDayOrDowMatch = this.day[d] === 1 || this.dayOfWeek[dow] === 1;
|
|
624
|
-
else isDayOrDowMatch = this.day[d] === 1 && this.dayOfWeek[dow] === 1;
|
|
625
|
-
return isSecondMatch && isMinuteMatch && isHourMatch && isDayOrDowMatch && isMonthMatch;
|
|
626
|
-
}
|
|
627
|
-
getNextRun() {
|
|
628
|
-
const date = /* @__PURE__ */ new Date();
|
|
629
|
-
if (this.hasSeconds) {
|
|
630
|
-
date.setMilliseconds(0);
|
|
631
|
-
date.setSeconds(date.getSeconds() + 1);
|
|
632
|
-
} else {
|
|
633
|
-
date.setSeconds(0, 0);
|
|
634
|
-
date.setMinutes(date.getMinutes() + 1);
|
|
635
|
-
}
|
|
636
|
-
const deadline = new Date(date);
|
|
637
|
-
deadline.setFullYear(deadline.getFullYear() + MAX_YEARS);
|
|
638
|
-
while (date < deadline) {
|
|
639
|
-
const s = date.getSeconds();
|
|
640
|
-
const m = date.getMinutes();
|
|
641
|
-
const h = date.getHours();
|
|
642
|
-
const d = date.getDate();
|
|
643
|
-
const mon = date.getMonth();
|
|
644
|
-
const dow = date.getDay();
|
|
645
|
-
if (this.month[mon + 1] === 0) {
|
|
646
|
-
date.setDate(1);
|
|
647
|
-
date.setMonth(mon + 1);
|
|
648
|
-
date.setHours(0, 0, 0, 0);
|
|
649
|
-
continue;
|
|
650
|
-
}
|
|
651
|
-
let isDayOrDowMatch;
|
|
652
|
-
if (!this._dayWildcard && !this._dowWildcard) isDayOrDowMatch = this.day[d] === 0 && this.dayOfWeek[dow] === 0;
|
|
653
|
-
else isDayOrDowMatch = this.day[d] === 0 || this.dayOfWeek[dow] === 0;
|
|
654
|
-
if (isDayOrDowMatch) {
|
|
655
|
-
date.setDate(d + 1);
|
|
656
|
-
date.setHours(0, 0, 0, 0);
|
|
657
|
-
continue;
|
|
658
|
-
}
|
|
659
|
-
if (this.hour[h] === 0) {
|
|
660
|
-
date.setHours(h + 1);
|
|
661
|
-
date.setMinutes(0, 0, 0);
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
if (this.minute[m] === 0) {
|
|
665
|
-
date.setMinutes(m + 1);
|
|
666
|
-
date.setSeconds(0, 0);
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
if (this.hasSeconds && this.second[s] === 0) {
|
|
670
|
-
date.setSeconds(s + 1);
|
|
671
|
-
date.setMilliseconds(0);
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
return new Date(date);
|
|
675
|
-
}
|
|
676
|
-
return null;
|
|
677
|
-
}
|
|
678
|
-
start() {
|
|
679
|
-
if (this.status !== "idle") return;
|
|
680
|
-
this.status = "running";
|
|
681
|
-
this.next();
|
|
682
|
-
}
|
|
683
|
-
stop() {
|
|
684
|
-
this.status = "idle";
|
|
685
|
-
if (this.timeoutId) {
|
|
686
|
-
clearTimeout(this.timeoutId);
|
|
687
|
-
this.timeoutId = null;
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
pause() {
|
|
691
|
-
if (this.status !== "running") return;
|
|
692
|
-
this.status = "paused";
|
|
693
|
-
if (this.timeoutId) {
|
|
694
|
-
clearTimeout(this.timeoutId);
|
|
695
|
-
this.timeoutId = null;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
resume() {
|
|
699
|
-
if (this.status !== "paused") return;
|
|
700
|
-
this.status = "running";
|
|
701
|
-
this.next();
|
|
702
|
-
}
|
|
703
|
-
getStatus() {
|
|
704
|
-
return this.status;
|
|
705
|
-
}
|
|
706
|
-
next() {
|
|
707
|
-
if (this.status !== "running") return;
|
|
708
|
-
const nextRun = this.getNextRun();
|
|
709
|
-
if (!nextRun) {
|
|
710
|
-
this.timeoutId = setTimeout(() => this.next(), RETRY_DELAY_MS);
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
const delay = nextRun.getTime() - Date.now();
|
|
714
|
-
this.timeoutId = setTimeout(() => {
|
|
715
|
-
this.run();
|
|
716
|
-
}, Math.max(0, delay));
|
|
717
|
-
}
|
|
718
|
-
async run() {
|
|
719
|
-
if (this.status !== "running") return;
|
|
720
|
-
const handlerResult = this.options.handler();
|
|
721
|
-
await mightThrow(Promise.resolve(handlerResult));
|
|
722
|
-
if (this.status === "running") this.next();
|
|
723
|
-
}
|
|
724
|
-
};
|
|
725
|
-
//#endregion
|
|
726
|
-
export { Cron, Month, WeekDay, any, cronJobBuilder, list, number, range, step };
|
|
1
|
+
import{mightThrow as e,mightThrowSync as t}from"../errors/index.mjs";var n=class extends Error{constructor(e){super(e),this.name=`OutOfBoundError`}},r=class extends TypeError{constructor(e){super(e),this.name=`InvalidRetryError`}};const i=[`minute`,`hour`,`day`,`month`,`weekday`];var a=class{fields=[];add(e){this.fields.push(e)}read(){return this.fields}},o=class{wrapper;constructor(e){this.wrapper=e}any(){return this.wrapper.add({type:`any`}),this}range(e){return this.wrapper.add({type:`range`,...e}),this}step(e){return this.wrapper.add({type:`step`,...e}),this}number(e){return this.wrapper.add({type:`value`,value:e}),this}};function s(e){return{type:`range`,...e}}function c(){return{type:`any`}}function l(e){return{type:`step`,...e}}function u(e){let t=new a;return e(new o(t)),{type:`list`,values:t.read()}}function d(e){return{type:`value`,value:e}}function f(e){let t={minute:void 0,hour:void 0,day:void 0,month:void 0,weekday:void 0},n={minute(e){return t.minute=p(e),n},hour(e){return t.hour=p(e),n},day(e){return t.day=p(e),n},month(e){return t.month=p(e),n},weekday(e){return t.weekday=p(e),n}};return e(n),h(t)}function p(e){if(e.type===`list`){let{values:t}=e;if(t.length===0)throw Error(`EmptyListError: List expression cannot be empty`);return t.map(e=>m(e)).join(`,`)}return m(e)}function m(e){switch(e.type){case`any`:return`*`;case`value`:return`${e.value}`;case`range`:{let{min:t,max:r}=e;if(t>r)throw new n(`Expected ${t} <= ${r}`);return`${t}-${r}`}case`step`:{let{step:t,range:r}=e;if(t===0)throw new n(`Expected step value greater than zero`);if(!r)return`*/${t}`;let{min:i,max:a}=r;if(a===0){if(i>a)throw new n(`Expected max value greater than zero`);return`${i}-${a}/${t}`}if(!a)return`${i}/${t}`;if(i>a)throw new n(`Expected ${i} <= ${a}`);return`${i}-${a}/${t}`}default:return`*`}}function h(e){let t=[];for(let n=0;n<i.length;n++){let r=i[n];if(!r)return``;t.push(e[r]??`*`)}return t.join(` `)}const g={sun:0,mon:1,tue:2,wed:3,thu:4,fri:5,sat:6},_={jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12};var v=class{constructor(){}};const y={"@yearly":`0 0 1 1 *`,"@annually":`0 0 1 1 *`,"@monthly":`0 0 1 * *`,"@weekly":`0 0 * * 0`,"@daily":`0 0 * * *`,"@midnight":`0 0 * * *`,"@hourly":`0 * * * *`,"@minutely":`* * * * *`};var b=class{getExpression(e){return y[e]||e}getJobName(e){return e.name}next(e,n){let{schedule:r}=e,i=this.getExpression(r),[a,o]=t(()=>Bun.cron.parse(i,n));if(a)throw a;return o}},x=class{options;jobs=new Map;constructor(e){if(this.options=e,!this.checkAttempts())throw new r(`Expected 'maxAttempts' to be a finite non-negative integer`)}async update(e){if(e.type===`add`){this.jobs.set(e.name,0);return}let t=this.jobs.get(e.name);if(t===void 0)return;if(e.type===`success`){this.jobs.set(e.name,0);return}let{job:n,error:r,name:i}=e,{maxAttempts:a}=this.options,o=this.runOnRetryError(r,i);if(t<a&&o){let n=this.calculateDelay(t);if(this.options.onFailedAttempt){let e={attemptNumber:t+1,delay:n,error:r,retriesLeft:a-t,jobName:i};await this.options.onFailedAttempt(e)}this.jobs.set(e.name,t+1),await this.runDelay(n);return}if(n.stop(),!this.options.onError)throw r;let s={name:i,error:r,failedAt:Date.now()};await this.options.onError(s)}checkAttempts(){let{maxAttempts:e}=this.options;return e>=0&&Number.isSafeInteger(e)&&!Object.is(e,-0)}runOnRetryError(e,t){return this.options.retryOnError?this.options.retryOnError({error:e,jobName:t}):!0}async runDelay(e){await new Promise(t=>setTimeout(t,e))}calculateDelay(e){let t=1e3*2**e;return Math.round(Math.random()*(Math.min(t,6e4)+1))}},S=class{listener=null;subscribe(e){this.listener=e}unsubscribe(){this.listener=null}async notify(e){this.listener&&await this.listener.update(e)}},C=class extends v{options;status;cron=null;manager;common;constructor(e){super(),this.options=e,this.status=`idle`,this.common=new b,this.options.retry&&(this.manager=new S,this.manager.subscribe(this.options.retry),this.manager.notify({type:`add`,name:this.getJobName()}))}[Symbol.dispose](){this.stop()}getStatus(){return this.status}run(){if(this.status===`running`)return;let{schedule:n,handler:r}=this.options,[i,a]=t(()=>{let t=this.common.getExpression(n);return Bun.cron(t,async()=>{let[t]=await e(Promise.resolve().then(()=>r()));if(!t)return this.manager&&await this.manager.notify({type:`success`,name:this.getJobName()}),Promise.resolve();if(this.manager){let e={type:`error`,error:t,job:this,name:this.getJobName()};await this.manager.notify(e);return}await Promise.reject(t)})});if(!i){this.status=`running`,this.cron=a;return}throw i}stop(){this.status===`running`&&this.cron&&(this.cron.stop(),this.status=`idle`)}ref(){this.status===`running`&&this.cron&&this.cron.ref()}unref(){this.status===`running`&&this.cron&&this.cron.unref()}getExpression(){return this.common.getExpression(this.options.schedule)}getJobName(){return this.common.getJobName(this.options)}next(e){return this.common.next(this.options,e)}},w=class{options;common;constructor(e){this.options=e,this.common=new b}async run(){let{path:e,schedule:t,name:n}=this.options,r=this.common.getExpression(t);await Bun.cron(e,r,n)}async stop(){await Bun.cron.remove(this.options.name)}getExpression(){return this.common.getExpression(this.options.schedule)}getJobName(){return this.common.getJobName(this.options)}next(e){return this.common.next(this.options,e)}};export{C as Cron,w as CronOS,r as InvalidRetryError,_ as Month,n as OutOfBoundError,x as RetryCronJob,g as WeekDay,c as any,f as cronJobBuilder,u as list,d as number,s as range,l as step};
|