UNPKG

mevn-orm

Version:
330 lines (329 loc) 13.6 kB
import type { Knex } from 'knex'; import { BelongsToRelation, HasManyRelation, HasOneRelation } from './relation.js'; type Row = Record<string, unknown>; /** * Paginated query result returned by {@link Model.paginate}. * * @typeParam T - Model instance type contained in `data`. */ interface PaginatedResult<T extends Model> { /** Model instances for the current page. */ data: ModelCollection<T>; /** Total rows matching the scoped query across all pages. */ total: number; /** Requested page size. */ per_page: number; /** Current page number (1-based). */ current_page: number; /** Next page number, or `null` on the last page. */ next_page: number | null; /** Previous page number, or `null` on the first page. */ prev_page: number | null; /** Total number of pages. */ last_page: number; } /** * Array subclass returned by {@link Model.all} and {@link Model.paginate}. * * Extends `Array` so it remains compatible with array operations while adding * {@link ModelCollection.toArray | toArray()} for API serialisation. * * @typeParam T - Model instance type stored in the collection. */ declare class ModelCollection<T extends Model> extends Array<T> { /** * Serialises every model in the collection to a plain object. * * @returns Array of serialised records (respects each model's `hidden` fields). */ toArray(): Row[]; } /** * ActiveRecord-style base model backed by Knex. * * Extend this class for each database table. Table names are inferred from the * class name unless overridden via `override table`. Use static methods for * queries and instance methods for row-level persistence. * * @example * ```ts * class User extends Model { * override fillable = ['name', 'email', 'password'] * override hidden = ['password'] * } * * const user = await User.create({ name: 'Jane', email: 'jane@example.com' }) * const page = await User.orderBy('name').paginate(10) * ``` */ declare class Model { #private; [key: string]: any; /** * Resolved database table name for this model class. * * Honours subclass `override table` declarations. */ static get currentTable(): string; /** * Active scoped Knex query built by `where()`, `orderBy()`, and other chain methods. * * Consumed and reset by terminal methods such as `first()`, `all()`, and `paginate()`. */ static currentQuery: Knex.QueryBuilder<Row, Row[]> | undefined; /** * Resolves the database table name for this model class. * * Instantiates the subclass to read its `table` property, so explicit * `override table` values are honoured on static query paths. * * @returns Resolved table name. */ static resolveTable(this: typeof Model): string; /** * Returns the active scoped query, initialising one against the model table when absent. * * Used internally by chain methods like `orderBy()` when called without a prior `where()`. */ static ensureCurrentQuery(this: typeof Model): Knex.QueryBuilder<Row, Row[]>; /** Attributes allowed through {@link Model.save | save()} mass assignment. */ fillable: string[]; /** Attributes excluded from {@link Model.toArray | toArray()} and stripped after reads. */ hidden: string[]; /** Snake_case singular name derived from the class name (used for default foreign keys). */ modelName: string; /** Database table this model maps to. Override when inference does not match your schema. */ table: string; /** Primary key value, set after insert or load. */ id?: number; /** * Creates a model instance from a database row or plain object. * * @param properties - Initial attribute values. */ constructor(properties?: Row); /** * Inserts the current model using {@link Model.fillable | fillable} attributes and reloads it. * * @returns This instance with database-assigned fields (including `id`) populated. */ save(): Promise<this>; /** * Updates the current row by primary key and returns a refreshed model instance. * * @param properties - Columns and values to update. * @returns Refreshed instance with updated attributes. * @throws When the instance has no `id`. */ update(properties: Row): Promise<this>; /** * Deletes the current row by primary key. * * @throws When the instance has no `id`. */ delete(): Promise<void>; /** * Bulk-updates rows in the model table. * * When preceded by {@link Model.where | where()}, only scoped rows are updated. * The query scope is reset after execution. * * @param properties - Columns and values to update. * @returns Number of rows updated. */ static update(properties: Row): Promise<number | undefined>; /** * Bulk-deletes rows in the model table. * * When preceded by {@link Model.where | where()}, only scoped rows are deleted. * The query scope is reset after execution. * * @returns Number of rows deleted. */ static destroy(): Promise<number | undefined>; /** * Finds a single model by primary key. * * @param id - Primary key value. * @param columns - Columns to select (default `'*'`). * @returns Model instance, or `null` when not found. Preserves the derived class type. */ static find<T extends typeof Model>(this: T, id: number | string, columns?: string | string[]): Promise<InstanceType<T> | null>; /** * Finds a model by primary key or throws when it does not exist. * * @param id - Primary key value. * @param columns - Columns to select (default `'*'`). * @returns Model instance. Preserves the derived class type. * @throws When no row matches the given id. */ static findOrFail<T extends typeof Model>(this: T, id: number | string, columns?: string | string[]): Promise<InstanceType<T>>; /** * Inserts a row and returns the created model instance. * * @param properties - Column values to insert. * @returns Created model with `hidden` fields stripped. Preserves the derived class type. */ static create<T extends typeof Model>(this: T, properties: Row): Promise<InstanceType<T>>; /** * Inserts multiple rows sequentially and returns created model instances. * * @param properties - Array of column value objects to insert. * @returns Created model instances in insertion order. */ static createMany<T extends typeof Model>(this: T, properties: Row[]): Promise<InstanceType<T>[]>; /** * Returns the first row matching `attributes`, or creates one with merged values. * * @param attributes - Lookup conditions. * @param values - Additional values used only when creating a new row. * @returns Existing or newly created model instance. */ static firstOrCreate<T extends typeof Model>(this: T, attributes: Row, values?: Row): Promise<InstanceType<T>>; /** * Starts a scoped query with a `where` clause. * * Chain further constraints (`orderBy`, `limit`, …) then call a terminal method * (`first`, `all`, `paginate`, `count`, `update`, `destroy`). * * @param conditions - Equality conditions passed to Knex `where`. * @returns Model constructor for chaining. */ static where<T extends typeof Model>(this: T, conditions?: Row): T; /** * Appends an `orderBy` clause to the current scoped query. * * @param column - Column to sort by. * @param direction - Sort direction (`'asc'` or `'desc'`). Defaults to `'asc'`. * @returns Model constructor for chaining. */ static orderBy<T extends typeof Model>(this: T, column: string, direction?: 'asc' | 'desc'): T; /** * Appends a `limit` clause to the current scoped query. * * @param count - Maximum number of rows to return. * @returns Model constructor for chaining. */ static limit<T extends typeof Model>(this: T, count: number): T; /** * Appends an `offset` clause to the current scoped query. * * @param count - Number of rows to skip (commonly paired with {@link Model.limit | limit()}). * @returns Model constructor for chaining. */ static offset<T extends typeof Model>(this: T, count: number): T; /** * Returns the first model matching the current scope. * * When no scope is active, returns the first row in the table. * The query scope is reset after execution. * * @param columns - Columns to select (default `'*'`). * @returns First matching model, or `null` when none found. */ static first<T extends typeof Model>(this: T, columns?: string | string[]): Promise<InstanceType<T> | null>; /** * Returns all models matching the current scope. * * When no scope is active, returns every row in the table. * The query scope is reset after execution. * * @param columns - Columns to select (default `'*'`). * @returns {@link ModelCollection} of matching models. */ static all<T extends typeof Model>(this: T, columns?: string | string[]): Promise<ModelCollection<InstanceType<T>>>; /** * Returns a paginated result set for the current scope. * * Runs a count query and a data query against the scoped builder. * The query scope is reset after execution. * * @param perPage - Items per page (default `15`). * @param page - Page number, 1-based (default `1`). * @param columns - Columns to select (default `'*'`). * @returns Paginated data and metadata. * * @example * ```ts * const result = await Post.where({ published: true }).orderBy('created_at', 'desc').paginate(10, 2) * ``` */ static paginate<T extends typeof Model>(this: T, perPage?: number, page?: number, columns?: string | string[]): Promise<PaginatedResult<InstanceType<T>>>; /** * Returns a row count for the current scope. * * When no scope is active, counts all rows in the table. * The query scope is reset after execution. * * @param column - Column to count (default `'*'` for all rows). * @returns Matching row count. */ static count(this: typeof Model, column?: string): Promise<number>; /** * Serialises the model to a plain object for API responses. * * Excludes ORM internals (`fillable`, `hidden`, `modelName`, `table`) and * any attributes listed in {@link Model.hidden | hidden}. * * @returns Plain data object safe to return from HTTP handlers. */ toArray(): Row; /** * Removes internal and hidden fields from a model instance in place. * * @param model - Model object to strip. * @param keepInternalState - When `true`, retains `fillable` and `hidden` keys. * @returns The same object reference with keys removed. */ stripColumns<T extends Record<string, unknown>>(model: T, keepInternalState?: boolean): T; } interface Model { /** * Defines a one-to-one relationship to another model. * * @param Related - Related model class constructor. * @param localKey - Parent key value (defaults to `this.id`). * @param foreignKey - Foreign key column on the related table (defaults to `{modelName}_id`). * @returns Lazy {@link HasOneRelation} — await directly or chain `where()` before executing. * * @example * ```ts * const profile = await user.profile() * const active = await user.profile().where({ active: true }).first() * ``` */ hasOne<T extends typeof Model>(Related: T, localKey?: number | string, foreignKey?: string): HasOneRelation<InstanceType<T>>; /** * Defines a one-to-many relationship to another model. * * @param Related - Related model class constructor. * @param localKey - Parent key value (defaults to `this.id`). * @param foreignKey - Foreign key column on the related table (defaults to `{modelName}_id`). * @returns Lazy {@link HasManyRelation} — await for all rows or call `.get()` / `.first()`. * * @example * ```ts * const posts = await user.posts() * const drafts = await user.posts().where({ status: 'draft' }).get() * ``` */ hasMany<T extends typeof Model>(Related: T, localKey?: number | string, foreignKey?: string): HasManyRelation<InstanceType<T>>; /** * Defines an inverse belongs-to relationship to a parent model. * * @param Related - Parent model class constructor. * @param foreignKey - Foreign key column on this model (defaults to `{relatedModelName}_id`). * @param ownerKey - Primary key column on the parent table (defaults to `'id'`). * @returns Lazy {@link BelongsToRelation} — await directly or chain `where()` before executing. * * @example * ```ts * const author = await post.author() * ``` */ belongsTo<T extends typeof Model>(Related: T, foreignKey?: string, ownerKey?: string): BelongsToRelation<InstanceType<T>>; } export { Model, ModelCollection }; export type { PaginatedResult }; export { HasOneRelation, HasManyRelation, BelongsToRelation, Relation } from './relation.js'; export { DB, getDB, configure, createKnexConfig, configureDatabase, setMigrationConfig, getMigrationConfig, makeMigration, migrateLatest, migrateRollback, migrateCurrentVersion, migrateList, } from './config.js';