export type FleetStatusKind = 'online' | 'auth' | 'offline' | 'unknown'; export type FleetFilter = 'all' | FleetStatusKind; export type FleetAuthFilter = 'all' | 'authenticated' | 'required' | 'unknown'; export type FleetSortColumn = | 'name' | 'status' | 'latency' | 'version' | 'capabilities' | 'tags' | 'freshness' | 'anomalies'; export type SortDirection = 'asc' | 'desc'; export type EmptyFleetReason = 'config' | 'search' | 'filter'; export interface FleetInstance { readonly id: string; readonly name?: string; readonly url: string; readonly description?: string; readonly tags?: readonly string[]; } export interface FleetStatus { readonly reachable: boolean; readonly authenticated?: boolean; readonly latencyMs?: number; readonly summary?: Readonly< Record & { resources?: Readonly<{ cpuPercent?: number; memoryPercent?: number; maxTemperatureCelsius?: number; phoneNumbers?: readonly string[]; }>; } >; } export interface FleetTableOptions { readonly query?: string; readonly filter?: FleetFilter; readonly auth?: FleetAuthFilter; readonly capability?: string; readonly version?: string; readonly tag?: string; readonly sort?: Readonly<{ column: FleetSortColumn; direction: SortDirection }>; readonly selectedIds?: ReadonlySet; readonly page?: number; readonly pageSize?: number; } export type FleetFreshness = 'fresh' | 'stale' | 'unknown'; export interface FleetTableRow { readonly id: string; readonly displayName: string; readonly instance: FleetInstance; readonly status?: FleetStatus; readonly statusKind: FleetStatusKind; readonly latencyMs?: number; readonly selected: boolean; readonly version: string | null; readonly capabilities: readonly string[]; readonly tags: readonly string[]; readonly freshness: FleetFreshness; readonly anomalies: readonly string[]; } export interface VisibleSelection { readonly checked: boolean; readonly indeterminate: boolean; readonly selectedCount: number; readonly rowCount: number; } export interface FleetTableViewModel { readonly rows: readonly FleetTableRow[]; readonly totalCount: number; readonly filteredCount: number; readonly visibleCount: number; readonly page: number; readonly pageSize: number; readonly pageCount: number; readonly selectedIds: readonly string[]; readonly visibleSelection: VisibleSelection; readonly emptyReason: EmptyFleetReason | null; readonly facets: Readonly<{ capabilities: readonly string[]; versions: readonly string[]; tags: readonly string[]; }>; } const STATUS_RANK: Readonly> = { online: 0, auth: 1, offline: 2, unknown: 3, }; export function fleetStatusKind(status: FleetStatus | undefined): FleetStatusKind { if (status === undefined) return 'unknown'; if (!status.reachable) return 'offline'; if (status.authenticated === false) return 'auth'; return 'online'; } function displayName(instance: FleetInstance): string { const name = instance.name?.trim(); return name ? name : instance.id; } function summaryString(status: FleetStatus | undefined, key: string): string | null { const value = status?.summary?.[key]; return typeof value === 'string' && value.trim() ? value.trim() : null; } function summaryStrings(status: FleetStatus | undefined, key: string): readonly string[] { const value = status?.summary?.[key]; if (!Array.isArray(value)) return []; return value .filter((item): item is string => typeof item === 'string' && Boolean(item.trim())) .map((item) => item.trim()); } function metadata(instance: FleetInstance, status: FleetStatus | undefined) { const freshnessValue = summaryString(status, 'freshness'); const freshness: FleetFreshness = freshnessValue === 'fresh' || freshnessValue === 'stale' ? freshnessValue : 'unknown'; return { version: summaryString(status, 'version'), capabilities: summaryStrings(status, 'capabilities'), tags: (instance.tags ?? []).filter((tag) => Boolean(tag.trim())), freshness, anomalies: summaryStrings(status, 'anomalies'), }; } type FleetMetadata = ReturnType; type FleetMetadataSortColumn = Exclude; function metadataSortValue( metadataValue: FleetMetadata, column: FleetMetadataSortColumn, ): string | null { if (column === 'version') return metadataValue.version; if (column === 'freshness') return metadataValue.freshness; return metadataValue[column].join(', ') || null; } function searchableText(instance: FleetInstance, status: FleetStatus | undefined): string { const details = metadata(instance, status); return [ instance.id, instance.name ?? '', instance.url, instance.description ?? '', ...details.tags, details.version ?? '', ...details.capabilities, details.freshness, ...details.anomalies, status?.summary === undefined ? '' : JSON.stringify(status.summary), ] .join(' ') .toLocaleLowerCase(); } function compareNames(left: FleetInstance, right: FleetInstance): number { return displayName(left).localeCompare(displayName(right), undefined, { sensitivity: 'base' }); } function compareOptional(left: string | null, right: string | null): number { if (left === null || right === null) return left === right ? 0 : left === null ? 1 : -1; return left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' }); } function compareInstances( left: FleetInstance, right: FleetInstance, statuses: ReadonlyMap, column: FleetSortColumn, direction: SortDirection, ): number { const leftStatus = statuses.get(left.id); const rightStatus = statuses.get(right.id); const leftMeta = metadata(left, leftStatus); const rightMeta = metadata(right, rightStatus); let comparison = 0; let missingComparison = false; if (column === 'status') { comparison = STATUS_RANK[fleetStatusKind(leftStatus)] - STATUS_RANK[fleetStatusKind(rightStatus)]; } else if (column === 'latency') { const leftMissing = leftStatus?.latencyMs === undefined; const rightMissing = rightStatus?.latencyMs === undefined; if (leftMissing !== rightMissing) { comparison = leftMissing ? 1 : -1; missingComparison = true; } else comparison = (leftStatus?.latencyMs ?? 0) - (rightStatus?.latencyMs ?? 0); } else if (column === 'name') comparison = compareNames(left, right); else { const leftValue = metadataSortValue(leftMeta, column); const rightValue = metadataSortValue(rightMeta, column); comparison = compareOptional(leftValue, rightValue); missingComparison = (leftValue === null) !== (rightValue === null); } if (comparison !== 0) return missingComparison ? comparison : direction === 'asc' ? comparison : -comparison; return compareNames(left, right) || left.id.localeCompare(right.id); } function authenticationMatches(status: FleetStatus | undefined, filter: FleetAuthFilter): boolean { if (filter === 'all') return true; if (filter === 'unknown') return status?.authenticated === undefined; return filter === 'authenticated' ? status?.authenticated === true : status?.authenticated === false; } function sortedUnique(values: readonly string[]): readonly string[] { return [...new Set(values)].sort((left, right) => left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' }), ); } export function buildFleetTableViewModel( instances: readonly FleetInstance[], statuses: ReadonlyMap, options: FleetTableOptions = {}, ): FleetTableViewModel { const query = options.query?.trim().toLocaleLowerCase() ?? ''; const filter = options.filter ?? 'all'; const auth = options.auth ?? 'all'; const sort = options.sort ?? { column: 'name', direction: 'asc' }; const pageSize = Math.max(1, Math.floor(options.pageSize ?? 10)); const knownIds = new Set(instances.map((instance) => instance.id)); const selectedIds = [...(options.selectedIds ?? [])].filter((id) => knownIds.has(id)).sort(); const selectedIdSet = new Set(selectedIds); const filteredInstances = instances .filter((instance) => { const status = statuses.get(instance.id); const details = metadata(instance, status); if (filter !== 'all' && fleetStatusKind(status) !== filter) return false; if (!authenticationMatches(status, auth)) return false; if (options.capability && !details.capabilities.includes(options.capability)) return false; if (options.version && details.version !== options.version) return false; if (options.tag && !details.tags.includes(options.tag)) return false; return !query || searchableText(instance, status).includes(query); }) .slice() .sort((left, right) => compareInstances(left, right, statuses, sort.column, sort.direction)); const filteredCount = filteredInstances.length; const pageCount = Math.max(1, Math.ceil(filteredCount / pageSize)); const page = Math.min(pageCount, Math.max(1, Math.floor(options.page ?? 1))); const pageInstances = filteredInstances.slice((page - 1) * pageSize, page * pageSize); const rows: FleetTableRow[] = pageInstances.map((instance) => { const status = statuses.get(instance.id); return { id: instance.id, displayName: displayName(instance), instance, ...(status === undefined ? {} : { status }), statusKind: fleetStatusKind(status), ...(status?.latencyMs === undefined ? {} : { latencyMs: status.latencyMs }), selected: selectedIdSet.has(instance.id), ...metadata(instance, status), }; }); const visibleSelectedCount = rows.reduce((count, row) => count + Number(row.selected), 0); const hasMetadataFilter = auth !== 'all' || Boolean(options.capability || options.version || options.tag); const emptyReason: EmptyFleetReason | null = instances.length === 0 ? 'config' : filteredCount > 0 ? null : query ? 'search' : filter !== 'all' || hasMetadataFilter ? 'filter' : null; const allMetadata = instances.map((instance) => metadata(instance, statuses.get(instance.id))); return { rows, totalCount: instances.length, filteredCount, visibleCount: rows.length, page, pageSize, pageCount, selectedIds, visibleSelection: { checked: rows.length > 0 && visibleSelectedCount === rows.length, indeterminate: visibleSelectedCount > 0 && visibleSelectedCount < rows.length, selectedCount: visibleSelectedCount, rowCount: rows.length, }, emptyReason, facets: { capabilities: sortedUnique(allMetadata.flatMap((item) => item.capabilities)), versions: sortedUnique(allMetadata.flatMap((item) => (item.version ? [item.version] : []))), tags: sortedUnique(allMetadata.flatMap((item) => item.tags)), }, }; }