import type { JobState, JobStateInput, JobStatus } from './types/jobs' const ACTIVE_JOB_STATUSES = new Set([ 'awaiting_input', 'queued', 'running', ]) const TERMINAL_JOB_STATUSES = new Set([ 'completed', 'failed', 'cancelled', ]) export interface JobCapabilityInput { status: string can_cancel?: boolean | null can_delete?: boolean | null } export interface JobCapabilities { can_cancel: boolean can_delete: boolean } /** Resolve action support while retaining one-cycle legacy status inference. */ export function resolveJobCapabilities(job: JobCapabilityInput): JobCapabilities { const legacyCanCancel = job.status === 'queued' || job.status === 'running' const legacyCanDelete = TERMINAL_JOB_STATUSES.has(job.status as JobStatus) return { can_cancel: typeof job.can_cancel === 'boolean' ? job.can_cancel : legacyCanCancel, can_delete: typeof job.can_delete === 'boolean' ? job.can_delete : legacyCanDelete, } } /** Clamp invalid or out-of-range progress values for safe display. */ export function normalizeJobPercent(value: number): number { if (!Number.isFinite(value)) return 0 return Math.min(100, Math.max(0, value)) } /** Create a detached canonical state without changing the transport input. */ export function normalizeJobState(job: JobStateInput): JobState { const capabilities = resolveJobCapabilities(job) return { ...job, definition_id: job.definition_id ?? (typeof job.metadata.definition_id === 'string' ? job.metadata.definition_id : null), result_status: job.result_status ?? (job.status === 'completed' && job.metadata.has_result === true ? 'available' : 'none'), result_expires_at: job.result_expires_at ?? null, progress: { ...job.progress, percent: normalizeJobPercent(job.progress.percent), }, warnings: [...job.warnings], folders: [...job.folders], metadata: { ...job.metadata }, ...capabilities, } } export function isActiveJobStatus(status: JobStatus): boolean { return ACTIVE_JOB_STATUSES.has(status) } export function isTerminalJobStatus(status: JobStatus): boolean { return TERMINAL_JOB_STATUSES.has(status) } export function jobStatusLabel(status: JobStatus): string { const labels: Record = { awaiting_input: 'Waiting for input', queued: 'Queued', running: 'Running', completed: 'Completed', failed: 'Failed', cancelled: 'Cancelled', } return labels[status] }