import type { ReactiveController, ReactiveControllerHost } from 'lit'; /** * Host slots the controller drives. {@link RoxyDataElement} satisfies this, so * the form mixin can attach a controller without the component wiring state by hand. */ interface FetchHost extends ReactiveControllerHost { data: T | null; loading: boolean; error: string | null; } /** A single request the controller issues on the component's behalf. */ export interface RoxyRequest { /** Path under the API base, e.g. "/dreams/symbols/water" or "/astrology/natal-chart". */ path: string; method?: 'GET' | 'POST'; /** JSON body for POST endpoints. */ body?: unknown; /** Query string parameters; nullish values are dropped. */ query?: Record; } /** * Client-side fetch for uncontrolled (self-fetching) components: drives `host.data` / `host.loading` / `host.error` and cancels a stale request when a newer one starts or the host disconnects. * * @remarks * Security boundary. The only credential this ever sends is a `pk_` publishable key, which carries a server-side origin allowlist. A secret (`sk_`) or legacy unprefixed key is refused before any network call and surfaced as an error, so a server secret cannot leak into a browser request. This centralizes the guard that originated in `` so every self-fetching component enforces it identically. * * Controlled-mode components never construct this. When a server injects the response as a `