/** * `systemFeed` — a transport-light polling helper for the AdminShell system * scope (issue #1774, epic #1766). * * The System edge presents jobs / schedules / dispatch — data that lives in the * `_smrt_*` SYSTEM tables. Those tables are intentionally **excluded** from the * core change-feed (`packages/core/src/change-feed.ts`) and are **not** surfaced * as `smrt-web` collections, so the live-collection path used by tenant/app * data does not apply here. Instead, an app exposes its own status endpoint * (a `+server.ts` that reads `_smrt_*` through the `smrt-jobs` query API) and * this helper polls it on an interval, mapping the response into the * presentation contracts the shell already renders: * `ShellSystemPanel[]` (for `SystemScopePanel`) and `ShellStatusChip[]` * (for `SystemStatusChips`). * * This module is deliberately kept OUT of the `./workspace` presentation barrel * and the `./web` (smrt-web / TanStack) entry: it is opt-in via the * `@happyvertical/smrt-svelte/workspace/live` subpath and pulls no runtime * dependency beyond Svelte's reactivity. It only imports the shell's *types*. * * @example * ```svelte * * * * * ``` */ import type { ShellStatusChip, ShellSystemPanel } from '../admin-shell/types.js'; /** Lifecycle phase of a {@link SystemFeedController}. */ export type SystemFeedStatus = 'idle' | 'loading' | 'success' | 'error' | 'stopped'; /** * The shell-facing shape produced by {@link SystemFeedOptions.map}. Either half * is optional so a feed can drive only the panels, only the chips, or both. */ export interface SystemFeedView { panels?: ShellSystemPanel[]; chips?: ShellStatusChip[]; } /** Options for {@link systemFeed}. */ export interface SystemFeedOptions { /** * App-supplied async loader. Called on each tick (and once immediately unless * `immediate` is `false`). Typically wraps `fetch('/…/status')`. Its resolved * value is handed to {@link SystemFeedOptions.map}. Rejections are caught and * surfaced via `error` / `onError` without stopping the poll loop. */ fetch: (signal: AbortSignal) => T | Promise; /** * Maps a raw `fetch` result into the shell presentation contracts. Runs * inside the same try/catch as `fetch`, so a throwing mapper is treated as a * failed tick (tolerated, loop continues). */ map: (data: T) => SystemFeedView; /** * Poll interval in milliseconds. Values `<= 0` disable the timer (single * fetch only, useful for tests / manual `refresh()`). Default `5000`. */ intervalMs?: number; /** * Fetch once as soon as the feed is created. Default `true`. When `false`, * nothing is fetched until the first timer tick or an explicit `refresh()`. */ immediate?: boolean; /** * Skip ticks while `document.hidden` is true and refresh once on the next * `visibilitychange` back to visible. Avoids polling a backgrounded tab. * Default `true`. Ignored (treated as `false`) when there is no `document` * (SSR / non-DOM environments). */ pauseWhenHidden?: boolean; /** Notified on every caught fetch/map error. Never throws into the loop. */ onError?: (error: unknown) => void; } /** * Reactive controller returned by {@link systemFeed}. `panels` / `chips` are * `$state`-backed getters safe to bind straight to `SystemScopePanel` / * `SystemStatusChips`. Call {@link SystemFeedController.dispose} (e.g. from * `onDestroy`) to stop the timer and detach listeners — this is the disposer. */ export interface SystemFeedController { /** Latest mapped panels; `[]` until the first successful tick. */ readonly panels: ShellSystemPanel[]; /** Latest mapped chips; `[]` until the first successful tick. */ readonly chips: ShellStatusChip[]; /** Lifecycle phase of the most recent tick. */ readonly status: SystemFeedStatus; /** The most recent caught error, or `null` after any success. */ readonly error: unknown; /** * Whether the poll timer is currently armed. Stays `false` for single-shot * feeds (`intervalMs <= 0`), under SSR (no `document`), and after `stop()` / * `dispose()`. */ readonly running: boolean; /** Fetch + map once, off-schedule. Resolves after state is updated. */ refresh(): Promise; /** * (Re)arm the poll timer. Idempotent. No-op when already running, after * {@link dispose}, when `intervalMs <= 0`, or under SSR (no `document`). */ start(): void; /** Disarm the poll timer without tearing down listeners. Idempotent. */ stop(): void; /** Stop the timer, detach listeners, abort any in-flight fetch. Terminal. */ dispose(): void; } /** * Create a polling feed that maps an app status endpoint into the AdminShell * system-scope presentation contracts. * * The returned {@link SystemFeedController} exposes reactive `panels` / `chips` * and a `dispose()` disposer. Bind the getters to the shell components and call * `dispose()` on teardown. */ export declare function systemFeed(options: SystemFeedOptions): SystemFeedController; //# sourceMappingURL=system-feed.svelte.d.ts.map