Add trigger builder helpers to improve DX for custom triggers

- Add createTrigger() and createAdvancedTrigger() helpers
- Add preset builders: webhookTrigger, cronTrigger, eventTrigger, etc.
- Implement virtual fields with JSON backing for trigger parameters
- Eliminate 90% of boilerplate when creating custom triggers
- Add /helpers export path for trigger builders
- Fix field name clashing between built-in and custom trigger parameters
- Add comprehensive examples and documentation
- Maintain backward compatibility with existing triggers

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-07 15:30:10 +02:00
parent 0a036752ea
commit c47197223c
18 changed files with 1185 additions and 623 deletions

View File

@@ -54,14 +54,17 @@ export function generateCronTasks(config: Config): void {
// Find the matching cron trigger and check its condition if present
const triggers = workflow.triggers as Array<{
condition?: string
cronExpression?: string
timezone?: string
parameters?: {
cronExpression?: string
timezone?: string
[key: string]: any
}
type: string
}>
const matchingTrigger = triggers?.find(trigger =>
trigger.type === 'cron-trigger' &&
trigger.cronExpression === cronExpression
trigger.parameters?.cronExpression === cronExpression
)
// Check trigger condition if present
@@ -183,8 +186,11 @@ export async function registerCronJobs(payload: Payload, logger: Payload['logger
for (const workflow of workflows.docs) {
const triggers = workflow.triggers as Array<{
cronExpression?: string
timezone?: string
parameters?: {
cronExpression?: string
timezone?: string
[key: string]: any
}
type: string
}>
@@ -192,12 +198,12 @@ export async function registerCronJobs(payload: Payload, logger: Payload['logger
const cronTriggers = triggers?.filter(t => t.type === 'cron-trigger') || []
for (const trigger of cronTriggers) {
if (trigger.cronExpression) {
if (trigger.parameters?.cronExpression) {
try {
// Validate cron expression before queueing
if (!validateCronExpression(trigger.cronExpression)) {
if (!validateCronExpression(trigger.parameters.cronExpression)) {
logger.error({
cronExpression: trigger.cronExpression,
cronExpression: trigger.parameters.cronExpression,
workflowId: workflow.id,
workflowName: workflow.name
}, 'Invalid cron expression format')
@@ -205,13 +211,13 @@ export async function registerCronJobs(payload: Payload, logger: Payload['logger
}
// Validate timezone if provided
if (trigger.timezone) {
if (trigger.parameters?.timezone) {
try {
// Test if timezone is valid by trying to create a date with it
new Intl.DateTimeFormat('en', { timeZone: trigger.timezone })
new Intl.DateTimeFormat('en', { timeZone: trigger.parameters.timezone })
} catch {
logger.error({
timezone: trigger.timezone,
timezone: trigger.parameters.timezone,
workflowId: workflow.id,
workflowName: workflow.name
}, 'Invalid timezone specified')
@@ -220,27 +226,27 @@ export async function registerCronJobs(payload: Payload, logger: Payload['logger
}
// Calculate next execution time
const nextExecution = getNextCronTime(trigger.cronExpression, trigger.timezone)
const nextExecution = getNextCronTime(trigger.parameters.cronExpression, trigger.parameters?.timezone)
// Queue the job
await payload.jobs.queue({
input: { cronExpression: trigger.cronExpression, timezone: trigger.timezone, workflowId: workflow.id },
input: { cronExpression: trigger.parameters.cronExpression, timezone: trigger.parameters?.timezone, workflowId: workflow.id },
task: 'workflow-cron-executor',
waitUntil: nextExecution
})
logger.info({
cronExpression: trigger.cronExpression,
cronExpression: trigger.parameters.cronExpression,
nextExecution: nextExecution.toISOString(),
timezone: trigger.timezone || 'UTC',
timezone: trigger.parameters?.timezone || 'UTC',
workflowId: workflow.id,
workflowName: workflow.name
}, 'Queued initial cron job for workflow')
} catch (error) {
logger.error({
cronExpression: trigger.cronExpression,
cronExpression: trigger.parameters.cronExpression,
error: error instanceof Error ? error.message : 'Unknown error',
timezone: trigger.timezone,
timezone: trigger.parameters?.timezone,
workflowId: workflow.id,
workflowName: workflow.name
}, 'Failed to queue cron job')
@@ -508,8 +514,11 @@ export async function updateWorkflowCronJobs(
}
const triggers = workflow.triggers as Array<{
cronExpression?: string
timezone?: string
parameters?: {
cronExpression?: string
timezone?: string
[key: string]: any
}
type: string
}>
@@ -524,12 +533,12 @@ export async function updateWorkflowCronJobs(
let scheduledJobs = 0
for (const trigger of cronTriggers) {
if (trigger.cronExpression) {
if (trigger.parameters?.cronExpression) {
try {
// Validate cron expression before queueing
if (!validateCronExpression(trigger.cronExpression)) {
if (!validateCronExpression(trigger.parameters.cronExpression)) {
logger.error({
cronExpression: trigger.cronExpression,
cronExpression: trigger.parameters.cronExpression,
workflowId,
workflowName: workflow.name
}, 'Invalid cron expression format')
@@ -537,12 +546,12 @@ export async function updateWorkflowCronJobs(
}
// Validate timezone if provided
if (trigger.timezone) {
if (trigger.parameters?.timezone) {
try {
new Intl.DateTimeFormat('en', { timeZone: trigger.timezone })
new Intl.DateTimeFormat('en', { timeZone: trigger.parameters.timezone })
} catch {
logger.error({
timezone: trigger.timezone,
timezone: trigger.parameters.timezone,
workflowId,
workflowName: workflow.name
}, 'Invalid timezone specified')
@@ -551,11 +560,11 @@ export async function updateWorkflowCronJobs(
}
// Calculate next execution time
const nextExecution = getNextCronTime(trigger.cronExpression, trigger.timezone)
const nextExecution = getNextCronTime(trigger.parameters.cronExpression, trigger.parameters?.timezone)
// Queue the job
await payload.jobs.queue({
input: { cronExpression: trigger.cronExpression, timezone: trigger.timezone, workflowId },
input: { cronExpression: trigger.parameters.cronExpression, timezone: trigger.parameters?.timezone, workflowId },
task: 'workflow-cron-executor',
waitUntil: nextExecution
})
@@ -563,17 +572,17 @@ export async function updateWorkflowCronJobs(
scheduledJobs++
logger.info({
cronExpression: trigger.cronExpression,
cronExpression: trigger.parameters.cronExpression,
nextExecution: nextExecution.toISOString(),
timezone: trigger.timezone || 'UTC',
timezone: trigger.parameters?.timezone || 'UTC',
workflowId,
workflowName: workflow.name
}, 'Scheduled cron job for workflow')
} catch (error) {
logger.error({
cronExpression: trigger.cronExpression,
cronExpression: trigger.parameters?.cronExpression,
error: error instanceof Error ? error.message : 'Unknown error',
timezone: trigger.timezone,
timezone: trigger.parameters?.timezone,
workflowId,
workflowName: workflow.name
}, 'Failed to schedule cron job')