@multiplier-labs/stepflow 0.3.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/.github/workflows/publish.yml +30 -0
- package/README.md +408 -0
- package/dist/chunk-FQCT3I2A.js +1512 -0
- package/dist/chunk-FQCT3I2A.js.map +1 -0
- package/dist/chunk-GXY66V7Q.js +111 -0
- package/dist/chunk-GXY66V7Q.js.map +1 -0
- package/dist/chunk-U5QGN3RT.js +715 -0
- package/dist/chunk-U5QGN3RT.js.map +1 -0
- package/dist/chunk-XCF4PLN7.js +575 -0
- package/dist/chunk-XCF4PLN7.js.map +1 -0
- package/dist/events/index.d.ts +346 -0
- package/dist/events/index.js +11 -0
- package/dist/events/index.js.map +1 -0
- package/dist/index-CPfGsmrp.d.ts +567 -0
- package/dist/index.d.ts +748 -0
- package/dist/index.js +1707 -0
- package/dist/index.js.map +1 -0
- package/dist/scheduler/index.d.ts +6 -0
- package/dist/scheduler/index.js +12 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/storage/index.d.ts +402 -0
- package/dist/storage/index.js +13 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/types-BVShq7zT.d.ts +150 -0
- package/dist/types-BlwFEGzj.d.ts +276 -0
- package/dist/types-DzkEqnQJ.d.ts +75 -0
- package/docs/github-packages-setup.md +97 -0
- package/docs/stepflow-api-reference.md +1662 -0
- package/package.json +85 -0
- package/src/core/engine.test.ts +1421 -0
- package/src/core/engine.ts +717 -0
- package/src/core/index.ts +7 -0
- package/src/core/orchestrator.ts +590 -0
- package/src/core/types.ts +242 -0
- package/src/events/index.ts +8 -0
- package/src/events/memory.test.ts +109 -0
- package/src/events/memory.ts +95 -0
- package/src/events/socketio.test.ts +428 -0
- package/src/events/socketio.ts +269 -0
- package/src/events/types.ts +121 -0
- package/src/events/webhook.test.ts +695 -0
- package/src/events/webhook.ts +591 -0
- package/src/index.ts +270 -0
- package/src/planning/index.ts +52 -0
- package/src/planning/planner.ts +601 -0
- package/src/planning/planning.test.ts +790 -0
- package/src/planning/registry.test.ts +445 -0
- package/src/planning/registry.ts +369 -0
- package/src/planning/types.ts +570 -0
- package/src/scheduler/cron.test.ts +480 -0
- package/src/scheduler/cron.ts +449 -0
- package/src/scheduler/index.ts +18 -0
- package/src/scheduler/postgres-persistence.test.ts +906 -0
- package/src/scheduler/postgres-persistence.ts +546 -0
- package/src/scheduler/sqlite-persistence.test.ts +235 -0
- package/src/scheduler/sqlite-persistence.ts +268 -0
- package/src/scheduler/types.ts +78 -0
- package/src/storage/index.ts +27 -0
- package/src/storage/memory.test.ts +399 -0
- package/src/storage/memory.ts +248 -0
- package/src/storage/postgres.ts +1550 -0
- package/src/storage/sqlite.test.ts +593 -0
- package/src/storage/sqlite.ts +662 -0
- package/src/storage/types.ts +368 -0
- package/src/utils/errors.test.ts +152 -0
- package/src/utils/errors.ts +191 -0
- package/src/utils/id.ts +21 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.test.ts +127 -0
- package/src/utils/logger.ts +96 -0
- package/src/utils/postgres-deps.ts +63 -0
- package/src/utils/retry.test.ts +197 -0
- package/src/utils/retry.ts +121 -0
- package/tsconfig.json +21 -0
- package/tsup.config.ts +14 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CronScheduler - Scheduler implementation using cron expressions.
|
|
3
|
+
*
|
|
4
|
+
* Provides time-based and workflow-completion-based triggers for workflows.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CronExpressionParser } from "cron-parser";
|
|
8
|
+
import type { WorkflowKind, RunStatus, Logger } from "../core/types";
|
|
9
|
+
import type { WorkflowEngine } from "../core/engine";
|
|
10
|
+
import type { Scheduler, WorkflowSchedule, TriggerType } from "./types";
|
|
11
|
+
import type { WorkflowEvent } from "../events/types";
|
|
12
|
+
import { generateId } from "../utils/id";
|
|
13
|
+
import { ConsoleLogger } from "../utils/logger";
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Configuration Types
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for the CronScheduler.
|
|
21
|
+
*/
|
|
22
|
+
export interface CronSchedulerConfig {
|
|
23
|
+
/** The workflow engine instance */
|
|
24
|
+
engine: WorkflowEngine;
|
|
25
|
+
|
|
26
|
+
/** Logger instance */
|
|
27
|
+
logger?: Logger;
|
|
28
|
+
|
|
29
|
+
/** Poll interval for checking schedules (ms, default: 1000) */
|
|
30
|
+
pollInterval?: number;
|
|
31
|
+
|
|
32
|
+
/** Optional persistence adapter for schedules */
|
|
33
|
+
persistence?: SchedulePersistence;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Interface for schedule persistence.
|
|
38
|
+
* Implement this to persist schedules to a database.
|
|
39
|
+
*/
|
|
40
|
+
export interface SchedulePersistence {
|
|
41
|
+
/** Load all schedules from storage */
|
|
42
|
+
loadSchedules(): Promise<WorkflowSchedule[]>;
|
|
43
|
+
|
|
44
|
+
/** Save a schedule */
|
|
45
|
+
saveSchedule(schedule: WorkflowSchedule): Promise<void>;
|
|
46
|
+
|
|
47
|
+
/** Update a schedule */
|
|
48
|
+
updateSchedule(
|
|
49
|
+
scheduleId: string,
|
|
50
|
+
updates: Partial<WorkflowSchedule>,
|
|
51
|
+
): Promise<void>;
|
|
52
|
+
|
|
53
|
+
/** Delete a schedule */
|
|
54
|
+
deleteSchedule(scheduleId: string): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// CronScheduler Class
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Scheduler implementation that supports cron expressions and workflow completion triggers.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const scheduler = new CronScheduler({
|
|
67
|
+
* engine,
|
|
68
|
+
* pollInterval: 1000,
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Add a cron schedule (every day at midnight)
|
|
72
|
+
* await scheduler.addSchedule({
|
|
73
|
+
* workflowKind: 'cleanup.daily',
|
|
74
|
+
* triggerType: 'cron',
|
|
75
|
+
* cronExpression: '0 0 * * *',
|
|
76
|
+
* enabled: true,
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* // Add a workflow completion trigger
|
|
80
|
+
* await scheduler.addSchedule({
|
|
81
|
+
* workflowKind: 'notification.send',
|
|
82
|
+
* triggerType: 'workflow_completed',
|
|
83
|
+
* triggerOnWorkflowKind: 'order.process',
|
|
84
|
+
* triggerOnStatus: ['succeeded'],
|
|
85
|
+
* enabled: true,
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* await scheduler.start();
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export class CronScheduler implements Scheduler {
|
|
92
|
+
private engine: WorkflowEngine;
|
|
93
|
+
private logger: Logger;
|
|
94
|
+
private pollInterval: number;
|
|
95
|
+
private persistence?: SchedulePersistence;
|
|
96
|
+
|
|
97
|
+
private schedules = new Map<string, WorkflowSchedule>();
|
|
98
|
+
private running = false;
|
|
99
|
+
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
100
|
+
private eventUnsubscribe: (() => void) | null = null;
|
|
101
|
+
|
|
102
|
+
constructor(config: CronSchedulerConfig) {
|
|
103
|
+
this.engine = config.engine;
|
|
104
|
+
this.logger = config.logger ?? new ConsoleLogger();
|
|
105
|
+
this.pollInterval = config.pollInterval ?? 1000;
|
|
106
|
+
this.persistence = config.persistence;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Scheduler Interface Implementation
|
|
111
|
+
// ============================================================================
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Start the scheduler.
|
|
115
|
+
* Begins polling for cron schedules and subscribes to workflow completion events.
|
|
116
|
+
*/
|
|
117
|
+
async start(): Promise<void> {
|
|
118
|
+
if (this.running) {
|
|
119
|
+
this.logger.warn("Scheduler is already running");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.logger.info("Starting scheduler...");
|
|
124
|
+
|
|
125
|
+
// Load schedules from persistence if available
|
|
126
|
+
if (this.persistence) {
|
|
127
|
+
const loaded = await this.persistence.loadSchedules();
|
|
128
|
+
for (const schedule of loaded) {
|
|
129
|
+
this.schedules.set(schedule.id, schedule);
|
|
130
|
+
}
|
|
131
|
+
this.logger.info(`Loaded ${loaded.length} schedules from persistence`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Calculate next run times for cron schedules
|
|
135
|
+
for (const schedule of this.schedules.values()) {
|
|
136
|
+
if (schedule.triggerType === "cron" && schedule.cronExpression) {
|
|
137
|
+
this.updateNextRunTime(schedule);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Start polling for cron schedules
|
|
142
|
+
this.pollTimer = setInterval(
|
|
143
|
+
() => this.checkSchedules(),
|
|
144
|
+
this.pollInterval,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Subscribe to workflow completion events
|
|
148
|
+
this.eventUnsubscribe = this.engine.subscribeToAll((event) => {
|
|
149
|
+
this.handleWorkflowEvent(event);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
this.running = true;
|
|
153
|
+
this.logger.info("Scheduler started");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Stop the scheduler.
|
|
158
|
+
* Stops polling and unsubscribes from events.
|
|
159
|
+
*/
|
|
160
|
+
async stop(): Promise<void> {
|
|
161
|
+
if (!this.running) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.logger.info("Stopping scheduler...");
|
|
166
|
+
|
|
167
|
+
if (this.pollTimer) {
|
|
168
|
+
clearInterval(this.pollTimer);
|
|
169
|
+
this.pollTimer = null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (this.eventUnsubscribe) {
|
|
173
|
+
this.eventUnsubscribe();
|
|
174
|
+
this.eventUnsubscribe = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.running = false;
|
|
178
|
+
this.logger.info("Scheduler stopped");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Add a new schedule.
|
|
183
|
+
*/
|
|
184
|
+
async addSchedule(
|
|
185
|
+
scheduleData: Omit<WorkflowSchedule, "id">,
|
|
186
|
+
): Promise<WorkflowSchedule> {
|
|
187
|
+
const schedule: WorkflowSchedule = {
|
|
188
|
+
...scheduleData,
|
|
189
|
+
id: generateId(),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Validate cron expression if provided
|
|
193
|
+
if (schedule.triggerType === "cron" && schedule.cronExpression) {
|
|
194
|
+
try {
|
|
195
|
+
CronExpressionParser.parse(schedule.cronExpression, {
|
|
196
|
+
tz: schedule.timezone,
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw new Error(`Invalid cron expression: ${schedule.cronExpression}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Calculate next run time
|
|
203
|
+
this.updateNextRunTime(schedule);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Validate workflow completion trigger
|
|
207
|
+
if (schedule.triggerType === "workflow_completed") {
|
|
208
|
+
if (!schedule.triggerOnWorkflowKind) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"triggerOnWorkflowKind is required for workflow_completed triggers",
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.schedules.set(schedule.id, schedule);
|
|
216
|
+
|
|
217
|
+
// Persist if available
|
|
218
|
+
if (this.persistence) {
|
|
219
|
+
await this.persistence.saveSchedule(schedule);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.logger.info(
|
|
223
|
+
`Added schedule: ${schedule.id} (${schedule.workflowKind})`,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
return schedule;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Remove a schedule.
|
|
231
|
+
*/
|
|
232
|
+
async removeSchedule(scheduleId: string): Promise<void> {
|
|
233
|
+
if (!this.schedules.has(scheduleId)) {
|
|
234
|
+
throw new Error(`Schedule not found: ${scheduleId}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.schedules.delete(scheduleId);
|
|
238
|
+
|
|
239
|
+
if (this.persistence) {
|
|
240
|
+
await this.persistence.deleteSchedule(scheduleId);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
this.logger.info(`Removed schedule: ${scheduleId}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Update a schedule.
|
|
248
|
+
*/
|
|
249
|
+
async updateSchedule(
|
|
250
|
+
scheduleId: string,
|
|
251
|
+
updates: Partial<WorkflowSchedule>,
|
|
252
|
+
): Promise<void> {
|
|
253
|
+
const schedule = this.schedules.get(scheduleId);
|
|
254
|
+
if (!schedule) {
|
|
255
|
+
throw new Error(`Schedule not found: ${scheduleId}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Don't allow changing the ID
|
|
259
|
+
delete updates.id;
|
|
260
|
+
|
|
261
|
+
// Update the schedule
|
|
262
|
+
Object.assign(schedule, updates);
|
|
263
|
+
|
|
264
|
+
// Revalidate and update next run time if cron expression changed
|
|
265
|
+
if (
|
|
266
|
+
updates.cronExpression !== undefined ||
|
|
267
|
+
updates.timezone !== undefined
|
|
268
|
+
) {
|
|
269
|
+
if (schedule.cronExpression) {
|
|
270
|
+
try {
|
|
271
|
+
CronExpressionParser.parse(schedule.cronExpression, {
|
|
272
|
+
tz: schedule.timezone,
|
|
273
|
+
});
|
|
274
|
+
this.updateNextRunTime(schedule);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Invalid cron expression: ${schedule.cronExpression}`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (this.persistence) {
|
|
284
|
+
await this.persistence.updateSchedule(scheduleId, updates);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
this.logger.debug(`Updated schedule: ${scheduleId}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get all schedules.
|
|
292
|
+
*/
|
|
293
|
+
async getSchedules(): Promise<WorkflowSchedule[]> {
|
|
294
|
+
return Array.from(this.schedules.values());
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get a schedule by ID.
|
|
299
|
+
*/
|
|
300
|
+
getSchedule(scheduleId: string): WorkflowSchedule | undefined {
|
|
301
|
+
return this.schedules.get(scheduleId);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Manually trigger a scheduled workflow.
|
|
306
|
+
*/
|
|
307
|
+
async triggerNow(scheduleId: string): Promise<string> {
|
|
308
|
+
const schedule = this.schedules.get(scheduleId);
|
|
309
|
+
if (!schedule) {
|
|
310
|
+
throw new Error(`Schedule not found: ${scheduleId}`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return this.executeSchedule(schedule);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// Internal Methods
|
|
318
|
+
// ============================================================================
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Check all cron schedules and execute those that are due.
|
|
322
|
+
*/
|
|
323
|
+
private checkSchedules(): void {
|
|
324
|
+
const now = new Date();
|
|
325
|
+
|
|
326
|
+
for (const schedule of this.schedules.values()) {
|
|
327
|
+
if (!schedule.enabled) continue;
|
|
328
|
+
if (schedule.triggerType !== "cron") continue;
|
|
329
|
+
if (!schedule.nextRunAt) continue;
|
|
330
|
+
|
|
331
|
+
if (schedule.nextRunAt <= now) {
|
|
332
|
+
// Execute the schedule
|
|
333
|
+
this.executeSchedule(schedule).catch((error) => {
|
|
334
|
+
this.logger.error(
|
|
335
|
+
`Failed to execute schedule ${schedule.id}:`,
|
|
336
|
+
error,
|
|
337
|
+
);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Update next run time
|
|
341
|
+
this.updateNextRunTime(schedule);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Handle workflow events for completion triggers.
|
|
348
|
+
*/
|
|
349
|
+
private handleWorkflowEvent(event: WorkflowEvent): void {
|
|
350
|
+
// Only handle run completion events
|
|
351
|
+
// Note: orchestrator emits 'run.completed' for success, 'run.failed' for failure
|
|
352
|
+
if (
|
|
353
|
+
event.eventType !== "run.completed" &&
|
|
354
|
+
event.eventType !== "run.failed"
|
|
355
|
+
) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const completedStatus: RunStatus =
|
|
360
|
+
event.eventType === "run.completed" ? "succeeded" : "failed";
|
|
361
|
+
const completedKind = event.kind;
|
|
362
|
+
|
|
363
|
+
// Find schedules triggered by this workflow completion
|
|
364
|
+
for (const schedule of this.schedules.values()) {
|
|
365
|
+
if (!schedule.enabled) continue;
|
|
366
|
+
if (schedule.triggerType !== "workflow_completed") continue;
|
|
367
|
+
if (schedule.triggerOnWorkflowKind !== completedKind) continue;
|
|
368
|
+
|
|
369
|
+
// Check if the status matches
|
|
370
|
+
if (
|
|
371
|
+
schedule.triggerOnStatus &&
|
|
372
|
+
!schedule.triggerOnStatus.includes(completedStatus)
|
|
373
|
+
) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Execute the schedule
|
|
378
|
+
this.executeSchedule(schedule, {
|
|
379
|
+
triggerRunId: event.runId,
|
|
380
|
+
triggerStatus: completedStatus,
|
|
381
|
+
}).catch((error) => {
|
|
382
|
+
this.logger.error(`Failed to execute schedule ${schedule.id}:`, error);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Execute a schedule by starting the workflow.
|
|
389
|
+
*/
|
|
390
|
+
private async executeSchedule(
|
|
391
|
+
schedule: WorkflowSchedule,
|
|
392
|
+
triggerContext?: { triggerRunId?: string; triggerStatus?: RunStatus },
|
|
393
|
+
): Promise<string> {
|
|
394
|
+
this.logger.info(
|
|
395
|
+
`Executing schedule: ${schedule.id} (${schedule.workflowKind})`,
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
// Build metadata with trigger info
|
|
399
|
+
const metadata = {
|
|
400
|
+
...schedule.metadata,
|
|
401
|
+
scheduleId: schedule.id,
|
|
402
|
+
triggerType: schedule.triggerType,
|
|
403
|
+
...(triggerContext ?? {}),
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// Start the workflow
|
|
407
|
+
const runId = await this.engine.startRun({
|
|
408
|
+
kind: schedule.workflowKind,
|
|
409
|
+
input: schedule.input,
|
|
410
|
+
metadata,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Update schedule state
|
|
414
|
+
schedule.lastRunAt = new Date();
|
|
415
|
+
schedule.lastRunId = runId;
|
|
416
|
+
|
|
417
|
+
if (this.persistence) {
|
|
418
|
+
await this.persistence.updateSchedule(schedule.id, {
|
|
419
|
+
lastRunAt: schedule.lastRunAt,
|
|
420
|
+
lastRunId: schedule.lastRunId,
|
|
421
|
+
nextRunAt: schedule.nextRunAt,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return runId;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Update the next run time for a cron schedule.
|
|
430
|
+
*/
|
|
431
|
+
private updateNextRunTime(schedule: WorkflowSchedule): void {
|
|
432
|
+
if (!schedule.cronExpression) return;
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
const interval = CronExpressionParser.parse(schedule.cronExpression, {
|
|
436
|
+
currentDate: new Date(),
|
|
437
|
+
tz: schedule.timezone,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
schedule.nextRunAt = interval.next().toDate();
|
|
441
|
+
} catch (error) {
|
|
442
|
+
this.logger.error(
|
|
443
|
+
`Failed to parse cron expression for schedule ${schedule.id}:`,
|
|
444
|
+
error,
|
|
445
|
+
);
|
|
446
|
+
schedule.nextRunAt = undefined;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler module exports.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from "./types";
|
|
6
|
+
export {
|
|
7
|
+
CronScheduler,
|
|
8
|
+
type CronSchedulerConfig,
|
|
9
|
+
type SchedulePersistence,
|
|
10
|
+
} from "./cron";
|
|
11
|
+
export {
|
|
12
|
+
SQLiteSchedulePersistence,
|
|
13
|
+
type SQLiteSchedulePersistenceConfig,
|
|
14
|
+
} from "./sqlite-persistence";
|
|
15
|
+
export {
|
|
16
|
+
PostgresSchedulePersistence,
|
|
17
|
+
type PostgresSchedulePersistenceConfig,
|
|
18
|
+
} from "./postgres-persistence";
|