import { DbEntity, EntityConstructor, ForeignKeyAction, IndexMetadata, IndexMethod, StatisticsMetadata } from './entity-base'; import { ColumnBuilder, IdentityOptions } from '../schema/column-builder'; import type { PartitionStrategy } from '../schema/table-builder'; import { TypeMapper } from '../types/type-mapper'; import { CollationDefinition } from '../types/collation-builder'; import { DbColumn } from './db-column'; import { SqlFragment } from '../query/conditions'; /** * Fluent API for configuring entity properties */ export declare class EntityPropertyBuilder { private entityClass; private propertyKey; private columnBuilder; constructor(entityClass: EntityConstructor, propertyKey: keyof TEntity, columnBuilder: ColumnBuilder); /** * Set the database column name */ hasColumnName(name: string): this; /** * Mark as primary key */ isPrimaryKey(): this; /** * Mark as required */ isRequired(): this; /** * Set default value */ hasDefaultValue(value: any): this; /** * Mark as unique */ isUnique(): this; /** * Set custom type mapper for bidirectional transformation */ hasCustomMapper(mapper: TypeMapper): this; /** * Set the collation for this column */ hasCollation(collation: CollationDefinition): this; /** * Mark column as GENERATED ALWAYS AS IDENTITY (modern PostgreSQL identity columns) * This is the preferred way over serial/bigserial for auto-incrementing primary keys */ generatedAlwaysAsIdentity(options?: IdentityOptions): this; } /** * Base navigation builder with shared functionality */ declare abstract class BaseNavigationBuilder { protected entityClass: EntityConstructor; protected propertyKey: keyof TEntity; protected targetEntity: () => EntityConstructor; protected relationType: 'one' | 'many'; constructor(entityClass: EntityConstructor, propertyKey: keyof TEntity, targetEntity: () => EntityConstructor, relationType: 'one' | 'many'); /** * Extract key parts from selector (supports single or multiple columns, and literal values). * Returns property name strings for column references, and "__LIT:value" for constants. */ protected extractKeyPartsFromSelector(selector: Function): string[]; protected setForeignKeyMetadata(foreignKeyParts: string[]): void; protected setPrincipalKeyMetadata(principalKeyParts: string[]): void; /** * Mark navigation as required */ isRequired(): this; /** * Specify the ON DELETE action for the foreign key constraint */ onDelete(action: ForeignKeyAction): this; /** * Specify the ON UPDATE action for the foreign key constraint */ onUpdate(action: ForeignKeyAction): this; /** * Specify a custom name for the foreign key constraint */ hasDbName(name: string): this; /** * Mark this as an inverse navigation (no FK constraint will be created). * * Use this when the FK constraint is defined on the OTHER entity. * This is useful for 1-to-1 relationships where you want navigation * from both sides but the FK only exists on one side. * * @example * // UserEshopVerification has the FK to UserEshop * model.entity(UserEshopVerification, entity => { * entity.hasOne(e => e.userEshop, () => UserEshop) * .withForeignKey(e => e.userEshopId) * .withPrincipalKey(e => e.id) * .onDelete('cascade'); * }); * * // UserEshop has inverse navigation (no FK created) * model.entity(UserEshop, entity => { * entity.hasOne(e => e.userEshopVerification, () => UserEshopVerification) * .withForeignKey(e => e.id) * .withPrincipalKey(e => e.userEshopId) * .isInverseNavigation(); // <-- No FK constraint created * }); */ isInverseNavigation(): this; } /** * Navigation builder for hasMany relationships (one-to-many) * Foreign key is on the target entity (many side) * Principal key is on the source entity (one side) */ export declare class HasManyNavigationBuilder extends BaseNavigationBuilder { /** * Specify the foreign key on the target entity (many side) * Supports single column: p => p.userId * Supports multiple columns: p => [p.userId, p.type] * Supports SQL fragments: p => sql`...` */ withForeignKey(foreignKey: (entity: TTarget) => DbColumn | (DbColumn | number | string | boolean)[] | SqlFragment): this; /** * Specify the principal key on the source entity (one side) * Supports single column: u => u.id * Supports multiple columns: u => [u.id, u.type] * Supports SQL fragments: u => sql`...` */ withPrincipalKey(principalKey: (entity: TEntity) => DbColumn | (DbColumn | number | string | boolean)[] | SqlFragment): this; } /** * Navigation builder for hasOne relationships (many-to-one or one-to-one) * Foreign key is on the source entity * Principal key is on the target entity */ export declare class HasOneNavigationBuilder extends BaseNavigationBuilder { /** * Specify the foreign key on the source entity * Supports single column: p => p.userId * Supports multiple columns: p => [p.userId, p.type] * Supports SQL fragments: p => sql`...` */ withForeignKey(foreignKey: (entity: TEntity) => DbColumn | (DbColumn | number | string | boolean)[] | SqlFragment): this; /** * Specify the principal key on the target entity * Supports single column: u => u.id * Supports multiple columns: u => [u.id, u.type] * Supports SQL fragments: u => sql`...` */ withPrincipalKey(principalKey: (entity: TTarget) => DbColumn | (DbColumn | number | string | boolean)[] | SqlFragment): this; } /** * Legacy navigation builder for backward compatibility * @deprecated Use HasManyNavigationBuilder or HasOneNavigationBuilder */ export declare class EntityNavigationBuilder extends BaseNavigationBuilder { withForeignKey(foreignKey: (entity: TTarget) => any): this; withForeignKey(foreignKey: (entity: TEntity) => any): this; withPrincipalKey(principalKey: (entity: TEntity) => any): this; withPrincipalKey(principalKey: (entity: TTarget) => any): this; } /** * Fluent API for configuring an entity */ export declare class EntityConfigBuilder { private entityClass; constructor(entityClass: EntityConstructor); /** * Set the table name */ toTable(name: string): this; /** * Set the schema name */ toSchema(name: string): this; /** * Configure a property */ property(selector: (entity: TEntity) => TEntity[K]): EntityPropertyConfigBuilder; /** * Configure a one-to-many navigation */ hasMany(selector: (entity: TEntity) => TEntity[K], targetEntity: () => EntityConstructor): HasManyNavigationBuilder; /** * Configure a many-to-one or one-to-one navigation */ hasOne(selector: (entity: TEntity) => TEntity[K], targetEntity: () => EntityConstructor): HasOneNavigationBuilder; /** * Configure an index */ hasIndex(indexName: string, selector?: (entity: TEntity) => Array): IndexBuilder; /** * Declare a PostgreSQL extended-statistics object * (`CREATE STATISTICS [(kinds)] ON FROM `). * * Plain columns come from the selector (same proxy mechanism as * {@link hasIndex}); expressions the selector helpers cannot express — e.g. * infix operators like a bitmask test — are supplied as raw SQL via * `.withExpression()` on the returned builder. A single-expression * declaration produces univariate EXPRESSION statistics (the fix for * planner misestimates of predicates like `(flags & 1::smallint) = 0`); * two or more entries produce multivariate statistics, optionally narrowed * with `.withKinds()`. * * Reconciled by NAME only: `ensureCreated()` / `migrate()` create the object * when a table has no same-named one and run `ANALYZE
` so the * statistics take effect immediately. Definitions are never diffed or * rebuilt — rename the object to change it. * * @example * // Univariate expression statistics (planner selectivity for a bitmask test): * entity.hasStatistics('stx_resort_admin_visible_flags') * .withExpression('("flags" & 1::smallint)'); * * // Multivariate dependencies between two columns: * entity.hasStatistics('stx_city_zip', e => [e.city, e.zip]).withKinds('dependencies'); */ hasStatistics(statisticsName: string, selector?: (entity: TEntity) => Array): StatisticsBuilder; /** * Configure declarative PostgreSQL table partitioning — the parent table's * `PARTITION BY ()` clause. Child partitions * (`PARTITION OF ... FOR VALUES ...`) are created/rotated separately (usually * at runtime), so they are not declared here. * * **Note:** PostgreSQL requires every partition-key column to be part of the * table's PRIMARY KEY / UNIQUE constraints. * * @example * // Strongly-typed, column-based (single or composite key): * entity.hasPartitioning({ strategy: 'range', columns: e => e.createdAt }); * entity.hasPartitioning({ strategy: 'list', columns: e => [e.region] }); * entity.hasPartitioning({ strategy: 'hash', columns: e => [e.tenantId] }); * * @example * // Custom raw key expression (e.g. RANGE over a function of a column): * entity.hasPartitioning({ strategy: 'range', expression: "date_trunc('month', created_at)" }); */ hasPartitioning(options: { strategy: PartitionStrategy; columns: (entity: TEntity) => TEntity[keyof TEntity] | Array; }): this; hasPartitioning(options: { strategy: PartitionStrategy; expression: string; }): this; /** * Resolve a partition-key column selector (e.g. `e => e.createdAt` or * `e => [e.region, e.createdAt]`) to database column names, using the same * proxy mechanism as {@link hasIndex}. */ private resolvePartitionColumns; /** * Mark a column (by DB column name) as participating in an ixNormalized index. * Sets the flag on both the property metadata and the column config. */ private markColumnNormalized; /** * Extract property name from selector */ private extractPropertyName; } /** * Index configuration builder */ export declare class IndexBuilder { private entityClass; private indexMetadata; constructor(entityClass: EntityConstructor, indexMetadata: IndexMetadata); /** * Mark the index as unique */ isUnique(): this; /** * Set the index method (e.g., 'gin', 'gist', 'hash') */ using(method: IndexMethod): this; /** * Set the operator class for index columns (e.g., 'gin_trgm_ops') */ withOperatorClass(opClass: string): this; /** * Create the index with `CREATE INDEX CONCURRENTLY`. The statement cannot run * inside a transaction, so callers must ensure the migration executes in * autocommit mode (e.g. through `DbSchemaManager.migrate()` / `ensureCreated()` * rather than the transactional journal-based runner). */ concurrent(): this; /** * Set raw SQL expressions for expression-based index columns. * When set, expressions are used instead of column names. * Use with helper functions like lower() and unaccent() for composability. * * @example * entity.hasIndex('idx_name_unaccent') * .withExpression(lower(unaccent('name'))) * * entity.hasIndex('idx_multi') * .withExpression(lower('email'), lower(unaccent('name'))) */ withExpression(...expressions: string[]): this; /** * Add a WHERE clause for a partial index. * * @example * entity.hasIndex('idx_active_users', e => [e.email]) * .where('active = true') */ where(condition: string): this; /** * Opt into `NULLS NOT DISTINCT` (PostgreSQL 15+) so the unique index treats * NULLs as equal — at most one row may hold NULL in the indexed column(s), * instead of PostgreSQL's default of allowing unlimited NULL rows. * * Only meaningful together with {@link isUnique}: PostgreSQL rejects the clause * on a non-unique index, so it is emitted only when the index is also unique. * * @example * entity.hasIndex('uq_user_external_ref', e => [e.externalRef]) * .isUnique() * .nullsNotDistinct() */ nullsNotDistinct(): this; } /** * Fluent configuration for an extended-statistics object declared via * {@link EntityTypeBuilder.hasStatistics}. Mutates the metadata object pushed * by `hasStatistics`, mirroring how {@link IndexBuilder} works. */ export declare class StatisticsBuilder { private entityClass; private statisticsMetadata; constructor(entityClass: EntityConstructor, statisticsMetadata: StatisticsMetadata); /** * Append raw SQL entries to the statistics `ON` list — the escape hatch for * expressions the selector helpers cannot express (infix operators, casts). * PostgreSQL requires each expression entry to be parenthesized. * * @example * entity.hasStatistics('stx_flags_system') * .withExpression('("flags" & 1::smallint)') */ withExpression(...expressions: string[]): this; /** * Restrict the multivariate statistics kinds built for a 2+-entry * declaration. Do not set kinds on a single-expression declaration — * PostgreSQL rejects the combination. */ withKinds(...kinds: Array<'ndistinct' | 'dependencies' | 'mcv'>): this; } /** * Property configuration builder that requires a column type */ export declare class EntityPropertyConfigBuilder { private entityClass; private propertyKey; constructor(entityClass: EntityConstructor, propertyKey: K); /** * Set the column type */ hasType(columnBuilder: ColumnBuilder): EntityPropertyBuilder; } export {}; //# sourceMappingURL=entity-builder.d.ts.map