import { useEffect, useRef, useState, type ReactNode } from 'react'; import type { InstanceContext } from '../app-shell.js'; /** Aggregate task counts only; per-task records and configuration are intentionally unsupported. */ export interface AutomationTaskCounts { readonly total?: number | null; readonly enabled?: number | null; readonly disabled?: number | null; readonly running?: number | null; readonly queued?: number | null; readonly succeeded?: number | null; readonly failed?: number | null; } /** Bounded high-level status labels only, never status payloads or raw logs. */ export type AutomationState = 'healthy' | 'degraded' | 'failed' | 'disabled' | 'unknown'; export type AutomationScheduler = 'active' | 'idle' | 'paused' | 'disabled' | 'unavailable'; export type AutomationWorkers = 'available' | 'busy' | 'degraded' | 'disabled' | 'unavailable'; export interface AutomationStatus { readonly state?: AutomationState | null; readonly scheduler?: AutomationScheduler | null; readonly workers?: AutomationWorkers | null; } export interface AutomationSnapshot { readonly observedAt?: string; readonly status: AutomationStatus; readonly tasks: AutomationTaskCounts; } export interface AutomationDataSource { /** Injected by the authenticated owner; this isolated module defines no production endpoint. */ load(instanceId: string, signal: AbortSignal): Promise; } export interface AutomationModuleProps { readonly instance: InstanceContext; readonly dataSource?: AutomationDataSource; readonly refreshSignal?: unknown; } type ReadState = | { kind: 'idle'; ownerId: string } | { kind: 'loading'; ownerId: string; snapshot?: AutomationSnapshot } | { kind: 'ready'; ownerId: string; snapshot: AutomationSnapshot } | { kind: 'error'; ownerId: string; snapshot?: AutomationSnapshot }; const SAFE_LOAD_ERROR = 'Automation data could not be loaded.'; const STATES = new Set(['healthy', 'degraded', 'failed', 'disabled', 'unknown']); const SCHEDULERS = new Set([ 'active', 'idle', 'paused', 'disabled', 'unavailable', ]); const WORKERS = new Set([ 'available', 'busy', 'degraded', 'disabled', 'unavailable', ]); const TASK_COUNT_KEYS = [ 'total', 'enabled', 'disabled', 'running', 'queued', 'succeeded', 'failed', ] as const; function isRecord(value: unknown): value is Readonly> { return typeof value === 'object' && value !== null && !Array.isArray(value); } function safeTimestamp(value: unknown): string | undefined { if ( typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(value) ) return undefined; const parsed = new Date(value); if (!Number.isFinite(parsed.getTime())) return undefined; const canonical = parsed.toISOString(); return canonical === value || canonical === value.replace(/Z$/, '.000Z') ? value : undefined; } function boundedEnum( value: unknown, allowed: ReadonlySet, ): T | null | undefined { if (value === null) return null; return typeof value === 'string' && allowed.has(value as T) ? (value as T) : undefined; } /** Copy and validate untrusted source data before it can enter React state. */ export function sanitizeAutomationSnapshot(value: unknown): AutomationSnapshot { const source = isRecord(value) ? value : {}; const rawStatus = isRecord(source.status) ? source.status : {}; const state = boundedEnum(rawStatus.state, STATES); const scheduler = boundedEnum(rawStatus.scheduler, SCHEDULERS); const workers = boundedEnum(rawStatus.workers, WORKERS); const status: AutomationStatus = { ...(state !== undefined ? { state } : {}), ...(scheduler !== undefined ? { scheduler } : {}), ...(workers !== undefined ? { workers } : {}), }; const rawTasks = isRecord(source.tasks) ? source.tasks : {}; const tasks: Record = {}; for (const key of TASK_COUNT_KEYS) { const count = rawTasks[key]; if (count === null || (Number.isSafeInteger(count) && (count as number) >= 0)) tasks[key] = count as number | null; } const observedAt = safeTimestamp(source.observedAt); return { ...(observedAt ? { observedAt } : {}), status, tasks, }; } function Section({ label, children }: { label: string; children: ReactNode }) { return (

{label}

{children}
); } function Fields({ values, }: { values: readonly (readonly [string, string | number | null | undefined])[]; }) { const supplied = values.filter(([, value]) => value !== undefined); if (!supplied.length) return

No aggregate status was supplied.

; return (
{supplied.map(([label, value]) => (
{label}
{value == null ? 'Unavailable' : String(value)}
))}
); } function SnapshotView({ snapshot }: { snapshot: AutomationSnapshot }) { const status = snapshot.status; const tasks = snapshot.tasks; return ( <> {snapshot.observedAt ?

Observed {snapshot.observedAt}

: null}

Read-only aggregate view; task details and operational controls are excluded.

); } export function AutomationModule({ instance, dataSource, refreshSignal }: AutomationModuleProps) { const requestFence = useRef(0); const [retry, setRetry] = useState(0); const [state, setState] = useState({ kind: 'idle', ownerId: instance.id }); useEffect(() => { const request = ++requestFence.current; const ownerId = instance.id; const controller = new AbortController(); if (instance.authentication !== 'authenticated' || !dataSource) { setState({ kind: 'idle', ownerId }); return () => controller.abort(); } setState((current) => ({ kind: 'loading', ownerId, ...(current.ownerId === ownerId && current.kind !== 'idle' && current.snapshot ? { snapshot: current.snapshot } : {}), })); let read: Promise; try { read = dataSource.load(ownerId, controller.signal); } catch (reason: unknown) { read = Promise.reject(reason); } void read.then( (snapshot) => { if (request === requestFence.current && !controller.signal.aborted) setState({ kind: 'ready', ownerId, snapshot: sanitizeAutomationSnapshot(snapshot) }); }, (_reason: unknown) => { void _reason; if (request === requestFence.current && !controller.signal.aborted) setState((current) => ({ kind: 'error', ownerId, ...(current.ownerId === ownerId && current.kind === 'loading' && current.snapshot ? { snapshot: current.snapshot } : {}), })); }, ); return () => controller.abort(); }, [dataSource, instance.authentication, instance.id, refreshSignal, retry]); if (instance.authentication !== 'authenticated') return (
Authentication is required before Automation data can be read for this instance.
); if (!dataSource) return (
No safe Automation read data source is available. This console will not invent or call an uncontracted production endpoint.
); const retained = state.ownerId === instance.id && (state.kind === 'loading' || state.kind === 'error') ? state.snapshot : undefined; if ((state.kind === 'idle' || state.kind === 'loading') && !retained) return (

Loading Automation…

); if (state.kind === 'error' && !retained) return (

Unable to load Automation: {SAFE_LOAD_ERROR}

); const snapshot = state.kind === 'ready' && state.ownerId === instance.id ? state.snapshot : retained; if (!snapshot) return null; return (
{state.kind === 'loading' ?

Refreshing Automation…

: null} {state.kind === 'error' ? (

Refresh failed; showing the last known Automation data.

) : null} {instance.freshness !== 'fresh' ? (

Automation data is {instance.freshness}; verify freshness before relying on these values.

) : null}
); }