import type * as React from "react"; import type { AdminLayers, SlotComponent } from "./slots.js"; import type { BrandProps, CellProps, CollectionListProps, HeaderProps, HomeProps, InputProps, ListActionsProps, RecordActionsProps, RecordAsideProps, RecordViewProps, ShellProps, SidebarProps, SurfaceProp } from "./slot-props.js"; /** * What `generated/admin-client.ts` knows about an application, as types: the input and row of each admin View, the * `data` of each collection's entries, and the input and output of each admin operation. The built-in entry has no * generated types, so its names are any string and its shapes are open. */ export interface AdminTypes { readonly views: { readonly [name: string]: { readonly input: object; readonly row: object; /** `true` when the View is an admin MCP tool, null when it is not; absent for the built-in entry. */ readonly mcp?: true | null; }; }; readonly collections: { readonly [name: string]: { readonly data: object; }; }; readonly operations: { readonly [name: string]: { readonly input: object; readonly output: unknown; }; }; /** The sidebar groups beyond Admin's own that the manifest places items in (`uiSchema.nav.group`), which `defineAdmin` must declare in `nav.groups`; any name when absent. */ readonly navGroups?: string; } type CustomGroup = T extends { readonly navGroups: infer G extends string; } ? G : string; type RequiredGroup = T extends { readonly navGroups: infer G extends string; } ? G : never; /** * Generated Admin metadata for `defineAdmin`: the column names each View's rows carry in the browser where they differ from * the SQL output names an MCP tool returns, so an MCP App's rows match the browser's. It names columns only; it grants and * denies nothing. The built-in entry has none. */ export interface AdminMeta { readonly rowNames?: { readonly [N in keyof T["views"]]?: { readonly [column: string]: string; }; }; } /** A component for a slot that renders in the browser only. */ type Browser

= SlotComponent

; /** A component for a slot that renders in the browser and in an MCP host: its props carry `surface`. */ type Both

= SlotComponent; /** A record/collection slot keeps its explicit surface, but is mounted only in browser Admin. */ type BrowserRecord

= SlotComponent; type Name = K & string; type Collections = Name; type Views = Name; /** The keys of an object type; any name for the open shape the built-in entry has. */ type Keys = [keyof O] extends [never] ? string : Name; /** `.` for every field of every collection. */ type FieldKey = { [C in Collections]: `${C}.${Keys}`; }[Collections]; /** `.` for every column of every admin View. */ type ColumnKey = { [V in Views]: `${V}.${Keys}`; }[Views]; type DataOf = C extends keyof T["collections"] ? T["collections"][C]["data"] : never; type FieldOf = K extends `${infer C}.${infer F}` ? Member, F> : never; /** A member's type; `unknown` for the open shape the built-in entry has. */ type Member = [keyof O] extends [never] ? unknown : F extends keyof O ? O[F] : never; type RowOf = V extends keyof T["views"] ? T["views"][V]["row"] : never; type ColumnOf = K extends `${infer V}.${infer F}` ? Member, F> : never; /** What a collection list passes as `row`: the columns it shows, `null` where empty. */ type ListRow = unknown extends D ? Readonly> : { readonly [P in keyof D]?: D[P] | null; }; /** The Views an MCP host can open: those with an admin MCP tool. Any View name for the open shape the built-in entry has. */ type McpViews = string extends Views ? string : { [V in Views]: T["views"][V] extends { readonly mcp: true; } ? V : never; }[Views]; type Of = K extends `${Prefix}${infer Rest}` ? Rest : never; /** * The regions of Admin an application can replace or wrap. Each component receives the props of the built-in one and * `renderDefault(props)`, which renders what it replaces. Slot names and props are versioned with the package; a misspelt * name fails `tsc`. In a key, `` is a collection's name, `` one of its fields (names contain no `.`), and * `.` a column of an admin View. * * Surface: `Shell`, `Sidebar`, `Header`, `Brand`, `Home`, `CollectionList` and `list.actions:` exist in the browser only. * `cell:view:` renders in both surfaces. `RecordView`, `record.aside:`, `record.actions:`, collection cells and * record inputs render only in browser Admin; their `surface` is the literal `"browser"`. An MCP row-action form * edits a Procedure's input, not a collection record, and never mounts record/input overrides. */ export type AdminComponents = { readonly Shell?: Browser; readonly Sidebar?: Browser; readonly Header?: Browser; readonly Brand?: Browser; readonly Home?: Browser; readonly CollectionList?: Browser; readonly RecordView?: BrowserRecord; } & { readonly [K in `record.aside:${Collections}`]?: BrowserRecord>>>; } & { readonly [K in `record.actions:${Collections}`]?: BrowserRecord>>>; } & { readonly [K in `list.actions:${Collections}`]?: Browser; } & { readonly [K in `cell:${FieldKey}`]?: BrowserRecord>, ListRow extends `${infer C}.${string}` ? C : never>>>>; } & { readonly [K in `cell:view:${ColumnKey}`]?: Both>, RowOf extends `${infer V}.${string}` ? V : never>>>; } & { readonly [K in `input:${FieldKey}`]?: BrowserRecord>>>; }; /** A reusable fragment of `defineAdmin`'s `components`, such as an audit trail panel. */ export interface AdminPlugin { readonly components?: AdminComponents; } /** * The remote origins Admin's MCP App may load from, as the host's CSP (`_meta.ui.csp`). `mantle bundle` refuses an App that * references a remote origin none of these names. The build writes it into the file, and `adminMcpApps` sends it as `_meta.ui.csp` when * a host reads the App, so it is declared once. */ export interface AdminMcpCsp { readonly connectDomains?: readonly string[]; readonly resourceDomains?: readonly string[]; readonly frameDomains?: readonly string[]; readonly baseUriDomains?: readonly string[]; } /** Text, or a `{ locale: text }` map. */ export type AdminText = string | Readonly>; /** Admin's own sidebar groups; project groups sort among the first three by `order` (content 10, operations 20, reports 30) and `more` is always last. */ export type BuiltInNavGroup = "content" | "operations" | "reports" | "more"; /** A project sidebar group: its title and its place among the others (default 50). */ export interface AdminNavGroup { readonly title: AdminText; readonly order?: number; } type Scalar = string | number | boolean; type ScalarOf = Extract, Scalar>; /** One declared field of a collection and the value to list it by: the list filters on a single field. Any field name for the open shape the built-in entry has. */ type NavFilter = [keyof D] extends [never] ? { readonly [field: string]: Scalar; } : { [F in keyof D & string]-?: [ScalarOf] extends [never] ? never : { readonly [P in F]: ScalarOf; } & { readonly [Q in Exclude]?: never; }; }[keyof D & string]; /** Where a sidebar item leads: a collection's list, preset to one field's value, or an admin View with its input. */ export type AdminNavTarget = { [C in Collections]: { readonly collection: C; readonly filter?: NavFilter; }; }[Collections] | { [V in Views]: { readonly view: V; readonly input?: T["views"][V]["input"]; }; }[Views]; /** A count shown on a sidebar item: `field` of the one row an aggregate admin View returns, typically one with `uiSchema.nav.hidden: true`. */ export type AdminBadge = { [V in Views]: { readonly view: V; readonly field: [keyof T["views"][V]["row"]] extends [never] ? string : keyof T["views"][V]["row"] & string; }; }[Views]; /** * A sidebar entry added beside the ones `uiSchema.nav` produces. Several items may lead to one resource with different * presets. An item whose collection or View the caller cannot see is not shown; showing or hiding one grants and denies nothing. */ export interface AdminNavItem { readonly group: BuiltInNavGroup | CustomGroup; readonly title: AdminText; /** Any icon component; the default is a folder for a collection and a chart for a View. */ readonly icon?: React.ElementType; /** Position within the group; items without one follow those with one. */ readonly order?: number; readonly to: AdminNavTarget; /** A count beside the title, read as the caller through the View's own `requires`; refused or missing shows nothing. Surface: browser. */ readonly badge?: AdminBadge; } /** Explicitly compose a custom page's native heading in browser Admin and its opted-in MCP App. */ export interface AdminPageHeader { readonly description?: AdminText; /** React content rendered beside the title; use hooks and host-aware links in an MCP page. */ readonly actions?: React.ReactNode; } /** * A page of the application's own, inside Admin's shell at `/admin/p/`. `nav` places it in the sidebar beside the items * `uiSchema.nav` produces, in the same groups as `nav.items`. Showing a page grants nothing: what it reads or writes is * authorized on the server, by the View or Procedure it calls. * * With `mcp`, the page is also an MCP App view. It then renders in a host, so it may use only what works there: the `both` hooks * (`useView`, `useOperation`) and components, `` and `useOpenLink()` for links, and no particular width. `mantle bundle` * checks the built App for what a host refuses. */ export interface AdminPage { /** One segment of lowercase letters, digits and hyphens, unique among the pages; a page that breaks this is skipped with a console error. */ readonly path: string; readonly title: AdminText; readonly element: React.ReactNode; /** * An object renders the same native title/description/actions on both surfaces; `false` lets `element` own its heading. * Omitted preserves the browser's native title and the MCP App's element-only presentation. Do not add another h1 * inside `element` when using the native heading. Description, like title, can be localized. */ readonly header?: AdminPageHeader | false; /** Any icon component; the default is a page. */ readonly icon?: React.ElementType; /** Without `nav` the page keeps its route and stays out of the sidebar. */ readonly nav?: { readonly group: BuiltInNavGroup | CustomGroup; readonly order?: number; }; /** * Also the page of an MCP App: when the host called this admin View's tool, the App shows `element` instead of the View's rows, * in the `mcp-app` surface (browser-only hooks read `unavailable`, links go through the host). `renders` names an admin View that * has an MCP tool; `tsc` refuses any other name. The page reads its data through the hooks, so in a host without server tools it * shows what its components make of `unavailable`. When several pages name one View, the first is shown. */ readonly mcp?: { readonly renders: McpViews; }; } /** Declare a project group for every `uiSchema.nav.group` the manifest uses; `tsc` refuses a missing one, and refuses Admin's own group ids. */ export type AdminNavGroups = { readonly [K in RequiredGroup]: AdminNavGroup; } & { readonly [id: string]: AdminNavGroup; } & { readonly [K in BuiltInNavGroup]?: never; }; /** `defineAdmin`'s `nav`. `groups` is required when the manifest uses a group Admin does not have. */ export interface AdminNav { readonly groups?: AdminNavGroups; readonly items?: readonly AdminNavItem[]; } /** Whether `defineAdmin` must be given `nav.groups`: not when the manifest needs none (the built-in entry's open shape too). */ type NeedsGroups = [RequiredGroup] extends [never] ? false : true; /** What `defineAdmin` takes beside the generated metadata. */ export type AdminConfig = AdminOptions & (NeedsGroups extends true ? { readonly nav: AdminNav & { readonly groups: AdminNavGroups; }; } : unknown); /** The options of `AdminConfig`, all optional. */ export interface AdminOptions { /** The sidebar: groups the manifest places items in, and items of the application's own. `uiSchema.nav` stays the default. */ readonly nav?: AdminNav; /** Pages of the application's own, in Admin's shell. */ readonly pages?: readonly AdminPage[]; /** Replace or wrap regions of Admin; applied after `plugins`, so these are outermost. */ readonly components?: AdminComponents; /** Fragments applied in order: each one's `renderDefault` is the plugin before it, or the built-in component. */ readonly plugins?: readonly AdminPlugin[]; /** * Where `/admin` leads: a collection (with an optional preset filter), an admin View, or one of `pages` by its `path`. * Without it, or when the caller cannot see the target, `/admin` is Admin's own home; with it, that home moves to * `/admin/setup`, listed under More. Surface: browser. Showing or skipping the landing grants and denies nothing. */ readonly home?: AdminNavTarget | { readonly page: string; }; /** What the Admin MCP App may load, for a host's CSP. */ readonly mcp?: { readonly csp?: AdminMcpCsp; }; } /** What an Admin entry default-exports and `` renders. */ export interface AdminDefinition { readonly meta: AdminMeta; /** The sidebar groups and items, as `defineAdmin` was given them. */ readonly nav: { readonly groups: { readonly [id: string]: AdminNavGroup; }; readonly items: readonly AdminNavItem[]; }; /** The application's own pages, as `defineAdmin` was given them. */ readonly pages: readonly AdminPage[]; /** Where `/admin` leads, as `defineAdmin` was given it. */ readonly home?: AdminNavTarget | { readonly page: string; }; /** The components of each slot, nearest the built-in first. */ readonly layers: AdminLayers; readonly mcp?: { readonly csp?: AdminMcpCsp; }; } /** * Compose Admin from generated metadata. The result is plain data; it grants and denies nothing: showing, hiding or * replacing a region changes no server-side permission. */ export declare function defineAdmin(meta: AdminMeta, ...[config]: NeedsGroups extends true ? [config: AdminConfig>] : [config?: AdminConfig>]): AdminDefinition; export {};