@xtr-dev/payload-automation 0.0.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.
Files changed (98) hide show
  1. package/README.md +73 -0
  2. package/dist/collections/Workflow.d.ts +3 -0
  3. package/dist/collections/Workflow.js +223 -0
  4. package/dist/collections/Workflow.js.map +1 -0
  5. package/dist/collections/WorkflowRuns.d.ts +2 -0
  6. package/dist/collections/WorkflowRuns.js +157 -0
  7. package/dist/collections/WorkflowRuns.js.map +1 -0
  8. package/dist/components/TriggerWorkflowButton.d.ts +7 -0
  9. package/dist/components/TriggerWorkflowButton.js +46 -0
  10. package/dist/components/TriggerWorkflowButton.js.map +1 -0
  11. package/dist/core/trigger-custom-workflow.d.ts +52 -0
  12. package/dist/core/trigger-custom-workflow.js +205 -0
  13. package/dist/core/trigger-custom-workflow.js.map +1 -0
  14. package/dist/core/workflow-executor.d.ts +86 -0
  15. package/dist/core/workflow-executor.js +456 -0
  16. package/dist/core/workflow-executor.js.map +1 -0
  17. package/dist/exports/client.d.ts +1 -0
  18. package/dist/exports/client.js +7 -0
  19. package/dist/exports/client.js.map +1 -0
  20. package/dist/exports/fields.d.ts +1 -0
  21. package/dist/exports/fields.js +6 -0
  22. package/dist/exports/fields.js.map +1 -0
  23. package/dist/exports/rsc.d.ts +1 -0
  24. package/dist/exports/rsc.js +3 -0
  25. package/dist/exports/rsc.js.map +1 -0
  26. package/dist/exports/views.d.ts +1 -0
  27. package/dist/exports/views.js +7 -0
  28. package/dist/exports/views.js.map +1 -0
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.js +8 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/plugin/config-types.d.ts +21 -0
  33. package/dist/plugin/config-types.js +3 -0
  34. package/dist/plugin/config-types.js.map +1 -0
  35. package/dist/plugin/cron-scheduler.d.ts +32 -0
  36. package/dist/plugin/cron-scheduler.js +537 -0
  37. package/dist/plugin/cron-scheduler.js.map +1 -0
  38. package/dist/plugin/index.d.ts +4 -0
  39. package/dist/plugin/index.js +66 -0
  40. package/dist/plugin/index.js.map +1 -0
  41. package/dist/plugin/init-collection-hooks.d.ts +4 -0
  42. package/dist/plugin/init-collection-hooks.js +63 -0
  43. package/dist/plugin/init-collection-hooks.js.map +1 -0
  44. package/dist/plugin/init-global-hooks.d.ts +3 -0
  45. package/dist/plugin/init-global-hooks.js +83 -0
  46. package/dist/plugin/init-global-hooks.js.map +1 -0
  47. package/dist/plugin/init-step-tasks.d.ts +3 -0
  48. package/dist/plugin/init-step-tasks.js +8 -0
  49. package/dist/plugin/init-step-tasks.js.map +1 -0
  50. package/dist/plugin/init-webhook.d.ts +2 -0
  51. package/dist/plugin/init-webhook.js +154 -0
  52. package/dist/plugin/init-webhook.js.map +1 -0
  53. package/dist/plugin/init-workflow-hooks.d.ts +6 -0
  54. package/dist/plugin/init-workflow-hooks.js +46 -0
  55. package/dist/plugin/init-workflow-hooks.js.map +1 -0
  56. package/dist/plugin/logger.d.ts +20 -0
  57. package/dist/plugin/logger.js +47 -0
  58. package/dist/plugin/logger.js.map +1 -0
  59. package/dist/steps/create-document-handler.d.ts +2 -0
  60. package/dist/steps/create-document-handler.js +36 -0
  61. package/dist/steps/create-document-handler.js.map +1 -0
  62. package/dist/steps/create-document.d.ts +46 -0
  63. package/dist/steps/create-document.js +55 -0
  64. package/dist/steps/create-document.js.map +1 -0
  65. package/dist/steps/delete-document-handler.d.ts +2 -0
  66. package/dist/steps/delete-document-handler.js +62 -0
  67. package/dist/steps/delete-document-handler.js.map +1 -0
  68. package/dist/steps/delete-document.d.ts +39 -0
  69. package/dist/steps/delete-document.js +47 -0
  70. package/dist/steps/delete-document.js.map +1 -0
  71. package/dist/steps/http-request-handler.d.ts +2 -0
  72. package/dist/steps/http-request-handler.js +14 -0
  73. package/dist/steps/http-request-handler.js.map +1 -0
  74. package/dist/steps/http-request.d.ts +12 -0
  75. package/dist/steps/http-request.js +19 -0
  76. package/dist/steps/http-request.js.map +1 -0
  77. package/dist/steps/index.d.ts +12 -0
  78. package/dist/steps/index.js +14 -0
  79. package/dist/steps/index.js.map +1 -0
  80. package/dist/steps/read-document-handler.d.ts +2 -0
  81. package/dist/steps/read-document-handler.js +53 -0
  82. package/dist/steps/read-document-handler.js.map +1 -0
  83. package/dist/steps/read-document.d.ts +46 -0
  84. package/dist/steps/read-document.js +75 -0
  85. package/dist/steps/read-document.js.map +1 -0
  86. package/dist/steps/send-email-handler.d.ts +2 -0
  87. package/dist/steps/send-email-handler.js +48 -0
  88. package/dist/steps/send-email-handler.js.map +1 -0
  89. package/dist/steps/send-email.d.ts +44 -0
  90. package/dist/steps/send-email.js +78 -0
  91. package/dist/steps/send-email.js.map +1 -0
  92. package/dist/steps/update-document-handler.d.ts +2 -0
  93. package/dist/steps/update-document-handler.js +40 -0
  94. package/dist/steps/update-document-handler.js.map +1 -0
  95. package/dist/steps/update-document.d.ts +46 -0
  96. package/dist/steps/update-document.js +63 -0
  97. package/dist/steps/update-document.js.map +1 -0
  98. package/package.json +133 -0
@@ -0,0 +1,32 @@
1
+ import type { Config, Payload } from 'payload';
2
+ /**
3
+ * Generate dynamic cron tasks for all workflows with cron triggers
4
+ * This is called at config time to register all scheduled tasks
5
+ */
6
+ export declare function generateCronTasks(config: Config): void;
7
+ /**
8
+ * Register cron jobs for workflows with cron triggers
9
+ * This is called at runtime after PayloadCMS is initialized
10
+ */
11
+ export declare function registerCronJobs(payload: Payload, logger: Payload['logger']): Promise<void>;
12
+ /**
13
+ * Validate a cron expression
14
+ */
15
+ export declare function validateCronExpression(cronExpression: string): boolean;
16
+ /**
17
+ * Handle re-queueing of cron jobs after they execute
18
+ * This ensures the job runs again at the next scheduled time
19
+ */
20
+ export declare function requeueCronJob(workflowId: string, cronExpression: string, timezone: string | undefined, payload: Payload, logger: Payload['logger']): Promise<void>;
21
+ /**
22
+ * Register or update cron jobs for a specific workflow
23
+ */
24
+ export declare function updateWorkflowCronJobs(workflowId: string, payload: Payload, logger: Payload['logger']): Promise<void>;
25
+ /**
26
+ * Cancel all cron jobs for a specific workflow
27
+ */
28
+ export declare function cancelWorkflowCronJobs(workflowId: string, payload: Payload, logger: Payload['logger']): void;
29
+ /**
30
+ * Remove cron jobs for a deleted workflow
31
+ */
32
+ export declare function removeWorkflowCronJobs(workflowId: string, payload: Payload, logger: Payload['logger']): void;
@@ -0,0 +1,537 @@
1
+ import * as cron from 'node-cron';
2
+ import { WorkflowExecutor } from '../core/workflow-executor.js';
3
+ import { getConfigLogger } from './logger.js';
4
+ /**
5
+ * Generate dynamic cron tasks for all workflows with cron triggers
6
+ * This is called at config time to register all scheduled tasks
7
+ */ export function generateCronTasks(config) {
8
+ const logger = getConfigLogger();
9
+ // Note: We can't query the database at config time, so we'll need a different approach
10
+ // We'll create a single task that handles all cron-triggered workflows
11
+ const cronTask = {
12
+ slug: 'workflow-cron-executor',
13
+ handler: async ({ input, req })=>{
14
+ const { cronExpression, timezone, workflowId } = input;
15
+ const logger = req.payload.logger.child({
16
+ plugin: '@xtr-dev/payload-automation'
17
+ });
18
+ try {
19
+ // Get the workflow
20
+ const workflow = await req.payload.findByID({
21
+ id: workflowId,
22
+ collection: 'workflows',
23
+ depth: 2,
24
+ req
25
+ });
26
+ if (!workflow) {
27
+ throw new Error(`Workflow ${workflowId} not found`);
28
+ }
29
+ // Create execution context for cron trigger
30
+ const context = {
31
+ steps: {},
32
+ trigger: {
33
+ type: 'cron',
34
+ req,
35
+ triggeredAt: new Date().toISOString()
36
+ }
37
+ };
38
+ // Create executor
39
+ const executor = new WorkflowExecutor(req.payload, logger);
40
+ // Find the matching cron trigger and check its condition if present
41
+ const triggers = workflow.triggers;
42
+ const matchingTrigger = triggers?.find((trigger)=>trigger.type === 'cron-trigger' && trigger.cronExpression === cronExpression);
43
+ // Check trigger condition if present
44
+ if (matchingTrigger?.condition) {
45
+ const conditionMet = executor.evaluateCondition(matchingTrigger.condition, context);
46
+ if (!conditionMet) {
47
+ logger.info({
48
+ condition: matchingTrigger.condition,
49
+ cronExpression,
50
+ workflowId,
51
+ workflowName: workflow.name
52
+ }, 'Cron trigger condition not met, skipping workflow execution');
53
+ // Re-queue for next execution but don't run workflow
54
+ if (cronExpression) {
55
+ void requeueCronJob(workflowId, cronExpression, timezone, req.payload, logger);
56
+ }
57
+ return {
58
+ output: {
59
+ executedAt: new Date().toISOString(),
60
+ status: 'skipped',
61
+ reason: 'Condition not met',
62
+ workflowId
63
+ },
64
+ state: 'succeeded'
65
+ };
66
+ }
67
+ logger.info({
68
+ condition: matchingTrigger.condition,
69
+ cronExpression,
70
+ workflowId,
71
+ workflowName: workflow.name
72
+ }, 'Cron trigger condition met');
73
+ }
74
+ // Execute the workflow
75
+ await executor.execute(workflow, context, req);
76
+ // Re-queue the job for the next scheduled execution if cronExpression is provided
77
+ if (cronExpression) {
78
+ void requeueCronJob(workflowId, cronExpression, timezone, req.payload, logger);
79
+ }
80
+ return {
81
+ output: {
82
+ executedAt: new Date().toISOString(),
83
+ status: 'completed',
84
+ workflowId
85
+ },
86
+ state: 'succeeded'
87
+ };
88
+ } catch (error) {
89
+ logger.error({
90
+ error: error instanceof Error ? error.message : 'Unknown error',
91
+ workflowId
92
+ }, 'Cron job execution failed');
93
+ // Re-queue even on failure to ensure continuity (unless it's a validation error)
94
+ if (cronExpression && !(error instanceof Error && error.message.includes('Invalid cron'))) {
95
+ void requeueCronJob(workflowId, cronExpression, timezone, req.payload, logger).catch((requeueError)=>{
96
+ logger.error({
97
+ error: requeueError instanceof Error ? requeueError.message : 'Unknown error',
98
+ workflowId
99
+ }, 'Failed to re-queue cron job after execution failure');
100
+ });
101
+ }
102
+ return {
103
+ output: {
104
+ error: error instanceof Error ? error.message : 'Unknown error',
105
+ workflowId
106
+ },
107
+ state: 'failed'
108
+ };
109
+ }
110
+ }
111
+ };
112
+ // Add the cron task to config if not already present
113
+ if (!config.jobs) {
114
+ config.jobs = {
115
+ tasks: []
116
+ };
117
+ }
118
+ if (!config.jobs.tasks) {
119
+ config.jobs.tasks = [];
120
+ }
121
+ if (!config.jobs.tasks.find((task)=>task.slug === cronTask.slug)) {
122
+ logger.debug(`Registering cron executor task: ${cronTask.slug}`);
123
+ config.jobs.tasks.push(cronTask);
124
+ } else {
125
+ logger.debug(`Cron executor task ${cronTask.slug} already registered, skipping`);
126
+ }
127
+ }
128
+ /**
129
+ * Register cron jobs for workflows with cron triggers
130
+ * This is called at runtime after PayloadCMS is initialized
131
+ */ export async function registerCronJobs(payload, logger) {
132
+ try {
133
+ // Find all workflows with cron triggers
134
+ const workflows = await payload.find({
135
+ collection: 'workflows',
136
+ depth: 0,
137
+ limit: 1000,
138
+ where: {
139
+ 'triggers.type': {
140
+ equals: 'cron-trigger'
141
+ }
142
+ }
143
+ });
144
+ logger.info(`Found ${workflows.docs.length} workflows with cron triggers`);
145
+ for (const workflow of workflows.docs){
146
+ const triggers = workflow.triggers;
147
+ // Find all cron triggers for this workflow
148
+ const cronTriggers = triggers?.filter((t)=>t.type === 'cron-trigger') || [];
149
+ for (const trigger of cronTriggers){
150
+ if (trigger.cronExpression) {
151
+ try {
152
+ // Validate cron expression before queueing
153
+ if (!validateCronExpression(trigger.cronExpression)) {
154
+ logger.error({
155
+ cronExpression: trigger.cronExpression,
156
+ workflowId: workflow.id,
157
+ workflowName: workflow.name
158
+ }, 'Invalid cron expression format');
159
+ continue;
160
+ }
161
+ // Validate timezone if provided
162
+ if (trigger.timezone) {
163
+ try {
164
+ // Test if timezone is valid by trying to create a date with it
165
+ new Intl.DateTimeFormat('en', {
166
+ timeZone: trigger.timezone
167
+ });
168
+ } catch {
169
+ logger.error({
170
+ timezone: trigger.timezone,
171
+ workflowId: workflow.id,
172
+ workflowName: workflow.name
173
+ }, 'Invalid timezone specified');
174
+ continue;
175
+ }
176
+ }
177
+ // Calculate next execution time
178
+ const nextExecution = getNextCronTime(trigger.cronExpression, trigger.timezone);
179
+ // Queue the job
180
+ await payload.jobs.queue({
181
+ input: {
182
+ cronExpression: trigger.cronExpression,
183
+ timezone: trigger.timezone,
184
+ workflowId: workflow.id
185
+ },
186
+ task: 'workflow-cron-executor',
187
+ waitUntil: nextExecution
188
+ });
189
+ logger.info({
190
+ cronExpression: trigger.cronExpression,
191
+ nextExecution: nextExecution.toISOString(),
192
+ timezone: trigger.timezone || 'UTC',
193
+ workflowId: workflow.id,
194
+ workflowName: workflow.name
195
+ }, 'Queued initial cron job for workflow');
196
+ } catch (error) {
197
+ logger.error({
198
+ cronExpression: trigger.cronExpression,
199
+ error: error instanceof Error ? error.message : 'Unknown error',
200
+ timezone: trigger.timezone,
201
+ workflowId: workflow.id,
202
+ workflowName: workflow.name
203
+ }, 'Failed to queue cron job');
204
+ }
205
+ } else {
206
+ logger.warn({
207
+ workflowId: workflow.id,
208
+ workflowName: workflow.name
209
+ }, 'Cron trigger found but no cron expression specified');
210
+ }
211
+ }
212
+ }
213
+ } catch (error) {
214
+ logger.error({
215
+ error: error instanceof Error ? error.message : 'Unknown error'
216
+ }, 'Failed to register cron jobs');
217
+ }
218
+ }
219
+ /**
220
+ * Validate a cron expression
221
+ */ export function validateCronExpression(cronExpression) {
222
+ return cron.validate(cronExpression);
223
+ }
224
+ /**
225
+ * Calculate the next time a cron expression should run
226
+ */ function getNextCronTime(cronExpression, timezone) {
227
+ if (!validateCronExpression(cronExpression)) {
228
+ throw new Error(`Invalid cron expression: ${cronExpression}`);
229
+ }
230
+ const now = new Date();
231
+ const options = timezone ? {
232
+ timezone
233
+ } : {};
234
+ // Create a task to find the next execution time
235
+ const task = cron.schedule(cronExpression, ()=>{}, {
236
+ ...options
237
+ });
238
+ // Parse cron expression parts
239
+ const cronParts = cronExpression.trim().split(/\s+/);
240
+ if (cronParts.length !== 5) {
241
+ void task.destroy();
242
+ throw new Error(`Invalid cron format: ${cronExpression}. Expected 5 parts.`);
243
+ }
244
+ const [minutePart, hourPart, dayPart, monthPart, weekdayPart] = cronParts;
245
+ // Calculate next execution with proper lookahead for any schedule frequency
246
+ // Start from next minute and look ahead systematically
247
+ let testTime = new Date(now.getTime() + 60 * 1000) // Start 1 minute from now
248
+ ;
249
+ testTime.setSeconds(0, 0); // Reset seconds and milliseconds
250
+ // Maximum iterations to prevent infinite loops (covers ~2 years)
251
+ const maxIterations = 2 * 365 * 24 * 60 // 2 years worth of minutes
252
+ ;
253
+ let iterations = 0;
254
+ while(iterations < maxIterations){
255
+ const minute = testTime.getMinutes();
256
+ const hour = testTime.getHours();
257
+ const dayOfMonth = testTime.getDate();
258
+ const month = testTime.getMonth() + 1;
259
+ const dayOfWeek = testTime.getDay();
260
+ if (matchesCronPart(minute, minutePart) && matchesCronPart(hour, hourPart) && matchesCronPart(dayOfMonth, dayPart) && matchesCronPart(month, monthPart) && matchesCronPart(dayOfWeek, weekdayPart)) {
261
+ void task.destroy();
262
+ return testTime;
263
+ }
264
+ // Increment time intelligently based on cron pattern
265
+ testTime = incrementTimeForCronPattern(testTime, cronParts);
266
+ iterations++;
267
+ }
268
+ void task.destroy();
269
+ throw new Error(`Could not calculate next execution time for cron expression: ${cronExpression} within reasonable timeframe`);
270
+ }
271
+ /**
272
+ * Intelligently increment time based on cron pattern to avoid unnecessary iterations
273
+ */ function incrementTimeForCronPattern(currentTime, cronParts) {
274
+ const [minutePart, hourPart, _dayPart, _monthPart, _weekdayPart] = cronParts;
275
+ const nextTime = new Date(currentTime);
276
+ // If minute is specific (not wildcard), we can jump to next hour
277
+ if (minutePart !== '*' && !minutePart.includes('/')) {
278
+ const targetMinute = getNextValidCronValue(currentTime.getMinutes(), minutePart);
279
+ if (targetMinute <= currentTime.getMinutes()) {
280
+ // Move to next hour
281
+ nextTime.setHours(nextTime.getHours() + 1, targetMinute, 0, 0);
282
+ } else {
283
+ nextTime.setMinutes(targetMinute, 0, 0);
284
+ }
285
+ return nextTime;
286
+ }
287
+ // If hour is specific and we're past it, jump to next day
288
+ if (hourPart !== '*' && !hourPart.includes('/')) {
289
+ const targetHour = getNextValidCronValue(currentTime.getHours(), hourPart);
290
+ if (targetHour <= currentTime.getHours()) {
291
+ // Move to next day
292
+ nextTime.setDate(nextTime.getDate() + 1);
293
+ nextTime.setHours(targetHour, 0, 0, 0);
294
+ } else {
295
+ nextTime.setHours(targetHour, 0, 0, 0);
296
+ }
297
+ return nextTime;
298
+ }
299
+ // Default: increment by 1 minute
300
+ nextTime.setTime(nextTime.getTime() + 60 * 1000);
301
+ return nextTime;
302
+ }
303
+ /**
304
+ * Get the next valid value for a cron part
305
+ */ function getNextValidCronValue(currentValue, cronPart) {
306
+ if (cronPart === '*') {
307
+ return currentValue + 1;
308
+ }
309
+ // Handle specific values and ranges
310
+ const values = parseCronPart(cronPart);
311
+ return values.find((v)=>v > currentValue) || values[0];
312
+ }
313
+ /**
314
+ * Parse a cron part into an array of valid values
315
+ */ function parseCronPart(cronPart) {
316
+ if (cronPart === '*') {
317
+ return [];
318
+ }
319
+ const values = [];
320
+ // Handle comma-separated values
321
+ if (cronPart.includes(',')) {
322
+ cronPart.split(',').forEach((part)=>{
323
+ values.push(...parseCronPart(part.trim()));
324
+ });
325
+ return values.sort((a, b)=>a - b);
326
+ }
327
+ // Handle ranges
328
+ if (cronPart.includes('-')) {
329
+ const [start, end] = cronPart.split('-').map((n)=>parseInt(n, 10));
330
+ for(let i = start; i <= end; i++){
331
+ values.push(i);
332
+ }
333
+ return values;
334
+ }
335
+ // Handle step values
336
+ if (cronPart.includes('/')) {
337
+ const [range, step] = cronPart.split('/');
338
+ const stepNum = parseInt(step, 10);
339
+ if (range === '*') {
340
+ // For wildcards with steps, return empty - handled elsewhere
341
+ return [];
342
+ }
343
+ const baseValues = parseCronPart(range);
344
+ return baseValues.filter((_, index)=>index % stepNum === 0);
345
+ }
346
+ // Single value
347
+ values.push(parseInt(cronPart, 10));
348
+ return values;
349
+ }
350
+ /**
351
+ * Check if a value matches a cron expression part
352
+ */ function matchesCronPart(value, cronPart) {
353
+ if (cronPart === '*') {
354
+ return true;
355
+ }
356
+ // Handle step values (e.g., */5)
357
+ if (cronPart.includes('/')) {
358
+ const [range, step] = cronPart.split('/');
359
+ const stepNum = parseInt(step, 10);
360
+ if (range === '*') {
361
+ return value % stepNum === 0;
362
+ }
363
+ }
364
+ // Handle ranges (e.g., 1-5)
365
+ if (cronPart.includes('-')) {
366
+ const [start, end] = cronPart.split('-').map((n)=>parseInt(n, 10));
367
+ return value >= start && value <= end;
368
+ }
369
+ // Handle comma-separated values (e.g., 1,3,5)
370
+ if (cronPart.includes(',')) {
371
+ const values = cronPart.split(',').map((n)=>parseInt(n, 10));
372
+ return values.includes(value);
373
+ }
374
+ // Handle single value
375
+ const cronValue = parseInt(cronPart, 10);
376
+ return value === cronValue;
377
+ }
378
+ /**
379
+ * Handle re-queueing of cron jobs after they execute
380
+ * This ensures the job runs again at the next scheduled time
381
+ */ export async function requeueCronJob(workflowId, cronExpression, timezone, payload, logger) {
382
+ try {
383
+ // Queue the job to run at the next scheduled time
384
+ await payload.jobs.queue({
385
+ input: {
386
+ cronExpression,
387
+ timezone,
388
+ workflowId
389
+ },
390
+ task: 'workflow-cron-executor',
391
+ waitUntil: getNextCronTime(cronExpression, timezone)
392
+ });
393
+ logger.debug({
394
+ nextRun: getNextCronTime(cronExpression, timezone),
395
+ timezone: timezone || 'UTC',
396
+ workflowId
397
+ }, 'Re-queued cron job');
398
+ } catch (error) {
399
+ logger.error({
400
+ error: error instanceof Error ? error.message : 'Unknown error',
401
+ workflowId
402
+ }, 'Failed to re-queue cron job');
403
+ }
404
+ }
405
+ /**
406
+ * Register or update cron jobs for a specific workflow
407
+ */ export async function updateWorkflowCronJobs(workflowId, payload, logger) {
408
+ try {
409
+ // First, cancel any existing cron jobs for this workflow
410
+ cancelWorkflowCronJobs(workflowId, payload, logger);
411
+ // Get the workflow
412
+ const workflow = await payload.findByID({
413
+ id: workflowId,
414
+ collection: 'workflows',
415
+ depth: 0
416
+ });
417
+ if (!workflow) {
418
+ logger.warn({
419
+ workflowId
420
+ }, 'Workflow not found for cron job update');
421
+ return;
422
+ }
423
+ const triggers = workflow.triggers;
424
+ // Find all cron triggers for this workflow
425
+ const cronTriggers = triggers?.filter((t)=>t.type === 'cron-trigger') || [];
426
+ if (cronTriggers.length === 0) {
427
+ logger.debug({
428
+ workflowId
429
+ }, 'No cron triggers found for workflow');
430
+ return;
431
+ }
432
+ let scheduledJobs = 0;
433
+ for (const trigger of cronTriggers){
434
+ if (trigger.cronExpression) {
435
+ try {
436
+ // Validate cron expression before queueing
437
+ if (!validateCronExpression(trigger.cronExpression)) {
438
+ logger.error({
439
+ cronExpression: trigger.cronExpression,
440
+ workflowId,
441
+ workflowName: workflow.name
442
+ }, 'Invalid cron expression format');
443
+ continue;
444
+ }
445
+ // Validate timezone if provided
446
+ if (trigger.timezone) {
447
+ try {
448
+ new Intl.DateTimeFormat('en', {
449
+ timeZone: trigger.timezone
450
+ });
451
+ } catch {
452
+ logger.error({
453
+ timezone: trigger.timezone,
454
+ workflowId,
455
+ workflowName: workflow.name
456
+ }, 'Invalid timezone specified');
457
+ continue;
458
+ }
459
+ }
460
+ // Calculate next execution time
461
+ const nextExecution = getNextCronTime(trigger.cronExpression, trigger.timezone);
462
+ // Queue the job
463
+ await payload.jobs.queue({
464
+ input: {
465
+ cronExpression: trigger.cronExpression,
466
+ timezone: trigger.timezone,
467
+ workflowId
468
+ },
469
+ task: 'workflow-cron-executor',
470
+ waitUntil: nextExecution
471
+ });
472
+ scheduledJobs++;
473
+ logger.info({
474
+ cronExpression: trigger.cronExpression,
475
+ nextExecution: nextExecution.toISOString(),
476
+ timezone: trigger.timezone || 'UTC',
477
+ workflowId,
478
+ workflowName: workflow.name
479
+ }, 'Scheduled cron job for workflow');
480
+ } catch (error) {
481
+ logger.error({
482
+ cronExpression: trigger.cronExpression,
483
+ error: error instanceof Error ? error.message : 'Unknown error',
484
+ timezone: trigger.timezone,
485
+ workflowId,
486
+ workflowName: workflow.name
487
+ }, 'Failed to schedule cron job');
488
+ }
489
+ }
490
+ }
491
+ if (scheduledJobs > 0) {
492
+ logger.info({
493
+ scheduledJobs,
494
+ workflowId
495
+ }, 'Updated cron jobs for workflow');
496
+ }
497
+ } catch (error) {
498
+ logger.error({
499
+ error: error instanceof Error ? error.message : 'Unknown error',
500
+ workflowId
501
+ }, 'Failed to update workflow cron jobs');
502
+ }
503
+ }
504
+ /**
505
+ * Cancel all cron jobs for a specific workflow
506
+ */ export function cancelWorkflowCronJobs(workflowId, payload, logger) {
507
+ try {
508
+ // Note: PayloadCMS job system doesn't have a built-in way to cancel specific jobs by input
509
+ // This is a limitation we need to work around
510
+ // For now, we log that we would cancel jobs for this workflow
511
+ logger.debug({
512
+ workflowId
513
+ }, 'Would cancel existing cron jobs for workflow (PayloadCMS limitation: cannot selectively cancel jobs)');
514
+ } catch (error) {
515
+ logger.error({
516
+ error: error instanceof Error ? error.message : 'Unknown error',
517
+ workflowId
518
+ }, 'Failed to cancel workflow cron jobs');
519
+ }
520
+ }
521
+ /**
522
+ * Remove cron jobs for a deleted workflow
523
+ */ export function removeWorkflowCronJobs(workflowId, payload, logger) {
524
+ try {
525
+ cancelWorkflowCronJobs(workflowId, payload, logger);
526
+ logger.info({
527
+ workflowId
528
+ }, 'Removed cron jobs for deleted workflow');
529
+ } catch (error) {
530
+ logger.error({
531
+ error: error instanceof Error ? error.message : 'Unknown error',
532
+ workflowId
533
+ }, 'Failed to remove workflow cron jobs');
534
+ }
535
+ }
536
+
537
+ //# sourceMappingURL=cron-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugin/cron-scheduler.ts"],"sourcesContent":["import type {Config, Payload, TaskConfig} from 'payload'\nimport * as cron from 'node-cron'\n\nimport {type Workflow, WorkflowExecutor} from '../core/workflow-executor.js'\nimport {getConfigLogger} from './logger.js'\n\n/**\n * Generate dynamic cron tasks for all workflows with cron triggers\n * This is called at config time to register all scheduled tasks\n */\nexport function generateCronTasks(config: Config): void {\n const logger = getConfigLogger()\n \n // Note: We can't query the database at config time, so we'll need a different approach\n // We'll create a single task that handles all cron-triggered workflows\n const cronTask: TaskConfig = {\n slug: 'workflow-cron-executor',\n handler: async ({ input, req }) => {\n const { cronExpression, timezone, workflowId } = input as { \n cronExpression?: string\n timezone?: string\n workflowId: string\n }\n \n const logger = req.payload.logger.child({ plugin: '@xtr-dev/payload-automation' })\n \n try {\n // Get the workflow\n const workflow = await req.payload.findByID({\n id: workflowId,\n collection: 'workflows',\n depth: 2,\n req\n })\n \n if (!workflow) {\n throw new Error(`Workflow ${workflowId} not found`)\n }\n \n // Create execution context for cron trigger\n const context = {\n steps: {},\n trigger: {\n type: 'cron',\n req,\n triggeredAt: new Date().toISOString()\n }\n }\n \n // Create executor\n const executor = new WorkflowExecutor(req.payload, logger)\n \n // Find the matching cron trigger and check its condition if present\n const triggers = workflow.triggers as Array<{\n condition?: string\n cronExpression?: string\n timezone?: string\n type: string\n }>\n\n const matchingTrigger = triggers?.find(trigger =>\n trigger.type === 'cron-trigger' &&\n trigger.cronExpression === cronExpression\n )\n\n // Check trigger condition if present\n if (matchingTrigger?.condition) {\n const conditionMet = executor.evaluateCondition(matchingTrigger.condition, context)\n \n if (!conditionMet) {\n logger.info({\n condition: matchingTrigger.condition,\n cronExpression,\n workflowId,\n workflowName: workflow.name\n }, 'Cron trigger condition not met, skipping workflow execution')\n \n // Re-queue for next execution but don't run workflow\n if (cronExpression) {\n void requeueCronJob(workflowId, cronExpression, timezone, req.payload, logger)\n }\n \n return {\n output: {\n executedAt: new Date().toISOString(),\n status: 'skipped',\n reason: 'Condition not met',\n workflowId\n },\n state: 'succeeded'\n }\n }\n \n logger.info({\n condition: matchingTrigger.condition,\n cronExpression,\n workflowId,\n workflowName: workflow.name\n }, 'Cron trigger condition met')\n }\n \n // Execute the workflow\n await executor.execute(workflow as Workflow, context, req)\n \n // Re-queue the job for the next scheduled execution if cronExpression is provided\n if (cronExpression) {\n void requeueCronJob(workflowId, cronExpression, timezone, req.payload, logger)\n }\n \n return {\n output: {\n executedAt: new Date().toISOString(),\n status: 'completed',\n workflowId\n },\n state: 'succeeded'\n }\n } catch (error) {\n logger.error({\n error: error instanceof Error ? error.message : 'Unknown error',\n workflowId\n }, 'Cron job execution failed')\n \n // Re-queue even on failure to ensure continuity (unless it's a validation error)\n if (cronExpression && !(error instanceof Error && error.message.includes('Invalid cron'))) {\n void requeueCronJob(workflowId, cronExpression, timezone, req.payload, logger)\n .catch((requeueError) => {\n logger.error({\n error: requeueError instanceof Error ? requeueError.message : 'Unknown error',\n workflowId\n }, 'Failed to re-queue cron job after execution failure')\n })\n }\n \n return {\n output: {\n error: error instanceof Error ? error.message : 'Unknown error',\n workflowId\n },\n state: 'failed'\n }\n }\n }\n }\n \n // Add the cron task to config if not already present\n if (!config.jobs) {\n config.jobs = { tasks: [] }\n }\n \n if (!config.jobs.tasks) {\n config.jobs.tasks = []\n }\n \n if (!config.jobs.tasks.find(task => task.slug === cronTask.slug)) {\n logger.debug(`Registering cron executor task: ${cronTask.slug}`)\n config.jobs.tasks.push(cronTask)\n } else {\n logger.debug(`Cron executor task ${cronTask.slug} already registered, skipping`)\n }\n}\n\n/**\n * Register cron jobs for workflows with cron triggers\n * This is called at runtime after PayloadCMS is initialized\n */\nexport async function registerCronJobs(payload: Payload, logger: Payload['logger']): Promise<void> {\n try {\n // Find all workflows with cron triggers\n const workflows = await payload.find({\n collection: 'workflows',\n depth: 0,\n limit: 1000,\n where: {\n 'triggers.type': {\n equals: 'cron-trigger'\n }\n }\n })\n \n logger.info(`Found ${workflows.docs.length} workflows with cron triggers`)\n \n for (const workflow of workflows.docs) {\n const triggers = workflow.triggers as Array<{\n cronExpression?: string\n timezone?: string\n type: string\n }>\n \n // Find all cron triggers for this workflow\n const cronTriggers = triggers?.filter(t => t.type === 'cron-trigger') || []\n \n for (const trigger of cronTriggers) {\n if (trigger.cronExpression) {\n try {\n // Validate cron expression before queueing\n if (!validateCronExpression(trigger.cronExpression)) {\n logger.error({\n cronExpression: trigger.cronExpression,\n workflowId: workflow.id,\n workflowName: workflow.name\n }, 'Invalid cron expression format')\n continue\n }\n \n // Validate timezone if provided\n if (trigger.timezone) {\n try {\n // Test if timezone is valid by trying to create a date with it\n new Intl.DateTimeFormat('en', { timeZone: trigger.timezone })\n } catch {\n logger.error({\n timezone: trigger.timezone,\n workflowId: workflow.id,\n workflowName: workflow.name\n }, 'Invalid timezone specified')\n continue\n }\n }\n \n // Calculate next execution time\n const nextExecution = getNextCronTime(trigger.cronExpression, trigger.timezone)\n \n // Queue the job\n await payload.jobs.queue({\n input: { cronExpression: trigger.cronExpression, timezone: trigger.timezone, workflowId: workflow.id },\n task: 'workflow-cron-executor',\n waitUntil: nextExecution\n })\n \n logger.info({\n cronExpression: trigger.cronExpression,\n nextExecution: nextExecution.toISOString(),\n timezone: trigger.timezone || 'UTC',\n workflowId: workflow.id,\n workflowName: workflow.name\n }, 'Queued initial cron job for workflow')\n } catch (error) {\n logger.error({\n cronExpression: trigger.cronExpression,\n error: error instanceof Error ? error.message : 'Unknown error',\n timezone: trigger.timezone,\n workflowId: workflow.id,\n workflowName: workflow.name\n }, 'Failed to queue cron job')\n }\n } else {\n logger.warn({\n workflowId: workflow.id,\n workflowName: workflow.name\n }, 'Cron trigger found but no cron expression specified')\n }\n }\n }\n } catch (error) {\n logger.error({\n error: error instanceof Error ? error.message : 'Unknown error'\n }, 'Failed to register cron jobs')\n }\n}\n\n/**\n * Validate a cron expression\n */\nexport function validateCronExpression(cronExpression: string): boolean {\n return cron.validate(cronExpression)\n}\n\n/**\n * Calculate the next time a cron expression should run\n */\nfunction getNextCronTime(cronExpression: string, timezone?: string): Date {\n if (!validateCronExpression(cronExpression)) {\n throw new Error(`Invalid cron expression: ${cronExpression}`)\n }\n\n const now = new Date()\n const options: { timezone?: string } = timezone ? { timezone } : {}\n \n // Create a task to find the next execution time\n const task = cron.schedule(cronExpression, () => {}, {\n ...options\n })\n \n // Parse cron expression parts\n const cronParts = cronExpression.trim().split(/\\s+/)\n if (cronParts.length !== 5) {\n void task.destroy()\n throw new Error(`Invalid cron format: ${cronExpression}. Expected 5 parts.`)\n }\n \n const [minutePart, hourPart, dayPart, monthPart, weekdayPart] = cronParts\n \n // Calculate next execution with proper lookahead for any schedule frequency\n // Start from next minute and look ahead systematically\n let testTime = new Date(now.getTime() + 60 * 1000) // Start 1 minute from now\n testTime.setSeconds(0, 0) // Reset seconds and milliseconds\n \n // Maximum iterations to prevent infinite loops (covers ~2 years)\n const maxIterations = 2 * 365 * 24 * 60 // 2 years worth of minutes\n let iterations = 0\n \n while (iterations < maxIterations) {\n const minute = testTime.getMinutes()\n const hour = testTime.getHours()\n const dayOfMonth = testTime.getDate()\n const month = testTime.getMonth() + 1\n const dayOfWeek = testTime.getDay()\n \n if (matchesCronPart(minute, minutePart) &&\n matchesCronPart(hour, hourPart) &&\n matchesCronPart(dayOfMonth, dayPart) &&\n matchesCronPart(month, monthPart) &&\n matchesCronPart(dayOfWeek, weekdayPart)) {\n void task.destroy()\n return testTime\n }\n \n // Increment time intelligently based on cron pattern\n testTime = incrementTimeForCronPattern(testTime, cronParts)\n iterations++\n }\n \n void task.destroy()\n throw new Error(`Could not calculate next execution time for cron expression: ${cronExpression} within reasonable timeframe`)\n}\n\n/**\n * Intelligently increment time based on cron pattern to avoid unnecessary iterations\n */\nfunction incrementTimeForCronPattern(currentTime: Date, cronParts: string[]): Date {\n const [minutePart, hourPart, _dayPart, _monthPart, _weekdayPart] = cronParts\n const nextTime = new Date(currentTime)\n \n // If minute is specific (not wildcard), we can jump to next hour\n if (minutePart !== '*' && !minutePart.includes('/')) {\n const targetMinute = getNextValidCronValue(currentTime.getMinutes(), minutePart)\n if (targetMinute <= currentTime.getMinutes()) {\n // Move to next hour\n nextTime.setHours(nextTime.getHours() + 1, targetMinute, 0, 0)\n } else {\n nextTime.setMinutes(targetMinute, 0, 0)\n }\n return nextTime\n }\n \n // If hour is specific and we're past it, jump to next day\n if (hourPart !== '*' && !hourPart.includes('/')) {\n const targetHour = getNextValidCronValue(currentTime.getHours(), hourPart)\n if (targetHour <= currentTime.getHours()) {\n // Move to next day\n nextTime.setDate(nextTime.getDate() + 1)\n nextTime.setHours(targetHour, 0, 0, 0)\n } else {\n nextTime.setHours(targetHour, 0, 0, 0)\n }\n return nextTime\n }\n \n // Default: increment by 1 minute\n nextTime.setTime(nextTime.getTime() + 60 * 1000)\n return nextTime\n}\n\n/**\n * Get the next valid value for a cron part\n */\nfunction getNextValidCronValue(currentValue: number, cronPart: string): number {\n if (cronPart === '*') {return currentValue + 1}\n \n // Handle specific values and ranges\n const values = parseCronPart(cronPart)\n return values.find(v => v > currentValue) || values[0]\n}\n\n/**\n * Parse a cron part into an array of valid values\n */\nfunction parseCronPart(cronPart: string): number[] {\n if (cronPart === '*') {return []}\n \n const values: number[] = []\n \n // Handle comma-separated values\n if (cronPart.includes(',')) {\n cronPart.split(',').forEach(part => {\n values.push(...parseCronPart(part.trim()))\n })\n return values.sort((a, b) => a - b)\n }\n \n // Handle ranges\n if (cronPart.includes('-')) {\n const [start, end] = cronPart.split('-').map(n => parseInt(n, 10))\n for (let i = start; i <= end; i++) {\n values.push(i)\n }\n return values\n }\n \n // Handle step values\n if (cronPart.includes('/')) {\n const [range, step] = cronPart.split('/')\n const stepNum = parseInt(step, 10)\n \n if (range === '*') {\n // For wildcards with steps, return empty - handled elsewhere\n return []\n }\n \n const baseValues = parseCronPart(range)\n return baseValues.filter((_, index) => index % stepNum === 0)\n }\n \n // Single value\n values.push(parseInt(cronPart, 10))\n return values\n}\n\n/**\n * Check if a value matches a cron expression part\n */\nfunction matchesCronPart(value: number, cronPart: string): boolean {\n if (cronPart === '*') {return true}\n \n // Handle step values (e.g., */5)\n if (cronPart.includes('/')) {\n const [range, step] = cronPart.split('/')\n const stepNum = parseInt(step, 10)\n \n if (range === '*') {\n return value % stepNum === 0\n }\n }\n \n // Handle ranges (e.g., 1-5)\n if (cronPart.includes('-')) {\n const [start, end] = cronPart.split('-').map(n => parseInt(n, 10))\n return value >= start && value <= end\n }\n \n // Handle comma-separated values (e.g., 1,3,5)\n if (cronPart.includes(',')) {\n const values = cronPart.split(',').map(n => parseInt(n, 10))\n return values.includes(value)\n }\n \n // Handle single value\n const cronValue = parseInt(cronPart, 10)\n return value === cronValue\n}\n\n/**\n * Handle re-queueing of cron jobs after they execute\n * This ensures the job runs again at the next scheduled time\n */\nexport async function requeueCronJob(\n workflowId: string,\n cronExpression: string,\n timezone: string | undefined,\n payload: Payload,\n logger: Payload['logger']\n): Promise<void> {\n try {\n // Queue the job to run at the next scheduled time\n await payload.jobs.queue({\n input: { cronExpression, timezone, workflowId },\n task: 'workflow-cron-executor',\n waitUntil: getNextCronTime(cronExpression, timezone)\n })\n \n logger.debug({\n nextRun: getNextCronTime(cronExpression, timezone),\n timezone: timezone || 'UTC',\n workflowId\n }, 'Re-queued cron job')\n } catch (error) {\n logger.error({\n error: error instanceof Error ? error.message : 'Unknown error',\n workflowId\n }, 'Failed to re-queue cron job')\n }\n}\n\n/**\n * Register or update cron jobs for a specific workflow\n */\nexport async function updateWorkflowCronJobs(\n workflowId: string,\n payload: Payload, \n logger: Payload['logger']\n): Promise<void> {\n try {\n // First, cancel any existing cron jobs for this workflow\n cancelWorkflowCronJobs(workflowId, payload, logger)\n \n // Get the workflow\n const workflow = await payload.findByID({\n id: workflowId,\n collection: 'workflows',\n depth: 0\n })\n \n if (!workflow) {\n logger.warn({ workflowId }, 'Workflow not found for cron job update')\n return\n }\n \n const triggers = workflow.triggers as Array<{\n cronExpression?: string\n timezone?: string\n type: string\n }>\n \n // Find all cron triggers for this workflow\n const cronTriggers = triggers?.filter(t => t.type === 'cron-trigger') || []\n \n if (cronTriggers.length === 0) {\n logger.debug({ workflowId }, 'No cron triggers found for workflow')\n return\n }\n \n let scheduledJobs = 0\n \n for (const trigger of cronTriggers) {\n if (trigger.cronExpression) {\n try {\n // Validate cron expression before queueing\n if (!validateCronExpression(trigger.cronExpression)) {\n logger.error({\n cronExpression: trigger.cronExpression,\n workflowId,\n workflowName: workflow.name\n }, 'Invalid cron expression format')\n continue\n }\n \n // Validate timezone if provided\n if (trigger.timezone) {\n try {\n new Intl.DateTimeFormat('en', { timeZone: trigger.timezone })\n } catch {\n logger.error({\n timezone: trigger.timezone,\n workflowId,\n workflowName: workflow.name\n }, 'Invalid timezone specified')\n continue\n }\n }\n \n // Calculate next execution time\n const nextExecution = getNextCronTime(trigger.cronExpression, trigger.timezone)\n \n // Queue the job\n await payload.jobs.queue({\n input: { cronExpression: trigger.cronExpression, timezone: trigger.timezone, workflowId },\n task: 'workflow-cron-executor',\n waitUntil: nextExecution\n })\n \n scheduledJobs++\n \n logger.info({\n cronExpression: trigger.cronExpression,\n nextExecution: nextExecution.toISOString(),\n timezone: trigger.timezone || 'UTC',\n workflowId,\n workflowName: workflow.name\n }, 'Scheduled cron job for workflow')\n } catch (error) {\n logger.error({\n cronExpression: trigger.cronExpression,\n error: error instanceof Error ? error.message : 'Unknown error',\n timezone: trigger.timezone,\n workflowId,\n workflowName: workflow.name\n }, 'Failed to schedule cron job')\n }\n }\n }\n \n if (scheduledJobs > 0) {\n logger.info({ scheduledJobs, workflowId }, 'Updated cron jobs for workflow')\n }\n } catch (error) {\n logger.error({\n error: error instanceof Error ? error.message : 'Unknown error',\n workflowId\n }, 'Failed to update workflow cron jobs')\n }\n}\n\n/**\n * Cancel all cron jobs for a specific workflow\n */\nexport function cancelWorkflowCronJobs(\n workflowId: string,\n payload: Payload,\n logger: Payload['logger']\n): void {\n try {\n // Note: PayloadCMS job system doesn't have a built-in way to cancel specific jobs by input\n // This is a limitation we need to work around\n // For now, we log that we would cancel jobs for this workflow\n logger.debug({ workflowId }, 'Would cancel existing cron jobs for workflow (PayloadCMS limitation: cannot selectively cancel jobs)')\n } catch (error) {\n logger.error({\n error: error instanceof Error ? error.message : 'Unknown error',\n workflowId\n }, 'Failed to cancel workflow cron jobs')\n }\n}\n\n/**\n * Remove cron jobs for a deleted workflow\n */\nexport function removeWorkflowCronJobs(\n workflowId: string,\n payload: Payload,\n logger: Payload['logger']\n): void {\n try {\n cancelWorkflowCronJobs(workflowId, payload, logger)\n logger.info({ workflowId }, 'Removed cron jobs for deleted workflow')\n } catch (error) {\n logger.error({\n error: error instanceof Error ? error.message : 'Unknown error',\n workflowId\n }, 'Failed to remove workflow cron jobs')\n }\n}\n"],"names":["cron","WorkflowExecutor","getConfigLogger","generateCronTasks","config","logger","cronTask","slug","handler","input","req","cronExpression","timezone","workflowId","payload","child","plugin","workflow","findByID","id","collection","depth","Error","context","steps","trigger","type","triggeredAt","Date","toISOString","executor","triggers","matchingTrigger","find","condition","conditionMet","evaluateCondition","info","workflowName","name","requeueCronJob","output","executedAt","status","reason","state","execute","error","message","includes","catch","requeueError","jobs","tasks","task","debug","push","registerCronJobs","workflows","limit","where","equals","docs","length","cronTriggers","filter","t","validateCronExpression","Intl","DateTimeFormat","timeZone","nextExecution","getNextCronTime","queue","waitUntil","warn","validate","now","options","schedule","cronParts","trim","split","destroy","minutePart","hourPart","dayPart","monthPart","weekdayPart","testTime","getTime","setSeconds","maxIterations","iterations","minute","getMinutes","hour","getHours","dayOfMonth","getDate","month","getMonth","dayOfWeek","getDay","matchesCronPart","incrementTimeForCronPattern","currentTime","_dayPart","_monthPart","_weekdayPart","nextTime","targetMinute","getNextValidCronValue","setHours","setMinutes","targetHour","setDate","setTime","currentValue","cronPart","values","parseCronPart","v","forEach","part","sort","a","b","start","end","map","n","parseInt","i","range","step","stepNum","baseValues","_","index","value","cronValue","nextRun","updateWorkflowCronJobs","cancelWorkflowCronJobs","scheduledJobs","removeWorkflowCronJobs"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Config } from 'payload';
2
+ import type { WorkflowsPluginConfig } from "./config-types.js";
3
+ export { getLogger } from './logger.js';
4
+ export declare const workflowsPlugin: <TSlug extends string>(pluginOptions: WorkflowsPluginConfig<TSlug>) => (config: Config) => Config;