( value: unknown, sourceLabel: string, )
| 127 | } |
| 128 | |
| 129 | export function validateJobFileShape( |
| 130 | value: unknown, |
| 131 | sourceLabel: string, |
| 132 | ): asserts value is JobFile { |
| 133 | if (!value || typeof value !== "object") { |
| 134 | throw new Error(`Invalid job config from ${sourceLabel}: expected a JSON object`); |
| 135 | } |
| 136 | |
| 137 | const jobFile = value as Record<string, unknown>; |
| 138 | if (!Array.isArray(jobFile.jobs)) { |
| 139 | throw new Error(`Invalid job config from ${sourceLabel}: "jobs" must be an array`); |
| 140 | } |
| 141 | |
| 142 | const ids = new Set<string>(); |
| 143 | jobFile.jobs.forEach((job, index) => { |
| 144 | validateScheduledJobConfig(job, sourceLabel, index); |
| 145 | const normalizedId = job.id.trim().toLowerCase(); |
| 146 | if (ids.has(normalizedId)) { |
| 147 | throw new Error( |
| 148 | `Invalid job config from ${sourceLabel}: duplicate job id "${job.id}" is not allowed`, |
| 149 | ); |
| 150 | } |
| 151 | ids.add(normalizedId); |
| 152 | }); |
| 153 | } |
| 154 | |
| 155 | function normalizeJobFile(jobFile: JobFile): JobFile { |
| 156 | return { |
no test coverage detected