Skip to content

API: Configuration, tenancy and themes

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/define_config.ts

Source

/** Attribute names (model properties, not DB columns) read on the host user model. */
export type AuthColumns = {
    name: string;
    email: string;
    password: string;
    avatar: string;
    emailVerifiedAt: string;
    timezone: string;
    locale: string;
    currentTenantId: string;
    mfaSecret: string;
    mfaRecoveryCodes: string;
    mfaEnabledAt: string;
    socialProvider?: string;
    socialProviderId?: string;
};

export interface PupitreConfig {
    cache: {
        dev: boolean;
    };
    timezone: string;
    i18n?: {
        fallbackLocale?: string;
        messages?: LocaleMessages;
    };
    storage: {
        disk: string;
    };
    policies?: () => Promise<Record<string, unknown>>;
    auth: {
        /** Host user model used by panel auth pages; required when any auth page is enabled. */
        model?: () => Promise<{
            default: LucidModel;
        }>;
        columns: AuthColumns;
        /** Password reset token lifetime in minutes. */
        passwordResetTtl: number;
        passkeys?: {
            rpId?: string;
            origin?: string;
            rpName?: string;
        };
    };
    mail?: Lazy<MailAdapter>;
    notifications?: Lazy<NotificationsAdapter>;
    jobs?: Lazy<JobsAdapter>;
    audit?: Lazy<AuditAdapter>;
    realtime?: Lazy<RealtimeAdapter>;
    exports?: {
        maxSyncRows?: number;
        maxAsyncRows?: number;
        retentionMinutes?: number;
    };
}

export type PupitreConfigInput = Partial<Pick<PupitreConfig, 'timezone' | 'policies' | 'mail' | 'notifications' | 'jobs' | 'exports' | 'audit' | 'i18n'>> & {
    realtime?: PupitreConfig['realtime'];
    cache?: Partial<PupitreConfig['cache']>;
    storage?: Partial<PupitreConfig['storage']>;
    auth?: Partial<Omit<PupitreConfig['auth'], 'columns'>> & {
        columns?: Partial<AuthColumns>;
    };
};

/** Creates the resolved Pupitre configuration with stable defaults. */
export function defineConfig(config: PupitreConfigInput = {});

/** Resolves a lazily configured adapter (`value` or `() => Promise<value>`). */
export async function resolveAdapter<T extends object>(value: Lazy<T> | undefined): Promise<T | undefined>;

src/definition/tenancy.ts

Source

/** Serializable tenant identity exposed to controllers and the client. */
export type TenantSummary = {
    id: string | number;
    name: string;
    slug?: string;
};

/** Resolved tenant for the current request. `record` is the host model instance when available. */
export type ResolvedTenant = TenantSummary & {
    record?: LucidRow | object;
};

/**
 * Tenancy adapter (DESIGN §Adapters). Implementations: default column driver
 * (`src/tenancy/column_driver.ts`) and `@rikology/pupitre/drivers/tenant`.
 */
export interface TenancyAdapter {
    /** Resolves the tenant for the request; `null` means "not found / not a member". */
    resolve(ctx: HttpContext): Promise<ResolvedTenant | null>;
    /** Restricts a resource query to the tenant. */
    scope(query: ModelQueryBuilderContract<LucidModel>, tenant: ResolvedTenant, options: {
        ownership: string;
        model: LucidModel;
        ctx: HttpContext;
    }): void;
    /** Fills ownership on create data (before persistence). */
    fill(data: Record<string, unknown>, tenant: ResolvedTenant, options: {
        ownership: string;
        model: LucidModel;
        ctx: HttpContext;
    }): Record<string, unknown>;
    /** Tenants the user can switch to. */
    listForUser(ctx: HttpContext): Promise<TenantSummary[]>;
    /** Implicit mode only: persists the user's current tenant. */
    switch?(ctx: HttpContext, tenantId: string | number): Promise<boolean>;
    /** Optional execution context wrapper used by tenant-aware optional peers. */
    run?(tenant: ResolvedTenant, next: () => Promise<void>): Promise<void>;
}

export type TenancyMode = 'url' | 'implicit';

export type TenancyDefinition = {
    mode: TenancyMode;
    /** Lazy tenant model (default column driver). */
    model?: () => Promise<{
        default: LucidModel;
    }>;
    /** Ownership column on resource tables; default `tenant_id`. */
    ownership: string;
    /** Tenant attribute used in URLs (mode `url`); default `slug`. */
    slugAttribute: string;
    /** Tenant attribute used as display name; default `name`. */
    titleAttribute: string;
    /** Show the tenant switcher. */
    menu: boolean;
    /** Custom adapter; defaults to the column driver. */
    adapter?: TenancyAdapter | (() => Promise<TenancyAdapter>);
    /** Default column driver: tenants the user belongs to. */
    tenantsForUser?: (user: LucidRow, ctx: HttpContext) => Promise<readonly LucidRow[]>;
    /** Implicit mode: user attribute holding the current tenant id; default `currentTenantId`. */
    currentTenantAttribute: string;
};

src/definition/theme.ts

Source

export type ThemeDefinition = {
    font?: {
        family: string;
        url?: string;
    };
    density?: 'compact' | 'comfortable';
    sidebar?: 'expanded' | 'collapsed' | 'top-nav';
    sidebarCollapsibleOnDesktop?: boolean;
    contentWidth?: 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | '7xl' | 'full' | 'screen';
    colors?: Record<string, string>;
    radius?: string;
    vars?: Record<string, string>;
    stylesheet?: string;
    darkMode?: 'light' | 'dark' | 'system';
    /** Legacy primary color accepted by panel.theme(). */
    primary?: string;
};

/** Creates a validated, immutable-friendly panel theme definition. */
export function defineTheme<T extends ThemeDefinition>(theme: T): T;

src/adapters/mail.ts

Source

/** Outgoing email produced by Pupitre (password reset, verification). */
export type PupitreMailMessage = {
    to: string;
    subject: string;
    html: string;
    text: string;
};

/**
 * Mail adapter (DESIGN §Adapters). Official driver over `@adonisjs/mail`:
 * `@rikology/pupitre/drivers/mail`.
 */
export interface MailAdapter {
    send(message: PupitreMailMessage): Promise<void>;
}

src/adapters/jobs.ts

Source

/** Jobs integration used by asynchronous actions and exports. */
export interface JobsAdapter {
    dispatch(job: string, payload: Record<string, unknown>): Promise<void>;
    register(job: string, handler: (payload: Record<string, unknown>) => Promise<void>): void;
    initialize?(): Promise<void>;
}

src/adapters/notifications.ts

Source

/** A database notification as shown in the panel tray. */
export type PupitreNotificationItem = {
    id: string;
    type: 'success' | 'error' | 'warning' | 'info';
    title: string;
    body?: string;
    url?: string;
    actions?: NotificationAction[];
    readAt: string | null;
    createdAt: string;
};

export type PupitreNotificationMessage = Omit<PupitreNotificationItem, 'id' | 'readAt' | 'createdAt'>;

/**
 * Notifications adapter (DESIGN §Adapters). Official driver over
 * `@rikology/adonisjs-notifications`: `@rikology/pupitre/drivers/notifications`.
 * Without an adapter only flash messages work.
 */
export interface NotificationsAdapter {
    toInbox(user: LucidRow, message: PupitreNotificationMessage, ctx?: HttpContext): Promise<void>;
    inbox(user: LucidRow, options: {
        page: number;
        perPage: number;
    }, ctx?: HttpContext): Promise<{
        items: PupitreNotificationItem[];
        meta: {
            total: number;
            perPage: number;
            currentPage: number;
            lastPage: number;
        };
    }>;
    unreadCount(user: LucidRow, ctx?: HttpContext): Promise<number>;
    markRead(user: LucidRow, ids: string[] | 'all', ctx?: HttpContext): Promise<void>;
}

src/adapters/realtime.ts

Source

/** Transport-neutral ping publisher. Channel authorization belongs to the adapter. */
export interface RealtimeAdapter {
    driver: string;
    publish(channel: string, event: RealtimeEvent): Promise<void>;
    initialize?(manager: PupitreManager): void | Promise<void>;
}

src/adapters/audit.ts

Source

export type AuditChange = {
    field: string;
    old: unknown;
    new: unknown;
};

export type AuditEntry = {
    id: string;
    event: string;
    auditableType: string | null;
    auditableId: string | null;
    actor: {
        type: string;
        id: string | null;
        label?: string;
    } | null;
    changes: AuditChange[];
    tags: string[];
    createdAt: string;
};

export type AuditPagination = {
    page?: number;
    perPage?: number;
};

export type AuditRecordOptions = AuditPagination & {
    tenantId?: string;
};

export type AuditPage = {
    items: AuditEntry[];
    meta: {
        total: number;
        perPage: number;
        currentPage: number;
        lastPage: number;
    };
};

/** Hosts own audit capture, storage, retention and redaction. Pupitre only reads it. */
export interface AuditAdapter {
    forRecord(model: LucidModel, id: string | number, options?: AuditRecordOptions): Promise<AuditPage>;
}
Navigation

Type to search…

↑↓ navigate↵ selectEsc close