import { DataSource } from '../database/DataSource'; import { DataTable } from '../database/DataTable'; import { PaginationResult } from '../database/PaginationResult'; import { Field } from '../database/query'; import { Condition, ConditionGroup, ExistsSubquery } from '../database/query/conditions'; import { OrderByDirection } from '../database/query/features/HasOrderByFields'; import { CastType } from './CastType'; import { EntityQuery } from './EntityQuery'; import { Relationship } from './Relationship'; export interface EntityMeta { table: string; primaryKey: string; fields: string[]; columnsMap: Record; autoGeneratedFields: string[]; casts: Record; relationships: Record; serializableFields: string[]; nonSerializableFields: string[]; source: string | null; } /** * Called by the @Entity decorator to register an entity's metadata. * This is the only place that writes to _metaStore. */ export declare function registerEntityMeta(entityClass: any, meta: EntityMeta): void; /** * Registers a named global scope for an entity class. * Called from BaseEntity.addGlobalScope() or directly. */ export declare function addGlobalScopeToStore(entityClass: any, name: string, fn: (query: any) => void | Promise): void; /** * Returns all registered global scopes for an entity class. */ export declare function getGlobalScopes(entityClass: any): Map void | Promise>; /** * Calls entityClass.booted() once per class (idempotent). * Invoked by Repository.get() the first time a repo is created for a class. */ export declare function bootEntityClass(entityClass: any): void; /** * Static facade for accessing entity repositories. * * Works with any entity class, whether or not it extends BaseEntity: * * Repository.get(Country).where('name', 'Argentina').first() * Repository.get(Country).save(countryInstance) * Repository.get(Country).delete(countryInstance) */ export declare class Repository { static get(entityClass: new (...args: any[]) => T): EntityRepository; } /** * All query, hydration, and persistence logic for a single entity class. * This is the single source of truth — BaseEntity methods are thin delegates * to this class, and plain (non-BaseEntity) entities use it directly via * Repository.get(EntityClass). * * Metadata is read from the module-level _metaStore, populated by @Entity. * The entity class itself is never modified by any decorator. */ export declare class EntityRepository { private readonly _entityClass; constructor(entityClass: new (...args: any[]) => T); private _meta; get table(): string; get primaryKey(): string; get fields(): string[]; get columnsMap(): Record; get autoGeneratedFields(): string[]; get casts(): Record; get relationships(): Record; get sourceName(): string | null; getSource(): DataSource; /** Creates a fresh EntityQuery for this entity. */ query(): EntityQuery; /** Hydrates a database row into a typed entity instance, applying any casts. */ fromRow(row: Record): T; /** The set of property names tracked for dirty-checking on a given instance. */ private _trackedFields; /** Deep-clones a value so later in-place mutations don't corrupt the clean snapshot. */ private _cloneValue; /** Value-equality used for dirty checks: handles Date and plain object/array (json/array casts). */ private _valuesEqual; /** * Snapshots the instance's current field values as "clean" — the baseline * against which future changes are compared. Called after hydration * (fromRow / fromJSON) and after every save(). */ private _markClean; /** * Returns the property names whose current value differs from the last * clean snapshot (or every set property, if the instance was never * hydrated/saved — e.g. a brand-new `new User()`). */ private _dirtyFields; /** * True if the instance (or, when given, a specific property) has unsaved * changes relative to its last hydration/save. * * user.email = 'new@x.com' * Repository.get(User).isDirty(user) // true * Repository.get(User).isDirty(user, 'name') // false */ isDirty(instance: T, field?: string): boolean; /** * Returns a `{ property: value }` map of only the properties that changed * since the instance's last hydration/save. */ getDirty(instance: T): Record; /** * Serializes an entity instance into a plain, JSON-safe object. * * - Own data properties and prototype getters are included. * - `Date` values are converted to ISO-8601 strings. * - Loaded relationships (and any nested entity values) are serialized * recursively into plain objects. * * Works with any entity class, whether or not it extends BaseEntity: * * Repository.get(Country).toJSON(countryInstance) */ toJSON(instance: T): Record; /** * Re-hydrates a plain object (typically the output of toJSON, or the result * of JSON.parse on a serialized entity) back into a typed entity instance. * * - Fields declared with a cast are coerced through it (e.g. `cast: 'date'` * revives Date instances, `cast: 'json'` keeps the parsed object, …). * - String values that look like full ISO-8601 timestamps are revived into * Date instances even when no cast was declared. * - Relationships are re-hydrated recursively into their related entity types * (single instance for hasOne/belongsTo, array for hasMany/*Through). * * Works with any entity class, whether or not it extends BaseEntity: * * Repository.get(Country).fromJSON(countryInstanceJSON) */ fromJSON(json: Record): T; /** Builds the plain object for an instance, guarding against circular graphs. */ private _serialize; /** Recursively converts a single value into a JSON-safe representation. */ private _serializeValue; /** Collects own enumerable properties plus @Serializable() getters, minus @Serializable(false) fields. */ private _serializableKeys; /** Revives a non-cast value, turning full ISO-8601 strings into Date instances. */ private _reviveValue; /** Assigns a value, silently skipping read-only / getter-only properties. */ private _assign; find(id: any): Promise; get(): Promise; first(): Promise; count(column?: Field): Promise; sum(column: Field): Promise; avg(column: Field): Promise; min(column: Field): Promise; max(column: Field): Promise; toBase(): Promise; paginate(perPage?: number, page?: number): Promise>; where(callback: (group: ConditionGroup) => void): EntityQuery; where(condition: Condition): EntityQuery; where(field: Field, value: any): EntityQuery; where(field: Field, operator: string, value: any): EntityQuery; whereIn(field: Field, values: any[]): EntityQuery; whereNotIn(field: Field, values: any[]): EntityQuery; whereBetween(field: Field, range: [any, any]): EntityQuery; whereNotBetween(field: Field, range: [any, any]): EntityQuery; whereNull(field: Field): EntityQuery; whereNotNull(field: Field): EntityQuery; whereLike(field: Field, pattern: string, caseSensitive?: boolean): EntityQuery; whereNotLike(field: Field, pattern: string, caseSensitive?: boolean): EntityQuery; whereColumn(field: Field, column: Field): EntityQuery; whereColumn(field: Field, operator: string, column: Field): EntityQuery; whereExists(subquery: ExistsSubquery): EntityQuery; whereArrayContains(field: Field, value: any): EntityQuery; whereNotExists(subquery: ExistsSubquery): EntityQuery; orderBy(field: Field, direction?: OrderByDirection): EntityQuery; orderByDesc(field: Field): EntityQuery; groupBy(...fields: Field[]): EntityQuery; having(condition: Condition): EntityQuery; having(field: Field, value: any): EntityQuery; having(field: Field, operator: string, value: any): EntityQuery; orHaving(condition: Condition): EntityQuery; orHaving(field: Field, value: any): EntityQuery; orHaving(field: Field, operator: string, value: any): EntityQuery; distinct(): EntityQuery; limit(value: number): EntityQuery; offset(value: number): EntityQuery; select(...fields: (Field | Field[])[]): EntityQuery; with(relations: Record) => void>): EntityQuery; with(relations: string | string[], ...rest: string[]): EntityQuery; when(condition: any, callback: (query: EntityQuery) => void): EntityQuery; whereHas(relationName: string, callback?: (query: EntityQuery) => void): EntityQuery; orWhereHas(relationName: string, callback?: (query: EntityQuery) => void): EntityQuery; joinRelationship(relationName: string): EntityQuery; innerJoinRelationship(relationName: string): EntityQuery; leftJoinRelationship(relationName: string): EntityQuery; /** * Persists an entity instance to the database. * - Primary key set → UPDATE. Only properties that changed since the last * hydration/save (the "dirty" fields) are written — * untouched columns are left out of the SET clause * entirely. If nothing is dirty, no query is issued. * - Primary key unset → INSERT (auto-generated columns excluded; generated * PK is written back onto the instance). * * After a successful save, every written field is marked clean again, so a * subsequent save() only touches whatever changes next: * * user.email = 'new@x.com' * await user.save() // UPDATE users SET email = 'new@x.com' WHERE id = ? * await user.save() // nothing dirty — no query issued */ save(instance: T): Promise; /** * Maps an entity-property record into a column-keyed storage row: auto-generated * columns and `undefined` values are skipped, property names are translated to * column names, and declared casts (`date`, `json`, `array`, …) are applied for * storage. Shared by insert() and upsert(). * * `keepProps` forces the given properties to be written even when they are * auto-generated. upsert() uses it for the conflict-target columns: an * `ON CONFLICT (id) DO UPDATE` can only match when the auto-generated key `id` * is actually present in the INSERT, otherwise every row is inserted as new. */ private _toStorageRow; /** * Inserts the given records in a single batch statement. Mirrors Eloquent's * `Model::insert($values)`: * * await Repository.get(Match).insert([ * { roundId: 4, position: 0, homeCompetitorIds: [1], status: 0 }, * { roundId: 4, position: 1, homeCompetitorIds: [2], status: 0 } * ]) * * `values` are expressed in terms of entity property names; this method maps * them to column names and applies the declared casts for storage. Auto-generated * columns (e.g. the primary key) are never written and generated keys are not * read back onto the records (use save() for a single record when you need the * generated id). Returns the number of affected rows. */ insert(values: Record[]): Promise; /** * Inserts the given records, updating the conflicting columns when a row * already exists. Mirrors Eloquent's `Model::upsert($values, $uniqueBy, $update)`. * * await Repository.get(PlayerStatistics).upsert( * [{ playerId: 7, points: 120, updatedAt: new Date() }], * 'playerId', * ['points', 'updatedAt'] * ) * * `values`, `uniqueBy` and `update` are expressed in terms of entity property * names; this method maps them to column names and applies the declared casts * (e.g. `date`, `json`, `array`) for storage. When `update` is omitted, every * provided column that is not part of `uniqueBy` is updated. Auto-generated * columns are not written EXCEPT when they are part of `uniqueBy` (the conflict * target must be present in the INSERT for `ON CONFLICT` to match — e.g. an * upsert keyed on the auto-generated primary key). */ upsert(values: Record[], uniqueBy: string | string[], update?: string[]): Promise; /** * Deletes an entity instance from the database by its primary key. * Throws if the primary key is not set on the instance. */ delete(instance: T): Promise; }