tempest-express-sdk 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{chunk-7Q5NBMSP.js → chunk-6KJTKSG5.js} +3 -3
- package/dist/chunk-6KJTKSG5.js.map +1 -0
- package/dist/cli.cjs +2 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +3547 -222
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1252 -32
- package/dist/index.d.ts +1252 -32
- package/dist/index.js +3513 -218
- package/dist/index.js.map +1 -1
- package/package.json +20 -8
- package/dist/chunk-7Q5NBMSP.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export { z } from 'zod';
|
|
3
3
|
import * as tempest_db_js from 'tempest-db-js';
|
|
4
|
-
import { Model, ModelClass, BaseRepository, WhereInput, InferModel, InferInsert, PaginationFilter as PaginationFilter$1, PaginationResult, AsyncDriver,
|
|
4
|
+
import { Model, ModelClass, BaseRepository, WhereInput, InferModel, InferInsert, PaginationFilter as PaginationFilter$1, PaginationResult, AsyncDriver, Column, AsyncSession, AsyncEngine } from 'tempest-db-js';
|
|
5
5
|
export { AsyncDriver, AsyncEngine, AsyncResult, AsyncSession, BaseRepository, BelongsTo, ColType, Column, ColumnFlags, CompiledQuery, CondNode, Condition, DeleteBuilder, DeleteNode, Dialect, EngineOptions, Executable, HasMany, InferInsert, InferModel, InsertBuilder, InsertNode, Model, ModelClass, NoResultError, NodeSqliteDriver, Operator, OrderTerm, PaginationResult, ParsedDatabaseUrl, PostgresDialect, QueryNode, RecordNotFound, Relation, RelationValue, PaginationFilter as RepositoryPaginationFilter, Returning, RowOf, SelectBuilder, SelectNode, SortDirection, SqliteDialect, SyncEngine, SyncSession, UpdateBuilder, UpdateNode, WhereArg, WhereInput, WithRelations, and, belongsTo, column, columnsOf, createEngine, createSyncEngine, del, detectDialect, getDialect, hasMany, insert, join, loadRelations, not, or, parseDatabaseUrl, select, sql, update } from 'tempest-db-js';
|
|
6
6
|
import { Request, Response as Response$1, RequestHandler, Router, ErrorRequestHandler, Express } from 'express';
|
|
7
7
|
import * as ws from 'ws';
|
|
@@ -3493,17 +3493,1151 @@ declare class MessagingHub {
|
|
|
3493
3493
|
}
|
|
3494
3494
|
|
|
3495
3495
|
/**
|
|
3496
|
-
*
|
|
3496
|
+
* Column introspection for the admin panel, mirroring `admin.forms`' widget
|
|
3497
|
+
* derivation.
|
|
3497
3498
|
*
|
|
3498
|
-
*
|
|
3499
|
-
*
|
|
3500
|
-
*
|
|
3501
|
-
*
|
|
3502
|
-
*
|
|
3499
|
+
* `tempest-db-js` keeps rich runtime metadata on every column — the canonical
|
|
3500
|
+
* type kind, enum members, `varchar` length, the not-null/default/primary-key
|
|
3501
|
+
* flags and the foreign-key reference — so the admin derives its form widgets
|
|
3502
|
+
* and list filters from the model itself instead of asking the project to
|
|
3503
|
+
* restate them. Kept separate from the router so the (fiddly) type handling is
|
|
3504
|
+
* unit-testable in isolation.
|
|
3505
|
+
*/
|
|
3506
|
+
|
|
3507
|
+
/** The set of form controls the admin knows how to render. */
|
|
3508
|
+
type AdminWidget = "text" | "textarea" | "number" | "checkbox" | "datetime" | "date" | "time" | "select" | "json";
|
|
3509
|
+
/** A `(value, label)` pair for a `select` widget. */
|
|
3510
|
+
interface AdminSelectOption {
|
|
3511
|
+
value: string;
|
|
3512
|
+
label: string;
|
|
3513
|
+
}
|
|
3514
|
+
/** The widget a column maps to, plus the attributes that render it. */
|
|
3515
|
+
interface WidgetSpec {
|
|
3516
|
+
/** The control to render. */
|
|
3517
|
+
widget: AdminWidget;
|
|
3518
|
+
/** `step` attribute for `number` widgets, or `null`. */
|
|
3519
|
+
step: string | null;
|
|
3520
|
+
/** Options for `select` widgets (empty otherwise). */
|
|
3521
|
+
options: AdminSelectOption[];
|
|
3522
|
+
}
|
|
3523
|
+
/** How a column is surfaced in the list view's filter bar. */
|
|
3524
|
+
type AdminFilterKind = "select" | "daterange" | "text";
|
|
3525
|
+
/**
|
|
3526
|
+
* Humanize a column key into a form label (`lastLoginAt` → `Last Login At`).
|
|
3527
|
+
*
|
|
3528
|
+
* @param name - The column key.
|
|
3529
|
+
* @returns A title-cased label.
|
|
3530
|
+
*/
|
|
3531
|
+
declare function humanizeField(name: string): string;
|
|
3532
|
+
/**
|
|
3533
|
+
* Return every column of a model, keyed by field name in declaration order.
|
|
3534
|
+
*
|
|
3535
|
+
* @param model - The model class.
|
|
3536
|
+
* @returns The column map (do not mutate).
|
|
3537
|
+
*/
|
|
3538
|
+
declare function adminColumns(model: ModelClass): Record<string, Column<unknown>>;
|
|
3539
|
+
/**
|
|
3540
|
+
* Map a column to the widget that edits it.
|
|
3541
|
+
*
|
|
3542
|
+
* `json` is matched before anything else because a JSON column carries no
|
|
3543
|
+
* useful scalar type, and `enum` is matched before the string kinds so its
|
|
3544
|
+
* members become a dropdown rather than a free-text input.
|
|
3545
|
+
*
|
|
3546
|
+
* @param column - The column to inspect.
|
|
3547
|
+
* @returns The widget, its `number` step (or `null`) and its `select` options.
|
|
3548
|
+
*/
|
|
3549
|
+
declare function widgetForColumn(column: Column<unknown>): WidgetSpec;
|
|
3550
|
+
/**
|
|
3551
|
+
* Whether a column may be left blank on submit — it is nullable, carries a
|
|
3552
|
+
* default, or is the primary key the database fills in.
|
|
3553
|
+
*
|
|
3554
|
+
* @param column - The column to inspect.
|
|
3555
|
+
* @returns `true` when the field is optional.
|
|
3556
|
+
*/
|
|
3557
|
+
declare function isColumnOptional(column: Column<unknown>): boolean;
|
|
3558
|
+
/**
|
|
3559
|
+
* Map a column to the filter control the list view shows for it.
|
|
3560
|
+
*
|
|
3561
|
+
* Booleans and enums become dropdowns, date-like columns become a from/to pair
|
|
3562
|
+
* of date inputs, and anything else falls back to an equality text input.
|
|
3563
|
+
*
|
|
3564
|
+
* @param column - The column to inspect.
|
|
3565
|
+
* @returns The filter kind and, for `select`, its options.
|
|
3566
|
+
*/
|
|
3567
|
+
declare function filterForColumn(column: Column<unknown>): {
|
|
3568
|
+
kind: AdminFilterKind;
|
|
3569
|
+
options: AdminSelectOption[];
|
|
3570
|
+
};
|
|
3571
|
+
/**
|
|
3572
|
+
* Whether a column holds free text a `LIKE '%…%'` search can match.
|
|
3573
|
+
*
|
|
3574
|
+
* @param column - The column to inspect.
|
|
3575
|
+
* @returns `true` for `varchar` / `text` / `char` columns.
|
|
3576
|
+
*/
|
|
3577
|
+
declare function isSearchableColumn(column: Column<unknown>): boolean;
|
|
3578
|
+
|
|
3579
|
+
/**
|
|
3580
|
+
* Declarative admin configuration for one model, mirroring `admin.config`.
|
|
3581
|
+
*
|
|
3582
|
+
* Instantiate one {@link AdminModel} per managed model and hand it to
|
|
3583
|
+
* {@link AdminSite.register}. Unlike Django's class-based `ModelAdmin` this is
|
|
3584
|
+
* a plain typed instance — the constructor options are the contract, there is
|
|
3585
|
+
* no metaclass magic, and every default is derived from the model's own column
|
|
3586
|
+
* metadata so an unconfigured model is already browsable.
|
|
3587
|
+
*/
|
|
3588
|
+
|
|
3589
|
+
/** Configuration accepted by {@link AdminModel}. */
|
|
3590
|
+
interface AdminModelOptions<C extends ModelClass> {
|
|
3591
|
+
/** The model class to manage. */
|
|
3592
|
+
model: C;
|
|
3593
|
+
/** URL slug. Defaults to the model's `tablename`, so URLs track tables. */
|
|
3594
|
+
slug?: string;
|
|
3595
|
+
/** Columns shown in the list view. Defaults to every column but the password hash. */
|
|
3596
|
+
listDisplay?: readonly string[];
|
|
3597
|
+
/** Columns surfaced as filter controls above the list. */
|
|
3598
|
+
listFilter?: readonly string[];
|
|
3599
|
+
/** Text columns searched with `LIKE '%value%'` by the search box. */
|
|
3600
|
+
searchFields?: readonly string[];
|
|
3601
|
+
/** Columns shown but never editable in the create/edit form. */
|
|
3602
|
+
readonlyFields?: readonly string[];
|
|
3603
|
+
/** Default ordering: a column key, or `-column` for descending. */
|
|
3604
|
+
ordering?: string;
|
|
3605
|
+
/** Rows per page in the list view. Default `25`. */
|
|
3606
|
+
pageSize?: number;
|
|
3607
|
+
/** Column used to look one row up from the detail URL. Default `"id"`. */
|
|
3608
|
+
identityField?: string;
|
|
3609
|
+
/** Singular display name. Defaults to the humanized class name. */
|
|
3610
|
+
verboseName?: string;
|
|
3611
|
+
/** Plural display name. Defaults to `verboseName` + `"s"`. */
|
|
3612
|
+
verboseNamePlural?: string;
|
|
3613
|
+
/** Whether the create form + POST endpoint are exposed. Default `true`. */
|
|
3614
|
+
canCreate?: boolean;
|
|
3615
|
+
/** Whether the edit form + POST endpoint are exposed. Default `true`. */
|
|
3616
|
+
canEdit?: boolean;
|
|
3617
|
+
/** Whether the delete action is exposed. Default `true`. */
|
|
3618
|
+
canDelete?: boolean;
|
|
3619
|
+
}
|
|
3620
|
+
/**
|
|
3621
|
+
* The admin configuration for one model.
|
|
3622
|
+
*
|
|
3623
|
+
* ```ts
|
|
3624
|
+
* new AdminModel({
|
|
3625
|
+
* model: UserModel,
|
|
3626
|
+
* listDisplay: ["email", "isAdmin", "isActive", "createdAt"],
|
|
3627
|
+
* listFilter: ["isActive", "isAdmin"],
|
|
3628
|
+
* searchFields: ["email"],
|
|
3629
|
+
* ordering: "-createdAt",
|
|
3630
|
+
* });
|
|
3631
|
+
* ```
|
|
3632
|
+
*/
|
|
3633
|
+
declare class AdminModel<C extends ModelClass = ModelClass> {
|
|
3634
|
+
/** The managed model class. */
|
|
3635
|
+
readonly model: C;
|
|
3636
|
+
/** Columns surfaced as filter controls. */
|
|
3637
|
+
readonly listFilter: string[];
|
|
3638
|
+
/** Text columns the search box matches against. */
|
|
3639
|
+
readonly searchFields: string[];
|
|
3640
|
+
/** Columns locked in the create/edit form. */
|
|
3641
|
+
readonlyFields: string[];
|
|
3642
|
+
/** Default ordering column, or `null` to leave it to the repository. */
|
|
3643
|
+
readonly orderKey: string | null;
|
|
3644
|
+
/** Whether {@link AdminModel.orderKey} sorts ascending. */
|
|
3645
|
+
readonly orderAscending: boolean;
|
|
3646
|
+
/** Rows per page in the list view. */
|
|
3647
|
+
readonly pageSize: number;
|
|
3648
|
+
/** Column used to look a single row up from the detail URL. */
|
|
3649
|
+
readonly identityField: string;
|
|
3650
|
+
/** Whether the create form is exposed. */
|
|
3651
|
+
readonly canCreate: boolean;
|
|
3652
|
+
/** Whether the edit form is exposed. */
|
|
3653
|
+
readonly canEdit: boolean;
|
|
3654
|
+
/** Whether the delete action is exposed. */
|
|
3655
|
+
readonly canDelete: boolean;
|
|
3656
|
+
private readonly slugOverride;
|
|
3657
|
+
private readonly listDisplayOverride;
|
|
3658
|
+
private readonly verboseNameOverride;
|
|
3659
|
+
private readonly verboseNamePluralOverride;
|
|
3660
|
+
/**
|
|
3661
|
+
* Build and validate the configuration.
|
|
3662
|
+
*
|
|
3663
|
+
* @param options - The declarative configuration. See {@link AdminModelOptions}.
|
|
3664
|
+
* @throws Error When a referenced column does not exist on the model.
|
|
3665
|
+
*/
|
|
3666
|
+
constructor(options: AdminModelOptions<C>);
|
|
3667
|
+
/**
|
|
3668
|
+
* Return the URL slug the model is exposed under.
|
|
3669
|
+
*
|
|
3670
|
+
* @returns The configured slug, or the model's table name.
|
|
3671
|
+
*/
|
|
3672
|
+
slug(): string;
|
|
3673
|
+
/**
|
|
3674
|
+
* Return the singular display name.
|
|
3675
|
+
*
|
|
3676
|
+
* @returns The configured name, or the humanized class name without its
|
|
3677
|
+
* trailing `Model`.
|
|
3678
|
+
*/
|
|
3679
|
+
verboseName(): string;
|
|
3680
|
+
/**
|
|
3681
|
+
* Return the plural display name.
|
|
3682
|
+
*
|
|
3683
|
+
* @returns The configured plural, or the singular with an `s`.
|
|
3684
|
+
*/
|
|
3685
|
+
verboseNamePlural(): string;
|
|
3686
|
+
/**
|
|
3687
|
+
* Return every column key on the model, in declaration order.
|
|
3688
|
+
*
|
|
3689
|
+
* @returns The column keys.
|
|
3690
|
+
*/
|
|
3691
|
+
columnNames(): string[];
|
|
3692
|
+
/**
|
|
3693
|
+
* Return the columns the list view renders.
|
|
3694
|
+
*
|
|
3695
|
+
* @returns The configured `listDisplay`, or every column but the password hash.
|
|
3696
|
+
*/
|
|
3697
|
+
listDisplayNames(): string[];
|
|
3698
|
+
/**
|
|
3699
|
+
* Return the columns the detail view renders.
|
|
3700
|
+
*
|
|
3701
|
+
* Unlike {@link AdminModel.listDisplayNames}, this is not narrowed by
|
|
3702
|
+
* `listDisplay`: the list view is a scannable summary, but the detail view is
|
|
3703
|
+
* where an operator goes to see the whole record, so trimming it there would
|
|
3704
|
+
* hide data with nowhere else to read it.
|
|
3705
|
+
*
|
|
3706
|
+
* @returns Every column but the password hash, in declaration order.
|
|
3707
|
+
*/
|
|
3708
|
+
detailFieldNames(): string[];
|
|
3709
|
+
/**
|
|
3710
|
+
* Return the columns a create/edit form exposes.
|
|
3711
|
+
*
|
|
3712
|
+
* Excludes the primary key, the managed timestamps, the password hash and
|
|
3713
|
+
* anything listed in `readonlyFields` — none of which a user edits directly
|
|
3714
|
+
* through the generic form.
|
|
3715
|
+
*
|
|
3716
|
+
* @returns The editable column keys, in declaration order.
|
|
3717
|
+
*/
|
|
3718
|
+
editableFieldNames(): string[];
|
|
3719
|
+
/**
|
|
3720
|
+
* Build a repository for this model bound to a session.
|
|
3721
|
+
*
|
|
3722
|
+
* @param session - The session the repository runs its statements on.
|
|
3723
|
+
* @returns A repository over {@link AdminModel.model}.
|
|
3724
|
+
*/
|
|
3725
|
+
repository(session: AsyncSession): BaseRepository<C>;
|
|
3726
|
+
}
|
|
3727
|
+
/** The row type a configured {@link AdminModel} reads and writes. */
|
|
3728
|
+
type AdminRow<A> = A extends AdminModel<infer C> ? InferModel<C> : never;
|
|
3729
|
+
|
|
3730
|
+
/**
|
|
3731
|
+
* Form building and submission parsing for the admin CRUD views, mirroring
|
|
3732
|
+
* `admin.forms`.
|
|
3733
|
+
*
|
|
3734
|
+
* One direction turns a model's columns into typed widget descriptors the
|
|
3735
|
+
* templates render; the other reads a posted `application/x-www-form-urlencoded`
|
|
3736
|
+
* body back into coerced values ready for the repository. Both live here, away
|
|
3737
|
+
* from the router, so the fiddly per-type handling is unit-testable on its own.
|
|
3738
|
+
*/
|
|
3739
|
+
|
|
3740
|
+
/** A single rendered form control. */
|
|
3741
|
+
interface AdminFormField {
|
|
3742
|
+
/** Column key, used as the form field name. */
|
|
3743
|
+
name: string;
|
|
3744
|
+
/** Human-readable label. */
|
|
3745
|
+
label: string;
|
|
3746
|
+
/** The control to render. */
|
|
3747
|
+
widget: AdminWidget;
|
|
3748
|
+
/** Pre-filled value, already stringified for the control. */
|
|
3749
|
+
value: string;
|
|
3750
|
+
/** Whether the field must be filled in. */
|
|
3751
|
+
required: boolean;
|
|
3752
|
+
/** Checkbox state (`checkbox` widget only). */
|
|
3753
|
+
checked: boolean;
|
|
3754
|
+
/** `step` attribute for `number` widgets. */
|
|
3755
|
+
step: string | null;
|
|
3756
|
+
/** `(value, label)` pairs for `select` widgets. */
|
|
3757
|
+
options: AdminSelectOption[];
|
|
3758
|
+
/** Per-field validation error, or `null`. */
|
|
3759
|
+
error: string | null;
|
|
3760
|
+
}
|
|
3761
|
+
/** The outcome of parsing a submitted create/edit form. */
|
|
3762
|
+
interface ParsedAdminForm {
|
|
3763
|
+
/** Coerced column values, ready to hand to the repository. */
|
|
3764
|
+
data: Record<string, unknown>;
|
|
3765
|
+
/** Per-field error messages, keyed by column. Empty when the form is valid. */
|
|
3766
|
+
errors: Record<string, string>;
|
|
3767
|
+
}
|
|
3768
|
+
/** Options for {@link buildFormFields}. */
|
|
3769
|
+
interface BuildFormFieldsOptions {
|
|
3770
|
+
/** Current values, keyed by column — a row on edit, a re-submission on error. */
|
|
3771
|
+
values?: Record<string, unknown>;
|
|
3772
|
+
/** Per-field errors to surface, keyed by column. */
|
|
3773
|
+
errors?: Record<string, string>;
|
|
3774
|
+
}
|
|
3775
|
+
/**
|
|
3776
|
+
* Render a stored value into the string a control pre-fills with.
|
|
3777
|
+
*
|
|
3778
|
+
* @param widget - The control the value is rendered for.
|
|
3779
|
+
* @param value - The stored value.
|
|
3780
|
+
* @returns The control's `value` text (empty for `null`/`undefined`).
|
|
3781
|
+
*/
|
|
3782
|
+
declare function formatFieldValue(widget: AdminWidget, value: unknown): string;
|
|
3783
|
+
/**
|
|
3784
|
+
* Build the controls a create/edit form renders.
|
|
3785
|
+
*
|
|
3786
|
+
* A field with no current value falls back to its column's literal default, so
|
|
3787
|
+
* a blank create form arrives pre-filled the way the database would fill it.
|
|
3788
|
+
* Without that, submitting the form untouched would write `false` over a
|
|
3789
|
+
* `default(true)` flag — the panel would silently deactivate every row it
|
|
3790
|
+
* creates.
|
|
3791
|
+
*
|
|
3792
|
+
* @param admin - The model configuration.
|
|
3793
|
+
* @param options - Current values and per-field errors.
|
|
3794
|
+
* @returns One {@link AdminFormField} per editable column, in declaration order.
|
|
3795
|
+
*/
|
|
3796
|
+
declare function buildFormFields(admin: AdminModel, options?: BuildFormFieldsOptions): AdminFormField[];
|
|
3797
|
+
/**
|
|
3798
|
+
* Read a submitted create/edit form back into coerced column values.
|
|
3799
|
+
*
|
|
3800
|
+
* A checkbox that is absent from the body is `false` (that is how browsers
|
|
3801
|
+
* submit an unchecked box), and an empty text field on an optional column
|
|
3802
|
+
* becomes `null` rather than an empty string, so a cleared field really clears
|
|
3803
|
+
* the column.
|
|
3804
|
+
*
|
|
3805
|
+
* @param admin - The model configuration.
|
|
3806
|
+
* @param body - The parsed request body.
|
|
3807
|
+
* @returns The coerced values plus any per-field errors.
|
|
3808
|
+
*/
|
|
3809
|
+
declare function parseFormBody(admin: AdminModel, body: Record<string, unknown>): ParsedAdminForm;
|
|
3810
|
+
/**
|
|
3811
|
+
* Render a stored value for a read-only list or detail cell.
|
|
3812
|
+
*
|
|
3813
|
+
* @param value - The stored value.
|
|
3814
|
+
* @returns A display string (empty for `null`/`undefined`).
|
|
3815
|
+
*/
|
|
3816
|
+
declare function formatCellValue(value: unknown): string;
|
|
3817
|
+
|
|
3818
|
+
/**
|
|
3819
|
+
* Authentication backends for the admin panel, mirroring `admin.auth`.
|
|
3820
|
+
*
|
|
3821
|
+
* Operators sign in with a row from the project's own database — there is no
|
|
3822
|
+
* separate admin password store. {@link UserModelAuthBackend} covers the
|
|
3823
|
+
* conventional case (a {@link BaseUserModel} subclass gated on `isActive` and
|
|
3824
|
+
* `isAdmin`); anything else — LDAP, an upstream identity provider, a service
|
|
3825
|
+
* account table — implements {@link AdminAuthBackend} directly.
|
|
3826
|
+
*/
|
|
3827
|
+
|
|
3828
|
+
/** The columns {@link UserModelAuthBackend} reads off a principal row. */
|
|
3829
|
+
interface UserPrincipalRow {
|
|
3830
|
+
id: string;
|
|
3831
|
+
email: string;
|
|
3832
|
+
hashedPassword: string;
|
|
3833
|
+
isActive: boolean;
|
|
3834
|
+
isAdmin: boolean;
|
|
3835
|
+
}
|
|
3836
|
+
/** Verifies a TOTP code for a principal that enrolled a second factor. */
|
|
3837
|
+
interface AdminMfaVerifier {
|
|
3838
|
+
/** Whether the principal has a confirmed second factor. */
|
|
3839
|
+
isEnabled(userId: string): Promise<boolean>;
|
|
3840
|
+
/** Whether `code` is a valid current TOTP for the principal. */
|
|
3841
|
+
verify(userId: string, code: string): Promise<boolean>;
|
|
3842
|
+
}
|
|
3843
|
+
/**
|
|
3844
|
+
* How the panel turns a login form into a principal.
|
|
3845
|
+
*
|
|
3846
|
+
* The interface is generic in the principal so a custom backend can hand its
|
|
3847
|
+
* own row type back to {@link AdminAuthBackend.displayName} and friends.
|
|
3848
|
+
*/
|
|
3849
|
+
interface AdminAuthBackend<Principal = unknown> {
|
|
3850
|
+
/**
|
|
3851
|
+
* Verify credentials.
|
|
3852
|
+
*
|
|
3853
|
+
* @param session - A DB session for the current request.
|
|
3854
|
+
* @param identifier - The submitted login identifier (typically an email).
|
|
3855
|
+
* @param password - The submitted plaintext password.
|
|
3856
|
+
* @returns The principal, or `null` when the credentials are rejected.
|
|
3857
|
+
*/
|
|
3858
|
+
authenticate(session: AsyncSession, identifier: string, password: string): Promise<Principal | null>;
|
|
3859
|
+
/**
|
|
3860
|
+
* Re-load the principal a session points at, so a deactivated operator loses
|
|
3861
|
+
* access on the next request rather than at cookie expiry.
|
|
3862
|
+
*
|
|
3863
|
+
* @param session - A DB session for the current request.
|
|
3864
|
+
* @param subject - The principal id stored in the session.
|
|
3865
|
+
* @returns The principal, or `null` when it no longer qualifies.
|
|
3866
|
+
*/
|
|
3867
|
+
loadPrincipal(session: AsyncSession, subject: string): Promise<Principal | null>;
|
|
3868
|
+
/**
|
|
3869
|
+
* Return the stable id stored in the session cookie.
|
|
3870
|
+
*
|
|
3871
|
+
* @param principal - The authenticated principal.
|
|
3872
|
+
* @returns The principal id.
|
|
3873
|
+
*/
|
|
3874
|
+
principalId(principal: Principal): string;
|
|
3875
|
+
/**
|
|
3876
|
+
* Return the name shown in the panel header.
|
|
3877
|
+
*
|
|
3878
|
+
* @param principal - The authenticated principal.
|
|
3879
|
+
* @returns A human-readable label.
|
|
3880
|
+
*/
|
|
3881
|
+
displayName(principal: Principal): string;
|
|
3882
|
+
/**
|
|
3883
|
+
* Whether this principal must clear a second factor before entering.
|
|
3884
|
+
*
|
|
3885
|
+
* Omit to declare the backend has no MFA — the panel then treats every
|
|
3886
|
+
* successful password check as a complete login.
|
|
3887
|
+
*
|
|
3888
|
+
* @param principal - The authenticated principal.
|
|
3889
|
+
* @returns `true` when a TOTP challenge is required.
|
|
3890
|
+
*/
|
|
3891
|
+
mfaEnabled?(principal: Principal): Promise<boolean>;
|
|
3892
|
+
/**
|
|
3893
|
+
* Verify the submitted TOTP code.
|
|
3894
|
+
*
|
|
3895
|
+
* @param principal - The authenticated principal.
|
|
3896
|
+
* @param code - The submitted code.
|
|
3897
|
+
* @returns `true` when the code is valid.
|
|
3898
|
+
*/
|
|
3899
|
+
verifyMfa?(principal: Principal, code: string): Promise<boolean>;
|
|
3900
|
+
}
|
|
3901
|
+
/** Options for {@link UserModelAuthBackend}. */
|
|
3902
|
+
interface UserModelAuthBackendOptions {
|
|
3903
|
+
/** Password hasher. Defaults to a stock {@link PasswordUtils}. */
|
|
3904
|
+
passwords?: PasswordUtils;
|
|
3905
|
+
/**
|
|
3906
|
+
* TOTP verifier. When given, a principal with a confirmed secret is sent
|
|
3907
|
+
* through the panel's `/mfa` challenge after the password check, so the
|
|
3908
|
+
* admin panel can never be the weaker door into an MFA-protected account.
|
|
3909
|
+
*/
|
|
3910
|
+
mfa?: AdminMfaVerifier;
|
|
3911
|
+
/** Column holding the login identifier. Default `"email"`. */
|
|
3912
|
+
identifierField?: string;
|
|
3913
|
+
/**
|
|
3914
|
+
* Require `isAdmin === true` on the row. Default `true`. Turn it off only
|
|
3915
|
+
* when the model expresses privilege some other way and the panel is already
|
|
3916
|
+
* gated elsewhere.
|
|
3917
|
+
*/
|
|
3918
|
+
requireAdmin?: boolean;
|
|
3919
|
+
}
|
|
3920
|
+
/**
|
|
3921
|
+
* The conventional backend: authenticate against a {@link BaseUserModel}
|
|
3922
|
+
* subclass, admitting only rows that are both active and flagged as admins.
|
|
3923
|
+
*
|
|
3924
|
+
* ```ts
|
|
3925
|
+
* new UserModelAuthBackend(UserModel);
|
|
3926
|
+
* ```
|
|
3927
|
+
*/
|
|
3928
|
+
declare class UserModelAuthBackend implements AdminAuthBackend<UserPrincipalRow> {
|
|
3929
|
+
private readonly model;
|
|
3930
|
+
private readonly passwords;
|
|
3931
|
+
private readonly mfa;
|
|
3932
|
+
private readonly identifierField;
|
|
3933
|
+
private readonly requireAdmin;
|
|
3934
|
+
/**
|
|
3935
|
+
* Build the backend.
|
|
3936
|
+
*
|
|
3937
|
+
* @param model - The user model class (a `BaseUserModel` subclass).
|
|
3938
|
+
* @param options - Hasher, MFA verifier and gating overrides.
|
|
3939
|
+
*/
|
|
3940
|
+
constructor(model: ModelClass, options?: UserModelAuthBackendOptions);
|
|
3941
|
+
/**
|
|
3942
|
+
* Whether a row is allowed into the panel at all.
|
|
3943
|
+
*
|
|
3944
|
+
* @param row - The candidate row.
|
|
3945
|
+
* @returns `true` when the row is active and (when required) an admin.
|
|
3946
|
+
*/
|
|
3947
|
+
private admits;
|
|
3948
|
+
/**
|
|
3949
|
+
* Verify an identifier/password pair.
|
|
3950
|
+
*
|
|
3951
|
+
* The identifier is lowercased and trimmed before lookup, matching the
|
|
3952
|
+
* normalization the auth service applies on signup.
|
|
3953
|
+
*
|
|
3954
|
+
* @param session - A DB session for the current request.
|
|
3955
|
+
* @param identifier - The submitted identifier.
|
|
3956
|
+
* @param password - The submitted plaintext password.
|
|
3957
|
+
* @returns The matching row, or `null` when it does not qualify.
|
|
3958
|
+
*/
|
|
3959
|
+
authenticate(session: AsyncSession, identifier: string, password: string): Promise<UserPrincipalRow | null>;
|
|
3960
|
+
/**
|
|
3961
|
+
* Re-load the principal a session points at.
|
|
3962
|
+
*
|
|
3963
|
+
* @param session - A DB session for the current request.
|
|
3964
|
+
* @param subject - The principal id from the session cookie.
|
|
3965
|
+
* @returns The row, or `null` when it vanished or lost its privileges.
|
|
3966
|
+
*/
|
|
3967
|
+
loadPrincipal(session: AsyncSession, subject: string): Promise<UserPrincipalRow | null>;
|
|
3968
|
+
/**
|
|
3969
|
+
* Return the row's primary key.
|
|
3970
|
+
*
|
|
3971
|
+
* @param principal - The authenticated row.
|
|
3972
|
+
* @returns The principal id.
|
|
3973
|
+
*/
|
|
3974
|
+
principalId(principal: UserPrincipalRow): string;
|
|
3975
|
+
/**
|
|
3976
|
+
* Return the label shown in the panel header.
|
|
3977
|
+
*
|
|
3978
|
+
* @param principal - The authenticated row.
|
|
3979
|
+
* @returns The identifier column's value.
|
|
3980
|
+
*/
|
|
3981
|
+
displayName(principal: UserPrincipalRow): string;
|
|
3982
|
+
/**
|
|
3983
|
+
* Whether the principal enrolled a second factor.
|
|
3984
|
+
*
|
|
3985
|
+
* @param principal - The authenticated row.
|
|
3986
|
+
* @returns `true` when an MFA verifier is configured and reports a secret.
|
|
3987
|
+
*/
|
|
3988
|
+
mfaEnabled(principal: UserPrincipalRow): Promise<boolean>;
|
|
3989
|
+
/**
|
|
3990
|
+
* Verify a submitted TOTP code.
|
|
3991
|
+
*
|
|
3992
|
+
* @param principal - The authenticated row.
|
|
3993
|
+
* @param code - The submitted code.
|
|
3994
|
+
* @returns `true` when the code is valid.
|
|
3995
|
+
*/
|
|
3996
|
+
verifyMfa(principal: UserPrincipalRow, code: string): Promise<boolean>;
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
/**
|
|
4000
|
+
* Signed-cookie sessions for the admin panel, mirroring `admin.session`.
|
|
4001
|
+
*
|
|
4002
|
+
* The panel's session is **stateless**: the principal id, display name, CSRF
|
|
4003
|
+
* token and expiry travel in the cookie itself, signed with HMAC-SHA256 over
|
|
4004
|
+
* the caller's secret. Nothing is kept server-side, so the panel survives a
|
|
4005
|
+
* restart and works across replicas without a shared store — the property that
|
|
4006
|
+
* matters most for an operator tool that is used in bursts and left open.
|
|
4007
|
+
*
|
|
4008
|
+
* The CSRF token lives inside the session payload, so every write form can
|
|
4009
|
+
* carry it and the server compares it against the cookie it already trusts.
|
|
4010
|
+
*/
|
|
4011
|
+
|
|
4012
|
+
/** The payload carried by the admin session cookie. */
|
|
4013
|
+
interface AdminSession {
|
|
4014
|
+
/** Stable id of the authenticated principal. */
|
|
4015
|
+
subject: string;
|
|
4016
|
+
/** Display name shown in the header. */
|
|
4017
|
+
displayName: string;
|
|
4018
|
+
/** Token every write form echoes back for CSRF validation. */
|
|
4019
|
+
csrfToken: string;
|
|
4020
|
+
/** Expiry, in epoch seconds. */
|
|
4021
|
+
expiresAt: number;
|
|
4022
|
+
/**
|
|
4023
|
+
* `true` once the second factor was accepted. Sessions issued for a
|
|
4024
|
+
* principal without MFA are complete from the start.
|
|
4025
|
+
*/
|
|
4026
|
+
mfaPassed: boolean;
|
|
4027
|
+
}
|
|
4028
|
+
/** Options for {@link AdminSessionStore}. */
|
|
4029
|
+
interface AdminSessionStoreOptions {
|
|
4030
|
+
/** HMAC key signing the cookie. At least 32 characters. */
|
|
4031
|
+
secret: string;
|
|
4032
|
+
/** Cookie name. Default `tempest_admin_session`. */
|
|
4033
|
+
cookieName?: string;
|
|
4034
|
+
/** Session lifetime in seconds. Default `28800` (8 hours). */
|
|
4035
|
+
maxAgeSeconds?: number;
|
|
4036
|
+
/** Send the cookie with `Secure` (HTTPS only). Default `true`. */
|
|
4037
|
+
cookieSecure?: boolean;
|
|
4038
|
+
/** Cookie `Path`. Default `/`. */
|
|
4039
|
+
cookiePath?: string;
|
|
4040
|
+
}
|
|
4041
|
+
/**
|
|
4042
|
+
* Issues, verifies and clears the admin session cookie.
|
|
4043
|
+
*
|
|
4044
|
+
* The cookie value is `<base64url payload>.<base64url signature>`; a payload
|
|
4045
|
+
* whose signature does not verify, or whose expiry has passed, resolves to
|
|
4046
|
+
* `null` — an operator with a tampered or stale cookie is simply logged out.
|
|
4047
|
+
*/
|
|
4048
|
+
declare class AdminSessionStore {
|
|
4049
|
+
private readonly secret;
|
|
4050
|
+
private readonly cookieName;
|
|
4051
|
+
private readonly maxAgeSeconds;
|
|
4052
|
+
private readonly cookieSecure;
|
|
4053
|
+
private readonly cookiePath;
|
|
4054
|
+
/**
|
|
4055
|
+
* Build the store.
|
|
4056
|
+
*
|
|
4057
|
+
* @param options - Secret, cookie name, lifetime and cookie flags.
|
|
4058
|
+
* @throws Error When the secret is shorter than 32 characters.
|
|
4059
|
+
*/
|
|
4060
|
+
constructor(options: AdminSessionStoreOptions);
|
|
4061
|
+
/**
|
|
4062
|
+
* Sign a payload.
|
|
4063
|
+
*
|
|
4064
|
+
* @param payload - The base64url payload to sign.
|
|
4065
|
+
* @returns The base64url signature.
|
|
4066
|
+
*/
|
|
4067
|
+
private sign;
|
|
4068
|
+
/**
|
|
4069
|
+
* Mint a fresh session for an authenticated principal.
|
|
4070
|
+
*
|
|
4071
|
+
* @param subject - The principal id.
|
|
4072
|
+
* @param displayName - The name shown in the header.
|
|
4073
|
+
* @param mfaPassed - Whether the second factor is already satisfied.
|
|
4074
|
+
* @returns The new session payload (not yet written to a response).
|
|
4075
|
+
*/
|
|
4076
|
+
issue(subject: string, displayName: string, mfaPassed?: boolean): AdminSession;
|
|
4077
|
+
/**
|
|
4078
|
+
* Read and verify the session carried by a request.
|
|
4079
|
+
*
|
|
4080
|
+
* @param req - The inbound request.
|
|
4081
|
+
* @returns The session, or `null` when absent, tampered with or expired.
|
|
4082
|
+
*/
|
|
4083
|
+
load(req: Request): AdminSession | null;
|
|
4084
|
+
/**
|
|
4085
|
+
* Write a session to the response as a signed cookie.
|
|
4086
|
+
*
|
|
4087
|
+
* @param res - The outbound response.
|
|
4088
|
+
* @param session - The session to persist.
|
|
4089
|
+
*/
|
|
4090
|
+
save(res: Response$1, session: AdminSession): void;
|
|
4091
|
+
/**
|
|
4092
|
+
* Drop the session cookie.
|
|
4093
|
+
*
|
|
4094
|
+
* @param res - The outbound response.
|
|
4095
|
+
*/
|
|
4096
|
+
clear(res: Response$1): void;
|
|
4097
|
+
/**
|
|
4098
|
+
* Render a `Set-Cookie` value with the configured flags.
|
|
4099
|
+
*
|
|
4100
|
+
* @param value - The cookie value.
|
|
4101
|
+
* @param maxAge - Lifetime in seconds (`0` expires it immediately).
|
|
4102
|
+
* @returns The header value.
|
|
4103
|
+
*/
|
|
4104
|
+
private cookie;
|
|
4105
|
+
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Compare a submitted CSRF token against the session's, in constant time.
|
|
4108
|
+
*
|
|
4109
|
+
* @param session - The active session.
|
|
4110
|
+
* @param submitted - The `csrf_token` field from the form body.
|
|
4111
|
+
* @returns `true` when the tokens match.
|
|
4112
|
+
*/
|
|
4113
|
+
declare function csrfTokenMatches(session: AdminSession, submitted: unknown): boolean;
|
|
4114
|
+
|
|
4115
|
+
/**
|
|
4116
|
+
* Typed theming for the server-rendered admin panel, mirroring `admin.theme`.
|
|
4117
|
+
*
|
|
4118
|
+
* The bundled stylesheet is driven entirely by CSS custom properties declared
|
|
4119
|
+
* on `:root`. An {@link AdminTheme} overrides those properties — plus the logo,
|
|
4120
|
+
* favicon, font and footer — through typed, documented fields instead of
|
|
4121
|
+
* forking the stylesheet. The values are injected as a `<style>` block after
|
|
4122
|
+
* the stylesheet (so they win), which means there is no CSS file to maintain on
|
|
4123
|
+
* the project side and every knob is discoverable in the editor.
|
|
4124
|
+
*
|
|
4125
|
+
* For anything the fields do not cover, point `customCssUrl` at your own
|
|
4126
|
+
* stylesheet — it is linked last, so it overrides everything, including this.
|
|
4127
|
+
*/
|
|
4128
|
+
/**
|
|
4129
|
+
* Appearance overrides for the admin panel. Every field is optional and
|
|
4130
|
+
* defaults to the stock look, so `{}` is a no-op.
|
|
4131
|
+
*/
|
|
4132
|
+
interface AdminTheme {
|
|
4133
|
+
/** Primary accent — links, primary buttons, active sidebar item. Default `#2563eb`. */
|
|
4134
|
+
accent?: string;
|
|
4135
|
+
/** Hover/active shade of {@link AdminTheme.accent}. Default `#1d4ed8`. */
|
|
4136
|
+
accentHover?: string;
|
|
4137
|
+
/** Color for destructive actions and error messages. Default `#b91c1c`. */
|
|
4138
|
+
danger?: string;
|
|
4139
|
+
/** Background of the top header band. Default `#0f172a`. */
|
|
4140
|
+
headerBg?: string;
|
|
4141
|
+
/** Background of the left sidebar. Falls back to `headerBg` so the chrome reads as one surface. */
|
|
4142
|
+
sidebarBg?: string;
|
|
4143
|
+
/** Main content background. Omitted uses the mode default (light grey, or near-black in dark mode). */
|
|
4144
|
+
pageBg?: string;
|
|
4145
|
+
/** Border radius for buttons, inputs, cards and tables. Default `6px`. */
|
|
4146
|
+
radius?: string;
|
|
4147
|
+
/** CSS `font-family` for the whole panel. Omitted keeps the system stack. */
|
|
4148
|
+
fontFamily?: string;
|
|
4149
|
+
/** URL of an image shown in the header instead of the brand text. */
|
|
4150
|
+
logoUrl?: string;
|
|
4151
|
+
/** `alt` text for the logo image. Default `Logo`. */
|
|
4152
|
+
logoAlt?: string;
|
|
4153
|
+
/** URL of the browser-tab favicon. */
|
|
4154
|
+
faviconUrl?: string;
|
|
4155
|
+
/** Text shown in the page footer. Default `Powered by tempest-express-sdk`. */
|
|
4156
|
+
footerText?: string;
|
|
4157
|
+
/** Switch the content surfaces to a dark palette (the chrome is already dark). */
|
|
4158
|
+
darkMode?: boolean;
|
|
4159
|
+
/** URL of an extra stylesheet linked **after** the theme, so it overrides everything. */
|
|
4160
|
+
customCssUrl?: string;
|
|
4161
|
+
}
|
|
4162
|
+
/** An {@link AdminTheme} with every default filled in. */
|
|
4163
|
+
interface ResolvedAdminTheme {
|
|
4164
|
+
accent: string;
|
|
4165
|
+
accentHover: string;
|
|
4166
|
+
danger: string;
|
|
4167
|
+
headerBg: string;
|
|
4168
|
+
sidebarBg: string;
|
|
4169
|
+
pageBg: string | null;
|
|
4170
|
+
radius: string;
|
|
4171
|
+
fontFamily: string | null;
|
|
4172
|
+
logoUrl: string | null;
|
|
4173
|
+
logoAlt: string;
|
|
4174
|
+
faviconUrl: string | null;
|
|
4175
|
+
footerText: string;
|
|
4176
|
+
darkMode: boolean;
|
|
4177
|
+
customCssUrl: string | null;
|
|
4178
|
+
}
|
|
4179
|
+
/**
|
|
4180
|
+
* Fill a theme with its defaults, validating every string field.
|
|
4181
|
+
*
|
|
4182
|
+
* @param theme - The partial theme (or nothing, for the stock look).
|
|
4183
|
+
* @returns The theme with every field resolved.
|
|
4184
|
+
* @throws Error When a string field contains a character that would break the markup.
|
|
4185
|
+
*/
|
|
4186
|
+
declare function resolveAdminTheme(theme?: AdminTheme): ResolvedAdminTheme;
|
|
4187
|
+
/**
|
|
4188
|
+
* Render the `<style>` body for a resolved theme.
|
|
4189
|
+
*
|
|
4190
|
+
* Dark mode only overrides the content-area surfaces — the header and sidebar
|
|
4191
|
+
* are already dark via `--tempest-bg` — and is skipped entirely when the
|
|
4192
|
+
* project pinned its own `pageBg`, since an explicit value always wins.
|
|
4193
|
+
*
|
|
4194
|
+
* @param theme - The resolved theme.
|
|
4195
|
+
* @returns CSS text, ready to inject verbatim inside a `<style>` element.
|
|
4196
|
+
*/
|
|
4197
|
+
declare function adminThemeCss(theme: ResolvedAdminTheme): string;
|
|
4198
|
+
|
|
4199
|
+
/**
|
|
4200
|
+
* Admin site registry, mirroring `admin.site` — the analog of Django's
|
|
4201
|
+
* `AdminSite`.
|
|
4202
|
+
*
|
|
4203
|
+
* A project instantiates one site, registers its {@link AdminModel}
|
|
4204
|
+
* configurations (one at a time with {@link AdminSite.register}, or all at
|
|
4205
|
+
* once with {@link AdminSite.automap}), and hands the site to
|
|
4206
|
+
* `makeAdminRouter`.
|
|
4207
|
+
*/
|
|
4208
|
+
|
|
4209
|
+
/** Branding and appearance options for an {@link AdminSite}. */
|
|
4210
|
+
interface AdminSiteOptions {
|
|
4211
|
+
/** Text used in the page `<title>` and the dashboard heading. Default `"Admin"`. */
|
|
4212
|
+
title?: string;
|
|
4213
|
+
/** Centered header brand. Falls back to `title`. */
|
|
4214
|
+
brand?: string;
|
|
4215
|
+
/** Dashboard subtitle. Default `"Site administration"`. */
|
|
4216
|
+
indexSubtitle?: string;
|
|
4217
|
+
/** Optional outbound "View site" link rendered in the header. */
|
|
4218
|
+
siteUrl?: string;
|
|
4219
|
+
/** Typed appearance overrides. Omitted keeps the stock look. */
|
|
4220
|
+
theme?: AdminTheme;
|
|
4221
|
+
}
|
|
4222
|
+
/** Options accepted by {@link AdminSite.automap}. */
|
|
4223
|
+
interface AdminAutomapOptions extends Omit<AdminModelOptions<ModelClass>, "model"> {
|
|
4224
|
+
/** Models to skip — each entry is the model class or its table name. */
|
|
4225
|
+
exclude?: readonly (ModelClass | string)[];
|
|
4226
|
+
/**
|
|
4227
|
+
* When `true` (default), a model whose slug is already registered is left
|
|
4228
|
+
* untouched, so a hand-tuned {@link AdminModel} can be registered first.
|
|
4229
|
+
* When `false`, a collision throws, as {@link AdminSite.register} does.
|
|
4230
|
+
*/
|
|
4231
|
+
skipRegistered?: boolean;
|
|
4232
|
+
}
|
|
4233
|
+
/**
|
|
4234
|
+
* The registry of {@link AdminModel} configurations a panel exposes.
|
|
4235
|
+
*
|
|
4236
|
+
* ```ts
|
|
4237
|
+
* const site = new AdminSite({ title: "MyApp Admin", brand: "myapp-admin" });
|
|
4238
|
+
* site.register({ model: UserModel, searchFields: ["email"] });
|
|
4239
|
+
* site.automap(models);
|
|
4240
|
+
* ```
|
|
4241
|
+
*/
|
|
4242
|
+
declare class AdminSite {
|
|
4243
|
+
/** Text used in the page `<title>` and the dashboard heading. */
|
|
4244
|
+
readonly title: string;
|
|
4245
|
+
/** Centered header brand, or `null` to fall back to {@link AdminSite.title}. */
|
|
4246
|
+
readonly brand: string | null;
|
|
4247
|
+
/** Dashboard subtitle. */
|
|
4248
|
+
readonly indexSubtitle: string;
|
|
4249
|
+
/** Outbound "View site" link, or `null`. */
|
|
4250
|
+
readonly siteUrl: string | null;
|
|
4251
|
+
/** Typed appearance overrides. */
|
|
4252
|
+
readonly theme: AdminTheme;
|
|
4253
|
+
private readonly registry;
|
|
4254
|
+
/**
|
|
4255
|
+
* Initialize the site.
|
|
4256
|
+
*
|
|
4257
|
+
* @param options - Branding and appearance. See {@link AdminSiteOptions}.
|
|
4258
|
+
*/
|
|
4259
|
+
constructor(options?: AdminSiteOptions);
|
|
4260
|
+
/**
|
|
4261
|
+
* Return the centered header brand text.
|
|
4262
|
+
*
|
|
4263
|
+
* @returns {@link AdminSite.brand} when set, otherwise {@link AdminSite.title}.
|
|
4264
|
+
*/
|
|
4265
|
+
brandText(): string;
|
|
4266
|
+
/**
|
|
4267
|
+
* Register a model configuration under its slug.
|
|
4268
|
+
*
|
|
4269
|
+
* @param admin - An {@link AdminModel} instance, or the options to build one.
|
|
4270
|
+
* @returns The registered instance, so the call can be chained or assigned.
|
|
4271
|
+
* @throws Error When another configuration already holds the same slug.
|
|
4272
|
+
*/
|
|
4273
|
+
register<C extends ModelClass>(admin: AdminModel<C> | AdminModelOptions<C>): AdminModel<C>;
|
|
4274
|
+
/**
|
|
4275
|
+
* Remove a previously registered configuration.
|
|
4276
|
+
*
|
|
4277
|
+
* @param slug - The slug to drop.
|
|
4278
|
+
* @throws Error When no configuration is registered under the slug.
|
|
4279
|
+
*/
|
|
4280
|
+
unregister(slug: string): void;
|
|
4281
|
+
/**
|
|
4282
|
+
* Look a configuration up by slug.
|
|
4283
|
+
*
|
|
4284
|
+
* @param slug - The admin slug.
|
|
4285
|
+
* @returns The configuration, or `null` when nothing matches.
|
|
4286
|
+
*/
|
|
4287
|
+
get(slug: string): AdminModel | null;
|
|
4288
|
+
/**
|
|
4289
|
+
* Return every registered configuration, ordered by display name.
|
|
4290
|
+
*
|
|
4291
|
+
* @returns The configurations (empty when nothing is registered).
|
|
4292
|
+
*/
|
|
4293
|
+
list(): AdminModel[];
|
|
4294
|
+
/**
|
|
4295
|
+
* Register every concrete model found in `source` at once.
|
|
4296
|
+
*
|
|
4297
|
+
* The batch counterpart to {@link AdminSite.register}: instead of one call
|
|
4298
|
+
* per table, hand it the models barrel and every model class declaring a
|
|
4299
|
+
* `tablename` is wrapped in a default {@link AdminModel}.
|
|
4300
|
+
*
|
|
4301
|
+
* ```ts
|
|
4302
|
+
* import * as models from "./db/models";
|
|
4303
|
+
*
|
|
4304
|
+
* site.automap(models);
|
|
4305
|
+
* site.automap([UserModel, OrderModel], { pageSize: 50 });
|
|
4306
|
+
* ```
|
|
4307
|
+
*
|
|
4308
|
+
* @param source - An array of model classes, or a module namespace object
|
|
4309
|
+
* whose values are swept (non-model entries are ignored).
|
|
4310
|
+
* @param options - `exclude`, `skipRegistered` and any {@link AdminModel}
|
|
4311
|
+
* option applied uniformly to every model discovered here.
|
|
4312
|
+
* @returns The configurations newly registered by this call.
|
|
4313
|
+
* @throws Error When `skipRegistered` is `false` and a slug collides.
|
|
4314
|
+
*/
|
|
4315
|
+
automap(source: readonly unknown[] | Record<string, unknown>, options?: AdminAutomapOptions): AdminModel[];
|
|
4316
|
+
}
|
|
4317
|
+
|
|
4318
|
+
/**
|
|
4319
|
+
* The bundled admin stylesheet, served at `{prefix}/static/admin.css`.
|
|
4320
|
+
*
|
|
4321
|
+
* Ported verbatim from tempest-fastapi-sdk's `admin/static/admin.css` so both
|
|
4322
|
+
* SDKs render the same panel. Everything is driven by the `--tempest-*` custom
|
|
4323
|
+
* properties an `AdminTheme` overrides, so a project restyles the panel without
|
|
4324
|
+
* touching this file — see `@/admin/theme`.
|
|
4325
|
+
*
|
|
4326
|
+
* It ships as a string rather than an asset because the package publishes only
|
|
4327
|
+
* `dist`: a `.css` file on disk would not survive the build.
|
|
4328
|
+
*/
|
|
4329
|
+
/** The stylesheet text. */
|
|
4330
|
+
declare const ADMIN_CSS: string;
|
|
4331
|
+
|
|
4332
|
+
/**
|
|
4333
|
+
* HTML rendering for the admin panel — the analog of the FastAPI SDK's jinja
|
|
4334
|
+
* templates, written as plain typed functions.
|
|
4335
|
+
*
|
|
4336
|
+
* There is no template engine and no external asset: every page is a string
|
|
4337
|
+
* built from a view model the router prepares, and the only stylesheet is the
|
|
4338
|
+
* one this package serves. That keeps the panel dependency-free (a template
|
|
4339
|
+
* engine would be a runtime dependency every consumer inherits) and keeps the
|
|
4340
|
+
* markup type-checked against the data that fills it.
|
|
4341
|
+
*
|
|
4342
|
+
* Every value interpolated into markup goes through {@link escapeHtml}.
|
|
4343
|
+
*/
|
|
4344
|
+
|
|
4345
|
+
/**
|
|
4346
|
+
* Escape a value for safe interpolation into HTML text or an attribute.
|
|
4347
|
+
*
|
|
4348
|
+
* @param value - The value to escape.
|
|
4349
|
+
* @returns The escaped text.
|
|
4350
|
+
*/
|
|
4351
|
+
declare function escapeHtml(value: unknown): string;
|
|
4352
|
+
/** One entry in the sidebar's model list. */
|
|
4353
|
+
interface AdminNavEntry {
|
|
4354
|
+
/** Display label. */
|
|
4355
|
+
label: string;
|
|
4356
|
+
/** Absolute URL of the model's list view. */
|
|
4357
|
+
url: string;
|
|
4358
|
+
}
|
|
4359
|
+
/** A banner shown above the page content. */
|
|
4360
|
+
interface AdminMessage {
|
|
4361
|
+
/** Message text. */
|
|
4362
|
+
text: string;
|
|
4363
|
+
/** Severity, driving the banner color: `success`, `error` or `warning`. */
|
|
4364
|
+
level: "success" | "error" | "warning";
|
|
4365
|
+
}
|
|
4366
|
+
/** Everything the chrome needs, shared by every page. */
|
|
4367
|
+
interface AdminRenderContext {
|
|
4368
|
+
/** The registered site (branding + models). */
|
|
4369
|
+
site: AdminSite;
|
|
4370
|
+
/** The site's theme, with defaults resolved. */
|
|
4371
|
+
theme: ResolvedAdminTheme;
|
|
4372
|
+
/** The router's mount prefix, without a trailing slash. */
|
|
4373
|
+
prefix: string;
|
|
4374
|
+
/** The active session, or `null` on the login and MFA pages. */
|
|
4375
|
+
session: AdminSession | null;
|
|
4376
|
+
/** The current request path, used to highlight the active sidebar item. */
|
|
4377
|
+
currentPath: string;
|
|
4378
|
+
/** Sidebar entries, one per registered model. */
|
|
4379
|
+
navModels: AdminNavEntry[];
|
|
4380
|
+
/** Banners rendered above the content. */
|
|
4381
|
+
messages: AdminMessage[];
|
|
4382
|
+
}
|
|
4383
|
+
/**
|
|
4384
|
+
* Wrap page content in the panel chrome: header, sidebar, footer and theme.
|
|
4385
|
+
*
|
|
4386
|
+
* The sidebar is off-canvas below 768px, opened by a checkbox the burger label
|
|
4387
|
+
* toggles — pure CSS, so the panel needs no JavaScript to be navigable.
|
|
4388
|
+
*
|
|
4389
|
+
* @param context - The shared chrome data.
|
|
4390
|
+
* @param title - The page `<title>`.
|
|
4391
|
+
* @param body - The already-escaped content markup.
|
|
4392
|
+
* @returns A complete HTML document.
|
|
4393
|
+
*/
|
|
4394
|
+
declare function renderLayout(context: AdminRenderContext, title: string, body: string): string;
|
|
4395
|
+
/**
|
|
4396
|
+
* Render the sign-in page.
|
|
4397
|
+
*
|
|
4398
|
+
* @param context - The shared chrome data (with no session).
|
|
4399
|
+
* @param error - An error to show above the form, or `null`.
|
|
4400
|
+
* @returns The full page.
|
|
4401
|
+
*/
|
|
4402
|
+
declare function renderLoginPage(context: AdminRenderContext, error: string | null): string;
|
|
4403
|
+
/**
|
|
4404
|
+
* Render the TOTP challenge shown between the password check and the panel.
|
|
4405
|
+
*
|
|
4406
|
+
* @param context - The shared chrome data (with no completed session).
|
|
4407
|
+
* @param error - An error to show above the form, or `null`.
|
|
4408
|
+
* @returns The full page.
|
|
4409
|
+
*/
|
|
4410
|
+
declare function renderMfaPage(context: AdminRenderContext, error: string | null): string;
|
|
4411
|
+
/** One model card on the dashboard. */
|
|
4412
|
+
interface AdminDashboardCard {
|
|
4413
|
+
/** Plural display name. */
|
|
4414
|
+
label: string;
|
|
4415
|
+
/** Row count, or `null` when counting failed. */
|
|
4416
|
+
count: number | null;
|
|
4417
|
+
/** URL of the list view. */
|
|
4418
|
+
url: string;
|
|
4419
|
+
/** URL of the create form, or `null` when creation is disabled. */
|
|
4420
|
+
newUrl: string | null;
|
|
4421
|
+
}
|
|
4422
|
+
/** The system metrics panel on the dashboard. */
|
|
4423
|
+
interface AdminDashboardMetrics {
|
|
4424
|
+
/** CPU load as a percentage of available cores. */
|
|
4425
|
+
cpuPercent: number;
|
|
4426
|
+
/** Memory used, as a percentage. */
|
|
4427
|
+
memoryPercent: number;
|
|
4428
|
+
/** Memory used, in GB. */
|
|
4429
|
+
memoryUsedGb: string;
|
|
4430
|
+
/** Memory total, in GB. */
|
|
4431
|
+
memoryTotalGb: string;
|
|
4432
|
+
}
|
|
4433
|
+
/**
|
|
4434
|
+
* Render the dashboard: one card per registered model plus the optional system
|
|
4435
|
+
* metrics panel.
|
|
4436
|
+
*
|
|
4437
|
+
* @param context - The shared chrome data.
|
|
4438
|
+
* @param cards - One entry per registered model.
|
|
4439
|
+
* @param metrics - The system metrics panel, or `null` when disabled.
|
|
4440
|
+
* @returns The full page.
|
|
4441
|
+
*/
|
|
4442
|
+
declare function renderDashboardPage(context: AdminRenderContext, cards: AdminDashboardCard[], metrics: AdminDashboardMetrics | null): string;
|
|
4443
|
+
/** A filter control rendered above the list view. */
|
|
4444
|
+
interface AdminFilterView {
|
|
4445
|
+
/** Column key the control filters on. */
|
|
4446
|
+
field: string;
|
|
4447
|
+
/** Human-readable label. */
|
|
4448
|
+
label: string;
|
|
4449
|
+
/** Which control to render. */
|
|
4450
|
+
kind: "select" | "daterange" | "text";
|
|
4451
|
+
/** Current value for `select` and `text` controls. */
|
|
4452
|
+
value: string;
|
|
4453
|
+
/** Lower bound for a `daterange` control. */
|
|
4454
|
+
valueFrom: string;
|
|
4455
|
+
/** Upper bound for a `daterange` control. */
|
|
4456
|
+
valueTo: string;
|
|
4457
|
+
/** Options for a `select` control. */
|
|
4458
|
+
options: {
|
|
4459
|
+
value: string;
|
|
4460
|
+
label: string;
|
|
4461
|
+
selected: boolean;
|
|
4462
|
+
}[];
|
|
4463
|
+
}
|
|
4464
|
+
/** A clickable column header's sort state. */
|
|
4465
|
+
interface AdminSortView {
|
|
4466
|
+
/** URL that applies (or flips) this column's ordering. */
|
|
4467
|
+
url: string;
|
|
4468
|
+
/** Whether the list is currently ordered by this column. */
|
|
4469
|
+
active: boolean;
|
|
4470
|
+
/** Whether the current ordering is ascending. */
|
|
4471
|
+
ascending: boolean;
|
|
4472
|
+
}
|
|
4473
|
+
/** The view model the list page renders. */
|
|
4474
|
+
interface AdminListView {
|
|
4475
|
+
/** Plural display name shown as the heading. */
|
|
4476
|
+
title: string;
|
|
4477
|
+
/** Column keys rendered as table columns. */
|
|
4478
|
+
columns: string[];
|
|
4479
|
+
/** One entry per row: its identity plus the formatted cells. */
|
|
4480
|
+
rows: {
|
|
4481
|
+
identity: string;
|
|
4482
|
+
cells: string[];
|
|
4483
|
+
url: string;
|
|
4484
|
+
}[];
|
|
4485
|
+
/** Total matching rows, across all pages. */
|
|
4486
|
+
total: number;
|
|
4487
|
+
/** Current page number, 1-based. */
|
|
4488
|
+
page: number;
|
|
4489
|
+
/** Total page count. */
|
|
4490
|
+
pages: number;
|
|
4491
|
+
/** URL of the previous page, or `null` on the first page. */
|
|
4492
|
+
prevUrl: string | null;
|
|
4493
|
+
/** URL of the next page, or `null` on the last page. */
|
|
4494
|
+
nextUrl: string | null;
|
|
4495
|
+
/** Whether a search box is rendered. */
|
|
4496
|
+
searchable: boolean;
|
|
4497
|
+
/** Current search text. */
|
|
4498
|
+
searchValue: string;
|
|
4499
|
+
/** Filter controls. */
|
|
4500
|
+
filters: AdminFilterView[];
|
|
4501
|
+
/** Sort state per column key. */
|
|
4502
|
+
sort: Record<string, AdminSortView>;
|
|
4503
|
+
/** URL of the create form, or `null` when creation is disabled. */
|
|
4504
|
+
newUrl: string | null;
|
|
4505
|
+
}
|
|
4506
|
+
/**
|
|
4507
|
+
* Render the paginated list view, with its search box, filters and sortable
|
|
4508
|
+
* column headers.
|
|
4509
|
+
*
|
|
4510
|
+
* @param context - The shared chrome data.
|
|
4511
|
+
* @param view - The prepared list view model.
|
|
4512
|
+
* @returns The full page.
|
|
4513
|
+
*/
|
|
4514
|
+
declare function renderListPage(context: AdminRenderContext, view: AdminListView): string;
|
|
4515
|
+
/** The view model the detail page renders. */
|
|
4516
|
+
interface AdminDetailView {
|
|
4517
|
+
/** Singular display name. */
|
|
4518
|
+
title: string;
|
|
4519
|
+
/** The row's identity, shown next to the title. */
|
|
4520
|
+
identity: string;
|
|
4521
|
+
/** One `(label, value)` pair per column. */
|
|
4522
|
+
fields: {
|
|
4523
|
+
label: string;
|
|
4524
|
+
value: string;
|
|
4525
|
+
}[];
|
|
4526
|
+
/** URL of the list view. */
|
|
4527
|
+
backUrl: string;
|
|
4528
|
+
/** URL of the edit form, or `null` when editing is disabled. */
|
|
4529
|
+
editUrl: string | null;
|
|
4530
|
+
/** URL the delete form posts to, or `null` when deletion is disabled. */
|
|
4531
|
+
deleteUrl: string | null;
|
|
4532
|
+
}
|
|
4533
|
+
/**
|
|
4534
|
+
* Render the single-record detail view.
|
|
4535
|
+
*
|
|
4536
|
+
* @param context - The shared chrome data (with an active session).
|
|
4537
|
+
* @param view - The prepared detail view model.
|
|
4538
|
+
* @returns The full page.
|
|
4539
|
+
* @throws Error When called without a session, since the write forms need a CSRF token.
|
|
4540
|
+
*/
|
|
4541
|
+
declare function renderDetailPage(context: AdminRenderContext, view: AdminDetailView): string;
|
|
4542
|
+
/** The view model the create/edit form renders. */
|
|
4543
|
+
interface AdminFormView {
|
|
4544
|
+
/** Whether the form creates a new record or edits an existing one. */
|
|
4545
|
+
mode: "create" | "edit";
|
|
4546
|
+
/** Singular display name. */
|
|
4547
|
+
title: string;
|
|
4548
|
+
/** The controls to render. */
|
|
4549
|
+
fields: AdminFormField[];
|
|
4550
|
+
/** URL the form posts to. */
|
|
4551
|
+
actionUrl: string;
|
|
4552
|
+
/** URL of the page to return to. */
|
|
4553
|
+
backUrl: string;
|
|
4554
|
+
/** A form-level error shown above the fields, or `null`. */
|
|
4555
|
+
error: string | null;
|
|
4556
|
+
}
|
|
4557
|
+
/**
|
|
4558
|
+
* Render the create/edit form.
|
|
4559
|
+
*
|
|
4560
|
+
* @param context - The shared chrome data (with an active session).
|
|
4561
|
+
* @param view - The prepared form view model.
|
|
4562
|
+
* @returns The full page.
|
|
4563
|
+
* @throws Error When called without a session, since the form needs a CSRF token.
|
|
4564
|
+
*/
|
|
4565
|
+
declare function renderFormPage(context: AdminRenderContext, view: AdminFormView): string;
|
|
4566
|
+
|
|
4567
|
+
/**
|
|
4568
|
+
* The server-rendered admin panel router, mirroring `admin.router`.
|
|
4569
|
+
*
|
|
4570
|
+
* Mounts a Django-style panel over an {@link AdminSite}: operators sign in with
|
|
4571
|
+
* a row from the project's own database, then browse, search, filter, sort,
|
|
4572
|
+
* create, edit and delete every registered model. Nothing here is JSON — the
|
|
4573
|
+
* responses are HTML pages built by `@/admin/templates` and styled by the one
|
|
4574
|
+
* stylesheet this package serves.
|
|
4575
|
+
*
|
|
4576
|
+
* ```text
|
|
4577
|
+
* GET {prefix}/static/admin.css the bundled stylesheet
|
|
4578
|
+
* GET {prefix}/login sign-in form
|
|
4579
|
+
* POST {prefix}/login credential check
|
|
4580
|
+
* GET {prefix}/mfa TOTP challenge (backends with MFA)
|
|
4581
|
+
* POST {prefix}/mfa TOTP verification
|
|
4582
|
+
* POST {prefix}/logout drop the session
|
|
4583
|
+
* GET {prefix}/ dashboard: row counts + system metrics
|
|
4584
|
+
* GET {prefix}/m/:slug list view: search, filters, sort, pages
|
|
4585
|
+
* GET {prefix}/m/:slug/new create form
|
|
4586
|
+
* POST {prefix}/m/:slug/new create
|
|
4587
|
+
* GET {prefix}/m/:slug/:identity detail view
|
|
4588
|
+
* GET {prefix}/m/:slug/:identity/edit edit form
|
|
4589
|
+
* POST {prefix}/m/:slug/:identity/edit update
|
|
4590
|
+
* POST {prefix}/m/:slug/:identity/delete delete
|
|
4591
|
+
* ```
|
|
4592
|
+
*
|
|
4593
|
+
* Every state-changing POST carries the session's CSRF token and is rejected
|
|
4594
|
+
* with `403` when it does not match.
|
|
4595
|
+
*/
|
|
4596
|
+
|
|
4597
|
+
/** Options for {@link makeAdminRouter}. */
|
|
4598
|
+
interface AdminRouterOptions {
|
|
4599
|
+
/** The engine the panel opens a session on for each request. */
|
|
4600
|
+
engine: AsyncEngine;
|
|
4601
|
+
/** How the login form turns credentials into a principal. */
|
|
4602
|
+
authBackend: AdminAuthBackend;
|
|
4603
|
+
/** HMAC key signing the session cookie. At least 32 characters. */
|
|
4604
|
+
secretKey: string;
|
|
4605
|
+
/** Mount prefix. Default `/admin`. */
|
|
4606
|
+
prefix?: string;
|
|
4607
|
+
/** Send the session cookie with `Secure`. Default `true` — turn it off only in local HTTP dev. */
|
|
4608
|
+
cookieSecure?: boolean;
|
|
4609
|
+
/** Session cookie name. Default `tempest_admin_session`. */
|
|
4610
|
+
cookieName?: string;
|
|
4611
|
+
/** Session lifetime in seconds. Default `28800` (8 hours). */
|
|
4612
|
+
sessionMaxAgeSeconds?: number;
|
|
4613
|
+
/** Show the CPU/memory panel on the dashboard. Default `true`. */
|
|
4614
|
+
showMetrics?: boolean;
|
|
4615
|
+
}
|
|
4616
|
+
/**
|
|
4617
|
+
* Build the admin panel router.
|
|
4618
|
+
*
|
|
4619
|
+
* @param site - The registered {@link AdminSite}.
|
|
4620
|
+
* @param options - Engine, auth backend, signing key and cookie/appearance flags.
|
|
4621
|
+
* @returns An Express router serving the whole panel under its prefix.
|
|
4622
|
+
* @throws Error When the signing key is shorter than 32 characters.
|
|
4623
|
+
*/
|
|
4624
|
+
declare function makeAdminRouter(site: AdminSite, options: AdminRouterOptions): Router;
|
|
4625
|
+
|
|
4626
|
+
/**
|
|
4627
|
+
* Headless admin: resource registry for the JSON admin API.
|
|
4628
|
+
*
|
|
4629
|
+
* The counterpart to the server-rendered panel in `@/admin/site`. Register one
|
|
4630
|
+
* {@link AdminJsonResource} per managed entity and {@link makeAdminJsonRouter}
|
|
4631
|
+
* exposes auto-derived CRUD + introspection endpoints your own frontend
|
|
4632
|
+
* (React, etc.) renders. Resources are callback-based, so they wire to a
|
|
4633
|
+
* `BaseService` — or any store — in a few lines and stay ORM-agnostic.
|
|
4634
|
+
*
|
|
4635
|
+
* Reach for this when the UI is yours; reach for {@link AdminSite} +
|
|
4636
|
+
* `makeAdminRouter` when you want the batteries-included HTML panel.
|
|
3503
4637
|
*/
|
|
3504
4638
|
|
|
3505
4639
|
/** A field descriptor a frontend uses to render list columns / form inputs. */
|
|
3506
|
-
interface
|
|
4640
|
+
interface AdminJsonField {
|
|
3507
4641
|
/** Field name (property key). */
|
|
3508
4642
|
name: string;
|
|
3509
4643
|
/** Loose type hint for rendering (`string`, `number`, `boolean`, `date`, …). */
|
|
@@ -3513,16 +4647,16 @@ interface AdminField {
|
|
|
3513
4647
|
/** Whether the field is read-only (shown, never submitted). */
|
|
3514
4648
|
readOnly?: boolean;
|
|
3515
4649
|
}
|
|
3516
|
-
/** A paginated list result returned by {@link
|
|
3517
|
-
interface
|
|
4650
|
+
/** A paginated list result returned by {@link AdminJsonResource.list}. */
|
|
4651
|
+
interface AdminJsonListResult<T = unknown> {
|
|
3518
4652
|
items: T[];
|
|
3519
4653
|
total: number;
|
|
3520
4654
|
page: number;
|
|
3521
4655
|
pageSize: number;
|
|
3522
4656
|
pages: number;
|
|
3523
4657
|
}
|
|
3524
|
-
/** Query parameters passed to {@link
|
|
3525
|
-
interface
|
|
4658
|
+
/** Query parameters passed to {@link AdminJsonResource.list}. */
|
|
4659
|
+
interface AdminJsonListQuery {
|
|
3526
4660
|
page: number;
|
|
3527
4661
|
pageSize: number;
|
|
3528
4662
|
/** Remaining query-string entries (domain filters). */
|
|
@@ -3532,13 +4666,13 @@ interface AdminListQuery {
|
|
|
3532
4666
|
* A managed resource. Only `name`, `fields` and `list`/`get` are required;
|
|
3533
4667
|
* omit a write callback to make that operation unavailable (405).
|
|
3534
4668
|
*/
|
|
3535
|
-
interface
|
|
4669
|
+
interface AdminJsonResource<T = unknown> {
|
|
3536
4670
|
/** URL-safe resource slug (e.g. `users`). */
|
|
3537
4671
|
name: string;
|
|
3538
4672
|
/** Field descriptors for list/detail/form rendering. */
|
|
3539
|
-
fields:
|
|
4673
|
+
fields: AdminJsonField[];
|
|
3540
4674
|
/** Return a page of records. */
|
|
3541
|
-
list(query:
|
|
4675
|
+
list(query: AdminJsonListQuery): Promise<AdminJsonListResult<T>>;
|
|
3542
4676
|
/** Return one record by id, or `null` when absent. */
|
|
3543
4677
|
get(id: string): Promise<T | null>;
|
|
3544
4678
|
/** Create a record from validated input. */
|
|
@@ -3553,7 +4687,7 @@ interface AdminResource<T = unknown> {
|
|
|
3553
4687
|
updateSchema?: z.ZodType;
|
|
3554
4688
|
}
|
|
3555
4689
|
/** A registry of admin resources. */
|
|
3556
|
-
declare class
|
|
4690
|
+
declare class AdminJsonSite {
|
|
3557
4691
|
readonly brand: string;
|
|
3558
4692
|
private readonly resources;
|
|
3559
4693
|
/**
|
|
@@ -3566,17 +4700,17 @@ declare class AdminSite {
|
|
|
3566
4700
|
* @param resource - The resource config.
|
|
3567
4701
|
* @returns The same resource (for chaining).
|
|
3568
4702
|
*/
|
|
3569
|
-
register<T>(resource:
|
|
4703
|
+
register<T>(resource: AdminJsonResource<T>): AdminJsonResource<T>;
|
|
3570
4704
|
/** Look up a resource by slug, or `null`. */
|
|
3571
|
-
get(name: string):
|
|
4705
|
+
get(name: string): AdminJsonResource | null;
|
|
3572
4706
|
/** Every registered resource. */
|
|
3573
|
-
list():
|
|
4707
|
+
list(): AdminJsonResource[];
|
|
3574
4708
|
}
|
|
3575
4709
|
|
|
3576
4710
|
/**
|
|
3577
4711
|
* Admin JSON router, mirroring `admin.router.make_admin_router`.
|
|
3578
4712
|
*
|
|
3579
|
-
* Exposes auto-derived CRUD + introspection over an {@link
|
|
4713
|
+
* Exposes auto-derived CRUD + introspection over an {@link AdminJsonSite}:
|
|
3580
4714
|
*
|
|
3581
4715
|
* ```text
|
|
3582
4716
|
* GET {prefix}/ site brand + resource list
|
|
@@ -3591,8 +4725,8 @@ declare class AdminSite {
|
|
|
3591
4725
|
* Pass a `guard` middleware (e.g. JWT + `requireRoles("admin")`) to protect it.
|
|
3592
4726
|
*/
|
|
3593
4727
|
|
|
3594
|
-
/** Options for {@link
|
|
3595
|
-
interface
|
|
4728
|
+
/** Options for {@link makeAdminJsonRouter}. */
|
|
4729
|
+
interface AdminJsonRouterOptions {
|
|
3596
4730
|
/** Route prefix. Default `/admin`. */
|
|
3597
4731
|
prefix?: string;
|
|
3598
4732
|
/** Guard middleware applied to every admin route (auth). */
|
|
@@ -3601,11 +4735,11 @@ interface AdminRouterOptions {
|
|
|
3601
4735
|
/**
|
|
3602
4736
|
* Build the admin router.
|
|
3603
4737
|
*
|
|
3604
|
-
* @param site - The registered {@link
|
|
4738
|
+
* @param site - The registered {@link AdminJsonSite}.
|
|
3605
4739
|
* @param options - Prefix and guard middleware.
|
|
3606
4740
|
* @returns An Express router with the admin endpoints mounted.
|
|
3607
4741
|
*/
|
|
3608
|
-
declare function
|
|
4742
|
+
declare function makeAdminJsonRouter(site: AdminJsonSite, options?: AdminJsonRouterOptions): Router;
|
|
3609
4743
|
|
|
3610
4744
|
/**
|
|
3611
4745
|
* Auth DTOs (Zod), mirroring `auth.schemas`.
|
|
@@ -4287,14 +5421,34 @@ declare function registerExceptionHandlers(app: Express, options?: RegisterExcep
|
|
|
4287
5421
|
*
|
|
4288
5422
|
* Swagger UI is served fully self-contained: its static assets ship with the
|
|
4289
5423
|
* `swagger-ui-dist` dependency and are mounted locally (no CDN), with a small
|
|
4290
|
-
* inline initializer pointing at the spec endpoint.
|
|
4291
|
-
*
|
|
4292
|
-
* ~1 MB and
|
|
4293
|
-
*
|
|
5424
|
+
* inline initializer pointing at the spec endpoint.
|
|
5425
|
+
*
|
|
5426
|
+
* Redoc's renderer is ~1 MB and is **not** vendored — the `redoc` package pulls
|
|
5427
|
+
* 22 dependencies and peers on `react`, `react-dom`, `styled-components`,
|
|
5428
|
+
* `mobx` and `core-js`, bounds no backend service should inherit just to render
|
|
5429
|
+
* a reference page. It is an **optional peer** instead: install `redoc` and
|
|
5430
|
+
* {@link mountRedoc} serves its standalone bundle from disk, so the page works
|
|
5431
|
+
* offline; without it the page falls back to the jsDelivr CDN and says so
|
|
5432
|
+
* out loud when the network blocks the bundle.
|
|
5433
|
+
*
|
|
5434
|
+
* Both pages declare an inline `<link rel="icon">`. Without one the browser
|
|
5435
|
+
* requests `/favicon.ico` at the origin root, which on an API-only service is a
|
|
5436
|
+
* 401, a 404 or an SPA catch-all — a red console error on every page load.
|
|
4294
5437
|
*/
|
|
4295
5438
|
|
|
4296
5439
|
/** A JSON-serializable OpenAPI document. */
|
|
4297
5440
|
type OpenApiDocument = Record<string, unknown>;
|
|
5441
|
+
/**
|
|
5442
|
+
* The bundled default favicon: a small SVG bolt as a `data:` URI.
|
|
5443
|
+
*
|
|
5444
|
+
* Inline on purpose — an asset route would be one more thing to mount, and the
|
|
5445
|
+
* whole point is to stop the browser from issuing a request the service cannot
|
|
5446
|
+
* answer. Pass {@link SwaggerOptions.favicon} to override it, or `false` to emit
|
|
5447
|
+
* no tag at all and let the browser fall back to `/favicon.ico`.
|
|
5448
|
+
*/
|
|
5449
|
+
declare const DEFAULT_DOCS_FAVICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiByeD0iNyIgZmlsbD0iIzRjNmVmNSIvPjxwYXRoIGQ9Ik0xNy45IDQuNSA4LjYgMTguNGg1LjJMMTMgMjcuNWw5LjQtMTMuOWgtNS4zeiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==";
|
|
5450
|
+
/** The jsDelivr URL used when no local Redoc bundle is available. */
|
|
5451
|
+
declare const REDOC_CDN_URL = "https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js";
|
|
4298
5452
|
/**
|
|
4299
5453
|
* Mount the OpenAPI document as JSON at `path`.
|
|
4300
5454
|
*
|
|
@@ -4307,6 +5461,29 @@ declare function mountOpenApiJson(app: Express, path: string, document: OpenApiD
|
|
|
4307
5461
|
interface SwaggerOptions {
|
|
4308
5462
|
/** Page title. Default `"API docs"`. */
|
|
4309
5463
|
title?: string;
|
|
5464
|
+
/**
|
|
5465
|
+
* Favicon URL or `data:` URI. Default {@link DEFAULT_DOCS_FAVICON}. Pass
|
|
5466
|
+
* `false` to emit no tag, letting the browser request `/favicon.ico`.
|
|
5467
|
+
*/
|
|
5468
|
+
favicon?: string | false;
|
|
5469
|
+
/**
|
|
5470
|
+
* Options merged into the `SwaggerUIBundle` constructor, after
|
|
5471
|
+
* {@link SWAGGER_UI_DEFAULTS} and before `presets`. Anything Swagger UI
|
|
5472
|
+
* accepts and JSON can carry.
|
|
5473
|
+
*
|
|
5474
|
+
* Two worth knowing:
|
|
5475
|
+
*
|
|
5476
|
+
* - `supportedSubmitMethods` — which verbs get a working **Try it out**.
|
|
5477
|
+
* Swagger UI enables all of them, so on an API with irreversible side
|
|
5478
|
+
* effects (sending, charging, dispatching) the docs page fires the real
|
|
5479
|
+
* thing. `["get"]` or `[]` narrows that.
|
|
5480
|
+
* - `layout: "StandaloneLayout"` — restores the Explore topbar, along with
|
|
5481
|
+
* the standalone preset script it needs.
|
|
5482
|
+
*
|
|
5483
|
+
* Function values throw at mount time rather than being dropped silently by
|
|
5484
|
+
* the JSON serialization.
|
|
5485
|
+
*/
|
|
5486
|
+
ui?: Record<string, unknown>;
|
|
4310
5487
|
}
|
|
4311
5488
|
/**
|
|
4312
5489
|
* Mount Swagger UI at `path`, reading the spec from `specUrl`.
|
|
@@ -4316,23 +5493,66 @@ interface SwaggerOptions {
|
|
|
4316
5493
|
* @param app - The Express application.
|
|
4317
5494
|
* @param path - Mount path for the UI (e.g. `/docs`).
|
|
4318
5495
|
* @param specUrl - URL the UI fetches the OpenAPI document from.
|
|
4319
|
-
* @param options - Page options.
|
|
5496
|
+
* @param options - Page and Swagger UI options.
|
|
5497
|
+
* @throws {Error} When `options.ui` carries a function value.
|
|
4320
5498
|
*/
|
|
4321
5499
|
declare function mountSwaggerUi(app: Express, path: string, specUrl: string, options?: SwaggerOptions): void;
|
|
5500
|
+
/** Where {@link mountRedoc} takes the standalone renderer bundle from. */
|
|
5501
|
+
type RedocBundleSource = "auto" | "local" | "cdn";
|
|
4322
5502
|
/** Options for {@link mountRedoc}. */
|
|
4323
5503
|
interface RedocOptions {
|
|
4324
5504
|
/** Page title. Default `"API reference"`. */
|
|
4325
5505
|
title?: string;
|
|
4326
|
-
/**
|
|
5506
|
+
/**
|
|
5507
|
+
* Favicon URL or `data:` URI. Default {@link DEFAULT_DOCS_FAVICON}. Pass
|
|
5508
|
+
* `false` to emit no tag, letting the browser request `/favicon.ico`.
|
|
5509
|
+
*/
|
|
5510
|
+
favicon?: string | false;
|
|
5511
|
+
/**
|
|
5512
|
+
* Where the renderer comes from. Default `"auto"`.
|
|
5513
|
+
*
|
|
5514
|
+
* - `"auto"` — serve the `redoc` optional peer's bundle from disk when it is
|
|
5515
|
+
* installed, fall back to the CDN when it is not.
|
|
5516
|
+
* - `"local"` — serve it from disk, and **throw at mount time** when `redoc`
|
|
5517
|
+
* is not installed. Use this when an air-gapped deploy must not silently
|
|
5518
|
+
* degrade into a CDN request.
|
|
5519
|
+
* - `"cdn"` — always load from {@link REDOC_CDN_URL}.
|
|
5520
|
+
*/
|
|
5521
|
+
bundle?: RedocBundleSource;
|
|
5522
|
+
/**
|
|
5523
|
+
* Absolute path to a Redoc standalone bundle to serve, instead of resolving
|
|
5524
|
+
* the `redoc` package. For a vendored copy, or a layout the resolver cannot
|
|
5525
|
+
* reach (the bundle is resolved from `process.cwd()`).
|
|
5526
|
+
*/
|
|
5527
|
+
bundlePath?: string;
|
|
5528
|
+
/**
|
|
5529
|
+
* Explicit URL for the bundle. Wins over {@link RedocOptions.bundle} and
|
|
5530
|
+
* {@link RedocOptions.bundlePath} — use it to point at a copy you already
|
|
5531
|
+
* serve yourself.
|
|
5532
|
+
*/
|
|
4327
5533
|
scriptUrl?: string;
|
|
4328
5534
|
}
|
|
5535
|
+
/**
|
|
5536
|
+
* Resolve the `redoc` package's standalone bundle from the application.
|
|
5537
|
+
*
|
|
5538
|
+
* Resolution starts at `process.cwd()`, not at this file: `redoc` is an
|
|
5539
|
+
* **optional peer**, so the copy that matters is the one the application
|
|
5540
|
+
* installed, and Node walks up from there to the project's `node_modules`.
|
|
5541
|
+
*
|
|
5542
|
+
* @returns The absolute path to the bundle, or `null` when `redoc` is absent.
|
|
5543
|
+
*/
|
|
5544
|
+
declare function resolveRedocBundle(): string | null;
|
|
4329
5545
|
/**
|
|
4330
5546
|
* Mount Redoc at `path`, reading the spec from `specUrl`.
|
|
4331
5547
|
*
|
|
5548
|
+
* By default the renderer is served from the `redoc` optional peer when it is
|
|
5549
|
+
* installed, so the page works offline; otherwise it falls back to the CDN.
|
|
5550
|
+
*
|
|
4332
5551
|
* @param app - The Express application.
|
|
4333
5552
|
* @param path - Mount path for Redoc (e.g. `/redoc`).
|
|
4334
5553
|
* @param specUrl - URL Redoc fetches the OpenAPI document from.
|
|
4335
5554
|
* @param options - Page and bundle options.
|
|
5555
|
+
* @throws {Error} When `bundle` is `"local"` and no bundle can be resolved.
|
|
4336
5556
|
*/
|
|
4337
5557
|
declare function mountRedoc(app: Express, path: string, specUrl: string, options?: RedocOptions): void;
|
|
4338
5558
|
|
|
@@ -5173,6 +6393,6 @@ declare function createTestDatabase(models: readonly ModelClass[]): TestDatabase
|
|
|
5173
6393
|
declare function withTestDatabase<T>(models: readonly ModelClass[], fn: (db: TestDatabase) => Promise<T>): Promise<T>;
|
|
5174
6394
|
|
|
5175
6395
|
/** The installed SDK version. Single source of truth for the barrel + CLI. */
|
|
5176
|
-
declare const VERSION = "0.
|
|
6396
|
+
declare const VERSION = "0.24.0";
|
|
5177
6397
|
|
|
5178
|
-
export { type ActivationInput, ActivationService, type ActivationServiceOptions, type ActivationStore, type
|
|
6398
|
+
export { ADMIN_CSS, type ActivationInput, ActivationService, type ActivationServiceOptions, type ActivationStore, type AdminAuthBackend, type AdminAutomapOptions, type AdminDashboardCard, type AdminDashboardMetrics, type AdminDetailView, type AdminFilterKind, type AdminFilterView, type AdminFormField, type AdminFormView, type AdminJsonField, type AdminJsonListQuery, type AdminJsonListResult, type AdminJsonResource, type AdminJsonRouterOptions, AdminJsonSite, type AdminListView, type AdminMessage, type AdminMfaVerifier, AdminModel, type AdminModelOptions, type AdminNavEntry, type AdminRenderContext, type AdminRouterOptions, type AdminRow, type AdminSelectOption, type AdminSession, AdminSessionStore, type AdminSessionStoreOptions, AdminSite, type AdminSiteOptions, type AdminSortView, type AdminTheme, type AdminWidget, AppException, type AppExceptionHandlerOptions, type AppExceptionOptions, type AttachWebSocketOptions, AttemptThrottle, type AttemptThrottleOptions, AuditAction, type AuthResponse, type AuthResultPageOptions, type AuthRouterOptions, type AuthUser, type BackupOptions, type BaseAppSettings, BaseAuditLogModel, BaseController, BaseModel, BaseOAuthClient, BaseOutboxModel, type BaseResponse, BaseService, BaseUserModel, BaseUserRefreshTokenModel, BaseUserTokenModel, type BodySizeLimitOptions, type BroadcastOptions, type BroadcastResult, type BrokerManager, type BuildFormFieldsOptions, CEP_PATTERN, CNPJ_PATTERN, CPF_PATTERN, type CPUMetrics, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type CacheManager, type CachedOptions, type CachedResponse, type CatalogData, CircuitOpenError, type ClientIpOptions, CompositeFeatureFlagBackend, ConflictException, type CreateAppOpenApi, type CreateAppOptions, type CsrfOptions, type CursorPaginationFilter, DEFAULT_DOCS_FAVICON, DEFAULT_LOCALE, type DownloadOptions, type EmailMessage, type EmailOptions, EmailProvider, type EmailProviderOptions, EmailUtils, type Enum, type EnumHelpers, type EnumSpec, EnvFeatureFlagBackend, EventStream, type EventStreamOptions, type ExceptionDetails, ExpiredTokenException, type FeatureFlagBackend, FeatureFlags, type FieldChange, type FileLoggingHandle, type FileLoggingOptions, type FlagContext, ForbiddenException, type GPUMetrics, type GenerateOpenApiOptions, GitHubOAuthClient, GoogleOAuthClient, GracefulShutdown, type GracefulShutdownOptions, HTTPClient, type HTTPClientOptions, HTTP_500_LOG_FILE, HTTP_500_MARKER, type HandshakeInfo, type HealthCheck, type HealthRouterOptions, HttpMetrics, IDEMPOTENCY_HEADER, type IdempotencyOptions, type IdempotencyRedisLike, type IdempotencyStore, type InboundHandler, type InboundMessage, InvalidTokenException, type IssuedSession, JSONLogger, JWTUtils, type JWTUtilsOptions, type JwtAuthOptions, type JwtClaims, type JwtDecoderLike, LEVEL_LOG_FILES, LocalUploadStorage, type LocalUploadStorageOptions, type LogEntry, type LogExtra, type LogLevel, type LogSink, type LogSource, type LoginInput, type LoginResult, type LogsRouterOptions, type MediaKind, MemoryBroker, MemoryCacheManager, MemoryFeatureFlagBackend, MemoryIdempotencyStore, type MemoryMetrics, MemoryRateLimitStore, MemorySessionStore, MemoryThrottleBackend, MessageCatalog, type MessageHandler, MessagingHub, type MessagingProvider, type MetricsRouterOptions, MetricsUtils, type MfaChallenge, type MfaChallengeInput, type MfaCodeInput, type MfaEnrollment, MfaService, type MfaServiceOptions, type MfaStore, NotFoundException, type OAuthClientOptions, OAuthError, type OAuthTokens, type OAuthUser, OIDCProvider, type OIDCProviderOptions, type OpenApiDocument, type OpenApiInfo, type OutboundMedia, type OutboundResult, type OutboxPublisher, OutboxRelay, type OutboxRelayOptions, OutboxStatus, PHONE_BR_PATTERN, type PaginationFilter, type PaginationLinkOptions, type ParsedAdminForm, type PasswordResetConfirmInput, type PasswordResetFormOptions, type PasswordResetRequestInput, PasswordResetService, type PasswordResetServiceOptions, type PasswordResetStore, PasswordUtils, REDOC_CDN_URL, REQUEST_ID_HEADER, RabbitBroker, type RabbitBrokerOptions, type RateLimitKeyFunc, type RateLimitOptions, type RateLimitRedisLike, type RateLimitResult, type RateLimitStore, RedisCacheManager, RedisIdempotencyStore, type RedisLike, type RedisPublisherLike, RedisRateLimitStore, RedisSSEBroker, type RedisSSEBrokerOptions, RedisSessionStore, type RedisSubscriberLike, type RedocBundleSource, type RedocOptions, type RefreshInput, Region, type RegionValue, type RegisterExceptionHandlersOptions, type RequestContext, type RequestTracingOptions, type ResolvedAdminTheme, type ResponseMapper, RetryPolicy, type RunServerOptions, type S3ClientLike, S3UploadStorage, type S3UploadStorageOptions, SSEBroker, type SaveOptions, type SendOptions, ServerSentEvent, type ServerSentEventInit, type Session, type SessionMiddlewareOptions, type SessionRedisLike, SessionService, type SessionServiceOptions, type SessionStore, type SignupInput, type SlowQueryOptions, type SpecProvider, type StateBR, type SwaggerOptions, type SyncFilter, type SystemMetrics, TOTPHelper, type TOTPOptions, type TaskHandler, TaskManager, type TaskManagerOptions, TelegramProvider, type TelegramProviderOptions, TenantScopedRepository, type TestDatabase, type ThrottleBackend, type ThrottleStatus, type ToDictOptions, type TokenPair, TooManyRequestsException, type TooManyRequestsOptions, type ToolSpecOptions, TwilioSmsProvider, type TwilioSmsProviderOptions, type TwilioWebhookOptions, UF, type UFValue, UnauthorizedException, type UnhandledExceptionHandlerOptions, type UploadResult, type UploadStorage, UserAuthService, type UserAuthServiceOptions, UserModelAuthBackend, type UserModelAuthBackendOptions, type UserPublic, type UserStore, UserTokenPurpose, VERSION, ValidationException, type WSEnvelope, WebPushDispatcher, type WebPushDispatcherOptions, WebPushError, WebPushGoneError, type WebPushKeys, type WebPushPayload, type WebPushSubscription, type WebSocketConnection, WebSocketHub, type WebSocketHubOptions, type WebSocketLike, type WebhookSignatureOptions, WebhookSignatureVerifier, WhatsAppProvider, type WhatsAppProviderOptions, type WhatsAppWebhookOptions, type WidgetSpec, activationSchema, addLogSink, adminColumns, adminThemeCss, attachWebSocketHub, authResponseSchema, authSettingsShape, backupDatabase, baseAppSettingsSchema, baseAppSettingsShape, baseResponseSchema, bearerToken, bodySizeLimitMiddleware, broadcastText, buildContentDisposition, buildFormFields, buildPaginationLinkHeader, cached, centsField, cepField, citiesByUf, cnpjField, coerceFlag, configureFileLogging, configureLogging, corsSettingsShape, cpfField, cpfOrCnpjField, createApp, createOpenApiRegistry, createTestDatabase, createdByColumn, csrfMiddleware, csrfTokenMatches, cursorPaginationFilterSchema, cursorPaginationSchema, databaseSettingsShape, decodeCursor, defaultMessageCatalog, defineEnum, deletedAtColumn, diffSnapshots, emailSettingsShape, encodeCursor, looseBoolean as envBoolean, envList, escapeHtml, filterForColumn, formatCellValue, formatFieldValue, generateCsrfToken, generateOAuthState, generateOpaqueToken, generateOpenApiDocument, getAuth, getClientIp, getConditions, getPaginationConditions, getRequestId, getState, hashOpaqueToken, hexColorField, humanizeField, idempotencyMiddleware, inboundMessageSchema, isColumnOptional, isSearchableColumn, isValidCep, isValidCity, isValidCnpj, isValidCpf, isValidCpfCnpj, isValidPhoneBr, isValidUf, jwtSettingsShape, keyByHeader, keyByIp, keyByJwtClaim, keyByJwtSubject, latitudeField, listStates, loadSettings, logEntrySchema, logSettingsShape, loginSchema, longitudeField, looseBoolean, makeAdminJsonRouter, makeAdminRouter, makeAppExceptionHandler, makeAuthRouter, makeFlagGuard, makeHealthRouter, makeJwtAuthMiddleware, makeLogsRouter, makeMetricsRouter, makeSessionMiddleware, makeToolSpecRouter, makeTwilioWebhookRouter, makeUnhandledExceptionHandler, makeWhatsAppWebhookRouter, mfaChallengeSchema, mfaCodeSchema, mfaEnrollResponseSchema, minioSettingsShape, modifyDict, mountOpenApiJson, mountRedoc, mountSwaggerUi, nonEmptyStrField, nonNegativeFloatField, nonNegativeIntField, normalizeCep, normalizeCnpj, normalizeCpf, normalizeCpfCnpj, normalizePhoneBr, normalizeUf, notFoundHandler, onlyDigits, paginationFilterSchema, paginationSchema, parseAcceptLanguage, parseCookies, parseFormBody, passwordResetConfirmSchema, passwordResetRequestSchema, percentField, phoneBrField, portField, positiveFloatField, positiveIntField, priceField, prometheusMiddleware, rabbitmqSettingsShape, rateLimitMiddleware, ratingField, ratioField, redisSettingsShape, refreshSchema, registerExceptionHandlers, renderAuthResultPage, renderDashboardPage, renderDetailPage, renderFormPage, renderLayout, renderListPage, renderLoginPage, renderMfaPage, renderPasswordResetFormPage, requestIdMiddleware, requestTracingMiddleware, requireRoles, resolveAdminTheme, resolveDownloadPath, resolveRedocBundle, runServer, runWithRequestContext, sendBytesDownload, sendFileDownload, serverSettingsShape, sessionCookie, sessionSettingsShape, setRequestId, signupSchema, slugField, snapshot, sseResponse, statesByRegion, syncFilterSchema, syncPaginationSchema, tableNameFor, toDict, toUtc, tokenFromUrl, tokenPairSchema, tokenSettingsShape, ufField, updatedByColumn, uploadSettingsShape, userPublicSchema, utcnow, validateTwilioSignature, verifyOpaqueToken, webPushKeysSchema, webPushPayloadSchema, webPushSettingsShape, webPushSubscriptionSchema, webSocketSettingsShape, widgetForColumn, withTestDatabase, wrapWithSlowQueryLog, wsEnvelopeSchema };
|