* Register a job: validate, create cron task (if enabled), store in map. * Does NOT persist – callers decide when to persist.
(
jobConfig: ScheduledJobConfig,
)
| 148 | // ------------------------------------------------------------------------- |
| 149 | |
| 150 | /** |
| 151 | * Register a job: validate, create cron task (if enabled), store in map. |
| 152 | * Does NOT persist – callers decide when to persist. |
| 153 | */ |
| 154 | private registerJob( |
| 155 | jobConfig: ScheduledJobConfig, |
| 156 | ): { registered: boolean; message: string } { |
| 157 | const normalizedConfig = normalizeScheduledJobConfig(jobConfig); |
| 158 | const normalizedCron = normalizeJobCron(normalizedConfig.cron); |
| 159 | |
| 160 | if (!isValidJobId(normalizedConfig.id)) { |
| 161 | const msg = `[Scheduler] Invalid job id "${normalizedConfig.id}": must be non-empty, must not contain "/", "\\\\", or line breaks, and must be ≤128 chars`; |
| 162 | console.error(msg); |
| 163 | return { registered: false, message: msg }; |
| 164 | } |
| 165 | |
| 166 | if (normalizedCron) { |
| 167 | // Validate cron expression |
| 168 | if (!cron.validate(normalizedCron)) { |
| 169 | const msg = `[Scheduler] Invalid cron expression for job "${normalizedConfig.name}": ${normalizedCron}`; |
| 170 | console.error(msg); |
| 171 | return { registered: false, message: msg }; |
| 172 | } |
| 173 | |
| 174 | // Validate timezone if provided |
| 175 | if (normalizedConfig.timezone && !isValidTimezone(normalizedConfig.timezone)) { |
| 176 | const msg = `[Scheduler] Invalid timezone for job "${normalizedConfig.name}": ${normalizedConfig.timezone}`; |
| 177 | console.error(msg); |
| 178 | return { registered: false, message: msg }; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | // Stop/remove existing job with the same id if any |
| 183 | this.removeFromMap(normalizedConfig.id); |
| 184 | |
| 185 | // Create cron task only for recurring enabled jobs |
| 186 | let task: ReturnType<typeof cron.schedule> | null = null; |
| 187 | if (normalizedCron && normalizedConfig.enabled !== false) { |
| 188 | task = this.createCronTask(normalizedConfig); |
| 189 | console.log( |
| 190 | `[Scheduler] Job "${normalizedConfig.name}" scheduled: ${normalizedCron}${normalizedConfig.timezone ? ` (${normalizedConfig.timezone})` : ""}`, |
| 191 | ); |
| 192 | } else if (normalizedCron) { |
| 193 | console.log( |
| 194 | `[Scheduler] Job "${normalizedConfig.name}" registered (disabled)`, |
| 195 | ); |
| 196 | } else { |
| 197 | console.log( |
| 198 | `[Scheduler] Job "${normalizedConfig.name}" registered as one-time (manual trigger only)`, |
| 199 | ); |
| 200 | } |
| 201 | |
| 202 | this.jobs.set(normalizedConfig.id, { |
| 203 | config: normalizedConfig, |
| 204 | task, |
| 205 | running: false, |
| 206 | notifyFailed: false, |
| 207 | }); |
no test coverage detected