@@ -5,14 +5,8 @@ import { eq } from 'drizzle-orm'
55import type { PgTransaction } from 'drizzle-orm/pg-core'
66import type { PostgresJsQueryResultHKT } from 'drizzle-orm/postgres-js'
77import { createLogger } from '@/lib/logs/console/logger'
8- import {
9- type BlockState ,
10- calculateNextRunTime ,
11- generateCronExpression ,
12- getScheduleTimeValues ,
13- getSubBlockValue ,
14- validateCronExpression ,
15- } from './utils'
8+ import type { BlockState } from '@/lib/workflows/schedules/utils'
9+ import { findScheduleBlocks , validateScheduleBlock } from '@/lib/workflows/schedules/validation'
1610
1711const logger = createLogger ( 'ScheduleDeployUtils' )
1812
@@ -28,18 +22,6 @@ type DbOrTx =
2822 ExtractTablesWithRelations < typeof schema >
2923 >
3024
31- /**
32- * Result of schedule validation
33- */
34- export interface ScheduleValidationResult {
35- isValid : boolean
36- error ?: string
37- scheduleType ?: string
38- cronExpression ?: string
39- nextRunAt ?: Date
40- timezone ?: string
41- }
42-
4325/**
4426 * Result of schedule creation during deploy
4527 */
@@ -52,166 +34,6 @@ export interface ScheduleDeployResult {
5234 timezone ?: string
5335}
5436
55- /**
56- * Check if a time value is valid (not empty/null/undefined)
57- */
58- function isValidTimeValue ( value : string | null | undefined ) : boolean {
59- return ! ! value && value . trim ( ) !== '' && value . includes ( ':' )
60- }
61-
62- /**
63- * Check if a schedule type has valid configuration
64- * Uses raw block values to avoid false positives from default parsing
65- */
66- function hasValidScheduleConfig ( scheduleType : string | undefined , block : BlockState ) : boolean {
67- switch ( scheduleType ) {
68- case 'minutes' : {
69- const rawValue = getSubBlockValue ( block , 'minutesInterval' )
70- const numValue = Number ( rawValue )
71- return ! ! rawValue && ! Number . isNaN ( numValue ) && numValue >= 1 && numValue <= 1440
72- }
73- case 'hourly' : {
74- const rawValue = getSubBlockValue ( block , 'hourlyMinute' )
75- const numValue = Number ( rawValue )
76- return rawValue !== '' && ! Number . isNaN ( numValue ) && numValue >= 0 && numValue <= 59
77- }
78- case 'daily' : {
79- const rawTime = getSubBlockValue ( block , 'dailyTime' )
80- return isValidTimeValue ( rawTime )
81- }
82- case 'weekly' : {
83- const rawDay = getSubBlockValue ( block , 'weeklyDay' )
84- const rawTime = getSubBlockValue ( block , 'weeklyDayTime' )
85- return ! ! rawDay && isValidTimeValue ( rawTime )
86- }
87- case 'monthly' : {
88- const rawDay = getSubBlockValue ( block , 'monthlyDay' )
89- const rawTime = getSubBlockValue ( block , 'monthlyTime' )
90- const dayNum = Number ( rawDay )
91- return (
92- ! ! rawDay &&
93- ! Number . isNaN ( dayNum ) &&
94- dayNum >= 1 &&
95- dayNum <= 31 &&
96- isValidTimeValue ( rawTime )
97- )
98- }
99- case 'custom' :
100- return ! ! getSubBlockValue ( block , 'cronExpression' )
101- default :
102- return false
103- }
104- }
105-
106- /**
107- * Get human-readable error message for missing schedule configuration
108- */
109- function getMissingConfigError ( scheduleType : string ) : string {
110- switch ( scheduleType ) {
111- case 'minutes' :
112- return 'Minutes interval is required for minute-based schedules'
113- case 'hourly' :
114- return 'Minute value is required for hourly schedules'
115- case 'daily' :
116- return 'Time is required for daily schedules'
117- case 'weekly' :
118- return 'Day and time are required for weekly schedules'
119- case 'monthly' :
120- return 'Day and time are required for monthly schedules'
121- case 'custom' :
122- return 'Cron expression is required for custom schedules'
123- default :
124- return 'Schedule type is required'
125- }
126- }
127-
128- /**
129- * Find schedule blocks in a workflow's blocks
130- */
131- export function findScheduleBlocks ( blocks : Record < string , BlockState > ) : BlockState [ ] {
132- return Object . values ( blocks ) . filter ( ( block ) => block . type === 'schedule' )
133- }
134-
135- /**
136- * Validate a schedule block's configuration
137- * Returns validation result with error details if invalid
138- */
139- export function validateScheduleBlock ( block : BlockState ) : ScheduleValidationResult {
140- const scheduleType = getSubBlockValue ( block , 'scheduleType' )
141-
142- if ( ! scheduleType ) {
143- return {
144- isValid : false ,
145- error : 'Schedule type is required' ,
146- }
147- }
148-
149- const hasValidConfig = hasValidScheduleConfig ( scheduleType , block )
150-
151- if ( ! hasValidConfig ) {
152- return {
153- isValid : false ,
154- error : getMissingConfigError ( scheduleType ) ,
155- scheduleType,
156- }
157- }
158-
159- const timezone = getSubBlockValue ( block , 'timezone' ) || 'UTC'
160-
161- try {
162- // Get parsed schedule values (safe to use after validation passes)
163- const scheduleValues = getScheduleTimeValues ( block )
164- const sanitizedScheduleValues =
165- scheduleType !== 'custom' ? { ...scheduleValues , cronExpression : null } : scheduleValues
166-
167- const cronExpression = generateCronExpression ( scheduleType , sanitizedScheduleValues )
168-
169- const validation = validateCronExpression ( cronExpression , timezone )
170- if ( ! validation . isValid ) {
171- return {
172- isValid : false ,
173- error : `Invalid cron expression: ${ validation . error } ` ,
174- scheduleType,
175- }
176- }
177-
178- const nextRunAt = calculateNextRunTime ( scheduleType , sanitizedScheduleValues )
179-
180- return {
181- isValid : true ,
182- scheduleType,
183- cronExpression,
184- nextRunAt,
185- timezone,
186- }
187- } catch ( error ) {
188- return {
189- isValid : false ,
190- error : error instanceof Error ? error . message : 'Failed to generate schedule' ,
191- scheduleType,
192- }
193- }
194- }
195-
196- /**
197- * Validate all schedule blocks in a workflow
198- * Returns the first validation error found, or success if all are valid
199- */
200- export function validateWorkflowSchedules (
201- blocks : Record < string , BlockState >
202- ) : ScheduleValidationResult {
203- const scheduleBlocks = findScheduleBlocks ( blocks )
204-
205- for ( const block of scheduleBlocks ) {
206- const result = validateScheduleBlock ( block )
207- if ( ! result . isValid ) {
208- return result
209- }
210- }
211-
212- return { isValid : true }
213- }
214-
21537/**
21638 * Create or update schedule records for a workflow during deployment
21739 * This should be called within a database transaction
0 commit comments