/**
* `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