Skip to content

API: Panels and navigation

Public TypeScript declarations verified against the package export entries.

This reference is generated before development, checking and building from declarations reachable through the package’s published entry points, following re-exports. Internal source-module exports are omitted. Types shown together share their source module; follow the source link for imports and implementation. Use the guides for examples and runtime behavior.

src/builder/panel.ts

Source

export type TenancyInput = Partial<Omit<TenancyDefinition, 'mode'>> & {
    mode: TenancyMode;
};

export type PanelBuilder = {
    readonly definition: PanelDefinition;
    impersonation(options?: ImpersonationOptions): PanelBuilder;
    strictAuthorization(enabled?: boolean): PanelBuilder;
    environmentIndicator(options?: EnvironmentIndicatorOptions): PanelBuilder;
    path(value: string): PanelBuilder;
    domain(value: string): PanelBuilder;
    guard(value: string): PanelBuilder;
    middleware(value: PanelDefinition['middleware']): PanelBuilder;
    resources(value: readonly unknown[] | Record<string, unknown>): PanelBuilder;
    pages(value: readonly unknown[] | Record<string, unknown>): PanelBuilder;
    brand(value: NonNullable<PanelDefinition['brand']>): PanelBuilder;
    theme(value: PanelThemeDefinition): PanelBuilder;
    timezone(value: string): PanelBuilder;
    locale(value: string): PanelBuilder;
    locales(value: readonly string[]): PanelBuilder;
    breadcrumbs(value: boolean): PanelBuilder;
    /** Serve the panel login/logout pages; `false` keeps the host's own auth. */
    login(enabled?: boolean): PanelBuilder;
    registration(enabled?: boolean): PanelBuilder;
    passwordReset(enabled?: boolean): PanelBuilder;
    emailVerification(enabled?: boolean): PanelBuilder;
    mfa(options?: {
        required?: boolean;
        issuer?: string;
    } | false): PanelBuilder;
    passkeys(enabled?: boolean): PanelBuilder;
    profile(options?: boolean | {
        avatar?: boolean;
    }): PanelBuilder;
    sessions(options?: {
        history?: boolean;
        retentionDays?: number;
    }): PanelBuilder;
    social(providers: string[] | {
        providers: string[];
        registration?: boolean;
    }): PanelBuilder;
    navigation(entries: readonly Definable<NavEntryDefinition>[]): PanelBuilder;
    userMenu(items: readonly UserMenuItemDefinition[]): PanelBuilder;
    tenancy(options: TenancyInput | false): PanelBuilder;
    notifications(options?: {
        interval?: number;
    } | false): PanelBuilder;
    globalSearch(options?: {
        keyBinding?: string;
    } | false): PanelBuilder;
    widgets(value: readonly unknown[] | Record<string, unknown>): PanelBuilder;
    filters(value: readonly WidgetFilterDefinition[]): PanelBuilder;
    /** Plugins applied in order at boot (`definePlugin`). Replaces earlier `.plugins()` calls. */
    plugins(value: readonly PupitrePlugin[]): PanelBuilder;
    toDefinition(): PanelDefinition;
    toJSON(): string;
};

export function definePanel(id: string): PanelBuilder;

src/builder/nav.ts

Source

export type NavItemBuilder = {
    readonly definition: NavItemDefinition;
    resource(slug: string): NavItemBuilder;
    page(slug: string): NavItemBuilder;
    url(url: string): NavItemBuilder;
    external(enabled?: boolean): NavItemBuilder;
    icon(value: string): NavItemBuilder;
    sort(value: number): NavItemBuilder;
    badge(value: Badge): NavItemBuilder;
    visible(value: Visible): NavItemBuilder;
    toDefinition(): NavItemDefinition;
};

export type NavGroupBuilder = {
    readonly definition: NavGroupDefinition;
    items(values: readonly (string | Definable<NavItemDefinition>)[]): NavGroupBuilder;
    icon(value: string): NavGroupBuilder;
    sort(value: number): NavGroupBuilder;
    collapsible(collapsed?: boolean): NavGroupBuilder;
    toDefinition(): NavGroupDefinition;
};

src/definition/panel.ts

Source

/** Built-in auth pages served by the panel (WP17). All default to disabled. */
export type PanelAuthDefinition = {
    login: boolean;
    registration: boolean;
    passwordReset: boolean;
    emailVerification: boolean;
    profile: {
        enabled: boolean;
        avatar: boolean;
    };
    mfa?: PanelMfaDefinition;
    passkeys?: PanelPasskeysDefinition;
};

export type PanelThemeDefinition = ThemeDefinition;

export type EnvironmentIndicatorOptions = {
    visible?: boolean | ((ctx: HttpContext) => boolean | Promise<boolean>);
    label?: string;
    color?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'gray';
};

export type ImpersonationOptions = {
    canImpersonate?: (actor: LucidRow, ctx: HttpContext) => boolean | Promise<boolean>;
    canBeImpersonated?: (target: LucidRow, ctx: HttpContext) => boolean | Promise<boolean>;
    redirectTo?: string;
};

export type PanelDefinition = {
    id: string;
    impersonation?: ImpersonationOptions;
    strictAuthorization?: boolean;
    environmentIndicator?: EnvironmentIndicatorOptions;
    path: string;
    brand?: {
        name: string;
        logo?: string;
        logoDark?: string;
        favicon?: string;
    };
    /** Route-group domain, e.g. `admin.example.com` or `:tenant.example.com`. */
    domain?: string;
    guard?: string;
    middleware: readonly (MiddlewareFn | ParsedNamedMiddleware)[];
    resources: readonly unknown[] | Record<string, unknown>;
    /** Custom pages (`definePage`), same shape as `resources` (array or lazy barrel). */
    pages: readonly unknown[] | Record<string, unknown>;
    /** Dashboard widget definitions registered on this panel. */
    widgets?: readonly unknown[] | Record<string, unknown>;
    dashboardFilters?: readonly WidgetFilterDefinition[];
    theme?: PanelThemeDefinition;
    timezone?: string;
    locale?: string;
    locales?: readonly string[];
    breadcrumbs: boolean;
    auth: PanelAuthDefinition;
    sessions?: {
        history: boolean;
        retentionDays: number;
    };
    social?: PanelSocialDefinition;
    /** Explicit navigation; when absent, nav is derived from resources/pages. */
    navigation?: NavEntryDefinition[];
    /**
     * Navigation contributed by plugins. Merged into `navigation` when the panel
     * declares one; otherwise rendered after the derived navigation, and the
     * resources/pages it references are not derived a second time.
     */
    pluginNavigation?: NavEntryDefinition[];
    /** Plugins in application order (WP36); applied by the manager at boot. */
    plugins: readonly PupitrePlugin[];
    userMenu: UserMenuItemDefinition[];
    tenancy?: TenancyDefinition;
    /** Database notifications tray + polling interval (ms); `null` disables. */
    notifications: {
        interval: number;
    } | null;
    /** Global search; `null` disables. */
    globalSearch: {
        keyBinding: string;
    } | null;
    toJSON(): string;
};

src/definition/navigation.ts

Source

/**
 * One navigation entry. `resource` / `page` reference a registered resource
 * or custom page by slug; `url` is a literal link.
 */
export type NavItemDefinition = {
    type: 'item';
    label?: string;
    resource?: string;
    page?: string;
    url?: string;
    icon?: string;
    external?: boolean;
    sort?: number;
    badge?: string | number | NavClosure<string | number | null | undefined>;
    visible?: boolean | NavClosure<boolean>;
};

export type NavGroupDefinition = {
    type: 'group';
    label: string;
    icon?: string;
    sort?: number;
    collapsible: boolean;
    collapsed: boolean;
    items: NavItemDefinition[];
};

export type NavEntryDefinition = NavGroupDefinition | NavItemDefinition;

/** Entry of the user menu in the topbar (in addition to built-in profile/logout). */
export type UserMenuItemDefinition = {
    label: string;
    url: string;
    icon?: string;
    method?: 'get' | 'post';
    external?: boolean;
    sort?: number;
    visible?: boolean | NavClosure<boolean>;
};
Navigation

Type to search…

↑↓ navigate↵ selectEsc close