import type { AdminBadge, AdminTypes } from "./define-admin.js"; export { useOpenLink } from "./slots.js"; /** Where Admin renders: its own page, or inside an MCP host (`ui://`). Slots and hooks that work in both say so. */ export type AdminSurface = "browser" | "mcp-app"; /** `unavailable` is a refused or a missing target, on purpose indistinguishable; it is also what a surface without the data reads as. */ export type ReadStatus = "loading" | "ready" | "unavailable" | "error"; /** A transport's answer: the data, or `unavailable` for a 403 or 404 (an MCP tool's `AUTH_DENIED` or `NOT_FOUND`). */ export type Reply = { readonly status: "ok"; readonly data: T; } | { readonly status: "unavailable"; }; type Row = Readonly>; /** An entry as `/admin/api/entries` returns it; `data` is typed by the generated collection. */ export interface AdminEntry { readonly id: string; readonly collection: string; readonly locale: string | null; readonly status: "draft" | "published" | "archived" | null; readonly version: number | string; readonly data: Data; readonly created_at?: number | null; readonly updated_at: number; } /** The signed-in caller, as `/admin/api/me` returns it. */ export interface AdminMe { readonly userId: string; readonly role: string | null; readonly admin: boolean; readonly login: string | null; readonly image: string | null; } /** What an MCP host lets Admin do beyond rendering. Each is sent only when the host advertised it. */ export interface AdminHost { /** The host's context: theme, locale, time zone, safe area and the rest. */ readonly context: Readonly>; readonly capabilities: Readonly>; /** `ui/open-link`: an http(s) URL in the user's browser. False when the host cannot or refuses. */ openLink(url: string): Promise; /** `ui/message`: a user message in the conversation. */ sendMessage(text: string): Promise; /** `ui/update-model-context`: what the model should know at its next turn. */ updateModelContext(params: { readonly content?: readonly { readonly type: "text"; readonly text: string; }[]; readonly structuredContent?: Readonly>; }): Promise; /** `ui/request-display-mode`: the mode granted, or null when the host offers none other. */ requestDisplayMode(mode: "inline" | "fullscreen" | "pip"): Promise<"inline" | "fullscreen" | "pip" | null>; } /** One View in a badge batch. */ export type BadgeAnswer = { readonly rows: readonly Row[]; } | { readonly unavailable: true; } | { readonly error: true; }; /** A badge as the sidebar shows it: loading, a count (zero included), unavailable (shows nothing) or error. */ export interface BadgeState { readonly count: number | undefined; readonly status: ReadStatus; } /** An MCP tool result, as a host returns it. */ export interface McpToolResult { readonly content?: readonly { readonly type: string; readonly text?: string; }[]; readonly structuredContent?: Readonly>; readonly isError?: boolean; } /** How a surface reads and writes: `/admin/api` in the browser, `tools/call` through an MCP host. The hooks do not know which. */ export interface AdminTransport { readonly surface: AdminSurface; /** The host's API; null in the browser. */ readonly host: AdminHost | null; /** Whether this surface can write: an MCP host without server tools is read-only. */ readonly writable: boolean; view(name: string, input: Row, page: { readonly limit?: number | undefined; readonly cursor?: string | undefined; }, rowNames?: { readonly [column: string]: string; }): Promise>; entries(collection: string, options: { readonly filter?: { readonly field: string; readonly value: string; } | undefined; readonly search?: string | undefined; readonly limit?: number | undefined; readonly cursor?: string | undefined; }): Promise[]; readonly nextCursor?: string; }>>; entry(collection: string, id: string): Promise>>; total(collection: string): Promise>; me(): Promise>; /** One read of up to `MAX_BADGE_VIEWS` Views' first rows, each as the caller: a refused or missing View is `unavailable`, one that could not answer is `error`. */ badges(names: readonly string[]): Promise>; run(name: string, input: Row): Promise>; } /** An MCP App's transport: Views and operations through `call` (the host's `tools/call`); everything else is `unavailable`. */ export interface McpAppTransportOptions { /** The host's server tool call; leave it out when the host did not advertise `serverTools`, and the App is read-only. */ readonly call?: ((tool: string, args: Record) => Promise) | undefined; readonly host: AdminHost; } /** `run` of a refused or missing operation rejects with this: the caller cannot tell which. */ export declare class AdminUnavailableError extends Error { constructor(); } export interface ViewOptions { /** The size of each page; `loadMore()` appends the next one. */ readonly limit?: number; } export interface EntriesOptions { /** One field equal to one value, as the entries route filters. */ readonly filter?: { readonly field: keyof Data & string; readonly value: string; }; readonly search?: string; /** The size of each page; `loadMore()` appends the next one. */ readonly limit?: number; } /** The pages loaded so far, in order, and how to load the next. Changing the input starts again from the first page. */ export interface LoadMoreState { /** Another page follows the ones loaded. */ readonly hasMore: boolean; /** Loads and appends the next page. Does nothing when `hasMore` is false, or while any fetch is in flight, a refetch after a write included: that one is not cancelled. */ readonly loadMore: () => void; /** The next page, which this hook's `loadMore()` fetches, is loading; the rows already loaded stay. */ readonly loadingMore: boolean; } export interface ViewState extends LoadMoreState { readonly rows: readonly R[]; /** `error` after a failed `loadMore()` too: the rows already loaded stay, and `loadMore()` retries. */ readonly status: ReadStatus; } export interface EntriesState extends LoadMoreState { readonly items: readonly AdminEntry[]; /** `error` after a failed `loadMore()` too: the items already loaded stay, and `loadMore()` retries. */ readonly status: ReadStatus; } export interface EntryState { readonly entry: AdminEntry | undefined; readonly status: ReadStatus; } export interface TotalState { readonly total: number | undefined; readonly status: ReadStatus; } export interface MeState { readonly me: AdminMe | undefined; readonly status: ReadStatus; } export interface OperationState { /** Undefined where the surface cannot write. Rejects with `AdminUnavailableError` when refused or missing. */ readonly run: ((input: I) => Promise) | undefined; readonly pending: boolean; } /** * Admin's data hooks, typed by the application's generated `AdminTypes`; `generated/admin-client.ts` exports them bound. * Every hook reads only the caller-authorized `/admin/api` routes in the browser (or an MCP tool in a host) and shares * Admin's query cache. None takes a caller, a role or a system flag; a refusal reads as `unavailable`. * * Surface: `useView` and `useOperation` are `both`. `useEntries`, `useEntry`, `useCollectionTotal` and `useMe` are * `browser` and read `unavailable` in an MCP host: declare the data an App needs as an admin View. */ export interface AdminHooks { /** `GET /admin/api/views/{name}`, or the View's admin MCP tool in a host. All pages loaded so far, appended in order: `loadMore()` fetches the next. */ useView(name: N, input: T["views"][N]["input"], options?: ViewOptions): ViewState; /** `GET /admin/api/entries`, appended page by page like `useView`. Browser only. */ useEntries(collection: N, options?: EntriesOptions): EntriesState; /** `GET /admin/api/entries/{id}`. Browser only. */ useEntry(collection: N, id: string): EntryState; /** `POST /admin/api/operations/{name}`, or the operation's admin MCP tool in a host. A success refreshes Views, entries and totals. */ useOperation(name: N): OperationState; /** The `total` of `GET /admin/api/collections/{name}/statistics`. Browser only. */ useCollectionTotal(collection: keyof T["collections"] & string): TotalState; /** `GET /admin/api/me`. Browser only. */ useMe(): MeState; /** * The counts of `refs` in one `GET /admin/api/view-batch`, in the order given: each is `field` of the first row of an aggregate * admin View, run as the caller. Cached 30 s, refreshed on window focus and after a mutation, never polled; keyed by the signed-in * user. Reads `unavailable` in an MCP host. Reads only the first `MAX_BADGE_VIEWS` distinct Views. */ useBadges(refs: readonly AdminBadge[]): readonly BadgeState[]; } /** The hooks typed by `T`; the types are all that differs, so every call returns the same functions. */ export declare function createAdminHooks(): AdminHooks; /** Untyped hooks for code that has no generated types, such as a component ejected from the default Admin. */ export declare const useView: AdminHooks["useView"]; export declare const useEntries: AdminHooks["useEntries"]; export declare const useEntry: AdminHooks["useEntry"]; export declare const useOperation: AdminHooks["useOperation"]; export declare const useCollectionTotal: AdminHooks["useCollectionTotal"]; export declare const useMe: AdminHooks["useMe"]; export declare const useBadges: AdminHooks["useBadges"]; /** The MCP host's API, or null in the browser. Handle the null: a component that renders in both surfaces must work in both. */ export declare function useHost(): AdminHost | null; /** Where this component renders. Branching on it is allowed; both branches must render. */ export declare function useAdminSurface(): AdminSurface; /** The transport for an MCP App; `@aotter/mantle-admin/hooks` is the only place an App's `tools/call` is wired. */ export declare const mcpAppTransport: (options: McpAppTransportOptions) => AdminTransport;