/** * The dashboard's data layer: snapshot-then-stream over the unified API * (design.md "Data Sources"; specs/web-ui "Data Sources"; task 10.2, 10.19). * * Loads its initial snapshot from the REST endpoints, then holds the SSE * stream open and applies each event incrementally — refetching the * snapshot only on reconnection (events may have been missed while * disconnected) or an explicit user request, never on a fixed interval * while the stream is healthy. When the stream is unavailable it falls back * to periodic snapshot refresh and reports the degraded transport via * `transport` so the interface can surface it (design.md "Degraded * transport is visible"). * * Room membership is not re-fetched on every assignment change: the server * only ever pushes a `room` (definition) or `room_membership` (single * device's assignment) delta, never a full room list (design.md D14; task * 9.7) — mirroring that, this store keeps room definitions and assignments * as two flat maps and derives each room's member list from them and the * live device map, exactly like `RoomManager.listRooms()` does server-side. * * Deliberately thin on logic: event application here is a handful of map * mutations per category, with everything genuinely decision-worthy (tile * ranking, revert deadlines, coalescing, reserved-key filtering, log * filtering) already extracted into the pure modules alongside this file * (design.md D23). This file itself is accepted as manually verified. */ import { type ReactNode } from "react"; import type { Automation, DeviceDescriptor, HomekitStatus, LogEntry, RoomWithMembers, StateMap, StatusData, StreamEvent, TransportState } from "../types.js"; import { CommandCoalescer } from "./command-coalescing.js"; /** One coalesced device-property command request (design.md D31). */ export interface DeviceCommandRequest { qualifiedId: string; property: string; value: unknown; } export type DeviceCommandCoalescer = CommandCoalescer; interface DataStoreValue { status: StatusData | null; automations: Automation[]; state: StateMap; devices: DeviceDescriptor[]; devicesByQualifiedId: Map; rooms: RoomWithMembers[]; unassignedDevices: DeviceDescriptor[]; logs: LogEntry[]; homekit: HomekitStatus | null; transport: TransportState; /** Re-fetches the full snapshot on explicit user request. */ refresh: () => Promise; /** Subscribe to every raw stream event — used by detail views that need one specific category (e.g. an automation's own executions). */ subscribe: (listener: (event: StreamEvent) => void) => () => void; /** * Hides a device: reflected in the local store immediately, then sent to * the server. Reverted if the request fails; otherwise reconciled (as a * no-op) by the `device_visibility` event the server broadcasts, the same * way another client's change already updates this one. */ hideDevice: (qualifiedId: string) => Promise; /** Unhides a device — the mirror of {@link hideDevice}. */ unhideDevice: (qualifiedId: string) => Promise; /** * The single app-wide coalescer every optimistic device command goes * through (design.md D31) — one instance, not one per component, so a * device's coalescing state survives a control unmounting mid-command * (e.g. navigating away during a drag) exactly as it would on the network * layer regardless of what the UI is doing. */ commandCoalescer: DeviceCommandCoalescer; } export declare function DataStoreProvider({ children }: { children: ReactNode; }): ReactNode; export declare function useDataStore(): DataStoreValue; export {}; //# sourceMappingURL=data-store.d.ts.map