Files
orca/src/shared/timer-delay.ts
T
Jinjing 21dee21a6d test(cli): lock CLI-compatible timeout parse contract (#11206)
parsePositiveSafeIntegerNumericText mirrors the CLI's own Number()
coercion on purpose: text like `600000.000000000000001` is the budget
the CLI will actually wait on, so rejecting it here would leave the
relay and SSH kill timers shorter than the CLI's and cut the request
short. Document that and pin it with regression cases.
2026-07-28 10:22:33 -07:00

60 lines
2.0 KiB
TypeScript

export const MAX_TIMER_DELAY_MS = 2_147_483_647
export function isSafeTimerDelayMs(value: unknown): value is number {
return (
typeof value === 'number' &&
Number.isSafeInteger(value) &&
value >= 0 &&
value <= MAX_TIMER_DELAY_MS
)
}
export function parsePositiveSafeIntegerText(raw: string): number | null {
const trimmed = raw.trim()
const value = Number(trimmed)
if (!Number.isSafeInteger(value) || value <= 0) {
return null
}
const exactValue = parseExactIntegerNumericText(trimmed)
return exactValue === BigInt(value) ? value : null
}
// Why: mirrors the CLI's own `Number()` coercion for generic `--timeout-ms`
// flags (cli/flags.ts getOptionalPositiveIntegerFlag). Text that coerces to an
// exact integer — `1000.0`, `600000.000000000000001` — is the budget the CLI
// will actually wait on, so rejecting it here would leave the caller's timer
// shorter than the CLI's and cut the request short. Callers that need exact
// text (orchestration ask) use parsePositiveSafeIntegerText instead.
export function parsePositiveSafeIntegerNumericText(raw: string): number | null {
const value = Number(raw)
return Number.isSafeInteger(value) && value > 0 ? value : null
}
function parseExactIntegerNumericText(raw: string): bigint | null {
if (
/^\+?0[xX][\da-fA-F]+$/.test(raw) ||
/^\+?0[bB][01]+$/.test(raw) ||
/^\+?0[oO][0-7]+$/.test(raw)
) {
return BigInt(raw.startsWith('+') ? raw.slice(1) : raw)
}
const match = /^\+?(\d+(?:\.\d*)?|\.\d+)(?:[eE]([+-]?\d+))?$/.exec(raw)
if (!match) {
return null
}
const [whole = '', fraction = ''] = match[1].split('.')
const digits = `${whole}${fraction}`.replace(/^0+/, '') || '0'
const shift = Number(match[2] ?? 0) - fraction.length
if (!Number.isSafeInteger(shift)) {
return null
}
if (shift >= 0) {
return BigInt(digits) * 10n ** BigInt(shift)
}
const removedDigits = -shift
if (removedDigits > digits.length || !digits.endsWith('0'.repeat(removedDigits))) {
return null
}
return BigInt(digits.slice(0, -removedDigits) || '0')
}