(field: string, min: number, max: number, allowL: boolean = false)
| 60 | // Validate a single cron field: supports *, numbers, ranges (n-m), steps (*/s, n/s, n-m/s), and comma-separated lists. |
| 61 | // When `allowL` is true, also accepts the standalone `L` token (used for the day-of-month field to mean "last day of month"). |
| 62 | const validateCronField = (field: string, min: number, max: number, allowL: boolean = false): boolean => { |
| 63 | const parts = field.split(',') |
| 64 | if (parts.some((p) => p === '')) return false // catches leading/trailing/consecutive commas |
| 65 | |
| 66 | for (const part of parts) { |
| 67 | if (allowL && part === 'L') continue |
| 68 | const slashIdx = part.indexOf('/') |
| 69 | if (slashIdx !== -1) { |
| 70 | const base = part.slice(0, slashIdx) |
| 71 | const stepStr = part.slice(slashIdx + 1) |
| 72 | if (!/^\d+$/.test(stepStr)) return false |
| 73 | const step = parseInt(stepStr, 10) |
| 74 | if (step < 1) return false |
| 75 | // Base must be *, a plain number, or a range |
| 76 | if (base !== '*' && !isValidRangeOrNumber(base, min, max)) return false |
| 77 | } else if (part !== '*') { |
| 78 | if (!isValidRangeOrNumber(part, min, max)) return false |
| 79 | } |
| 80 | } |
| 81 | return true |
| 82 | } |
| 83 | |
| 84 | // Per-position field ranges [min, max]: minute hour day-of-month month day-of-week |
| 85 | const fieldRanges: Array<[number, number]> = [ |
no test coverage detected