UNPKG

orange-orm

Version:

Object Relational Mapper

939 lines (810 loc) 41.6 kB
//map2.d.ts - Extended Row Types with Relations Support import type { PGliteOptions } from './pglite.d.ts'; import type { ConnectionConfiguration } from 'tedious'; import type { D1Database } from '@cloudflare/workers-types'; import type { PoolAttributes } from 'oracledb'; import type { AxiosInterceptorManager, InternalAxiosRequestConfig, AxiosResponse } from 'axios'; export type ORMColumnType = 'string' | 'bigint' | 'uuid' | 'date' | 'numeric' | 'boolean' | 'json' | 'binary'; // Base column definition with space-prefixed required properties export type ORMColumnDefinition = { ' type': ORMColumnType; ' notNull'?: boolean; ' notNullExceptInsert'?: boolean; }; // JSON column definition with custom TypeScript type export type ORMJsonColumnDefinition<T = any> = { ' type': 'json'; ' tsType': T; ' notNull'?: boolean; ' notNullExceptInsert'?: boolean; }; type NormalizeColumn<T> = T extends ORMColumnType ? { ' type': T; ' notNull'?: boolean; ' notNullExceptInsert'?: boolean } : T extends { ' type': ORMColumnType } ? { ' notNull'?: boolean; ' notNullExceptInsert'?: boolean } & T : T extends { ' type': 'json'; ' tsType': any } ? { ' notNull'?: boolean; ' notNullExceptInsert'?: boolean } & T : never; type IsRequired<CT> = CT extends { ' notNull': true } ? true : false; type IsRequiredInsert<CT> = NormalizeColumn<CT>[' notNullExceptInsert'] extends true ? false // If notNullExceptInsert is true, then it's NOT required for insert : NormalizeColumn<CT>[' notNull'] extends true ? true // If notNull is true (and notNullExceptInsert is not true), then it IS required for insert : false; // Otherwise, it's optional type ColumnTypeToTS<CT> = NormalizeColumn<CT>[' type'] extends 'numeric' ? number : NormalizeColumn<CT>[' type'] extends 'boolean' ? boolean : NormalizeColumn<CT>[' type'] extends 'json' ? CT extends { ' type': 'json'; ' tsType': infer T } ? T : any : NormalizeColumn<CT>[' type'] extends 'date' ? (string | Date) : string; export type RelationType = 'hasMany' | 'hasOne' | 'references'; export type RelationDefinition<Tables extends Record<string, any>> = { type: RelationType; target: keyof Tables; }; export type TableDefinition<Tables extends Record<string, any>> = { columns: Record<string, ORMColumnDefinition | ORMJsonColumnDefinition>; primaryKey: readonly (keyof any)[]; relations?: Record<string, RelationDefinition<Tables>>; }; export interface RawFilter { sql: string | (() => string); parameters?: any[]; } export interface Filter extends RawFilter { and(other: RawFilter | RawFilter[], ...filters: RawFilter[]): Filter; or(other: RawFilter | RawFilter[], ...filters: RawFilter[]): Filter; not(): Filter; } type StringOnlyMethods = { startsWith(value: string | null | undefined): Filter; iStartsWith(value: string | null | undefined): Filter; endsWith(value: string | null | undefined): Filter; iEndsWith(value: string | null | undefined): Filter; contains(value: string | null | undefined): Filter; iContains(value: string | null | undefined): Filter; iEqual(value: string | null | undefined): Filter; ieq(value: string | null | undefined): Filter; }; export type ColumnFilterType<Val, ColumnType = any> = { equal(value: Val | null | undefined): Filter; eq(value: Val | null | undefined): Filter; notEqual(value: Val | null | undefined): Filter; ne(value: Val | null | undefined): Filter; lessThan(value: Val | null | undefined): Filter; lt(value: Val | null | undefined): Filter; lessThanOrEqual(value: Val | null | undefined): Filter; le(value: Val | null | undefined): Filter; greaterThan(value: Val | null | undefined): Filter; gt(value: Val | null | undefined): Filter; greaterThanOrEqual(value: Val | null | undefined): Filter; ge(value: Val | null | undefined): Filter; in(values: (Val | null | undefined)[]): Filter; between(from: Val | null | undefined, to: Val | null | undefined): Filter; notIn(values: (Val | null | undefined)[]): Filter; } & (ColumnType extends 'string' ? StringOnlyMethods : {}); export type JsonArray = Array<JsonValue>; export type JsonObject = { [key: string]: JsonValue }; export type JsonValue = string | number | boolean | null | JsonArray | JsonObject; export type ColumnRefs<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { [C in keyof M[K]['columns']]: ColumnFilterType< ColumnTypeToTS<M[K]['columns'][C]>, NormalizeColumn<M[K]['columns'][C]>[' type'] >; }; export type RootTableRefs<M extends Record<string, TableDefinition<M>>, Target extends keyof M> = ColumnRefs<M, Target> & RelationRefs<M, Target>; export type RelationTableRefs<M extends Record<string, TableDefinition<M>>, Target extends keyof M> = ColumnRefs<M, Target> & RelationRefs<M, Target> & { exists(): Filter; }; export type HasManyRelationFilter<M extends Record<string, TableDefinition<M>>, Target extends keyof M> = { any(predicate: (row: RelationTableRefs<M, Target>) => Filter): Filter; all(predicate: (row: RelationTableRefs<M, Target>) => Filter): Filter; none(predicate: (row: RelationTableRefs<M, Target>) => Filter): Filter; exists(): Filter; }; export type FilterableSingleRelation<M extends Record<string, TableDefinition<M>>, Target extends keyof M> = RelationTableRefs<M, Target> & { (predicate: (row: RelationTableRefs<M, Target>) => Filter): Filter; }; export type RelationRefs<M extends Record<string, TableDefinition<M>>, K extends keyof M> = M[K] extends { relations: infer R } ? { [RName in keyof R]: R[RName] extends RelationDefinition<M> ? R[RName]['type'] extends 'hasMany' ? HasManyRelationFilter<M, R[RName]['target']> & RelationTableRefs<M, R[RName]['target']> : R[RName]['type'] extends 'hasOne' | 'references' ? FilterableSingleRelation<M, R[RName]['target']> : never : never; } : {}; export type OrderBy<M extends Record<string, TableDefinition<M>>, K extends keyof M> = | `${Extract<keyof M[K]['columns'], string>}` | `${Extract<keyof M[K]['columns'], string>} asc` | `${Extract<keyof M[K]['columns'], string>} desc` | Array< | `${Extract<keyof M[K]['columns'], string>}` | `${Extract<keyof M[K]['columns'], string>} asc` | `${Extract<keyof M[K]['columns'], string>} desc` >; // Reserved property names that should not conflict with relation selectors type ReservedFetchStrategyProps = 'orderBy' | 'where'; // Base fetch strategy properties (reserved props) type BaseFetchStrategy<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { orderBy?: OrderBy<M, K>; limit?: number; offset?: number; where?: WhereFunc<M, K>; }; export type PrimaryKeyObject<M extends Record<string, TableDefinition<M>>, K extends keyof M> = M[K]['primaryKey'] extends readonly (infer Keys extends keyof M[K]['columns'])[] ? { [PK in Keys]: ColumnTypeToTS<M[K]['columns'][PK]> } : never; // Column selection properties type ColumnSelection<M extends Record<string, TableDefinition<M>>, K extends keyof M> = Partial<Record<keyof M[K]['columns'], boolean>>; // Relation selection properties (excluding reserved names) type RelationSelection<M extends Record<string, TableDefinition<M>>, K extends keyof M> = M[K] extends { relations: infer R } ? { [RName in keyof R as RName extends ReservedFetchStrategyProps ? never : RName]?: R[RName] extends { target: infer T } ? T extends keyof M ? true | false | FetchStrategy<M, T> : never : never; } : {}; // Helper type to extract only column filter types (not relation objects) type AllColumnFilterTypes<M extends Record<string, TableDefinition<M>>, K extends keyof M> = ColumnRefs<M, K>[keyof ColumnRefs<M, K>]; // Helper type to get column filter types from related tables type RelatedColumnFilterTypes<M extends Record<string, TableDefinition<M>>, K extends keyof M> = M[K] extends { relations: infer R } ? { [RName in keyof R]: R[RName] extends { target: infer Target extends keyof M } ? ColumnRefs<M, Target>[keyof ColumnRefs<M, Target>] : never; }[keyof R] : never; // All valid column filter types (direct columns + related table columns) type ValidColumnFilterTypes<M extends Record<string, TableDefinition<M>>, K extends keyof M> = AllColumnFilterTypes<M, K> | RelatedColumnFilterTypes<M, K>; // Column selection refs without filter methods - only for getting values/references type ColumnSelectionRefs<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { [C in keyof M[K]['columns']]: M[K]['columns'][C]; }; // type ColumnSelectionRefs<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { // // Required columns (notNull = true) // [C in keyof M[K]['columns'] as IsRequired<M[K]['columns'][C]> extends true ? C : never]: ColumnTypeToTS<M[K]['columns'][C]>; // } & { // // Optional columns (nullable) // [C in keyof M[K]['columns'] as IsRequired<M[K]['columns'][C]> extends true ? never : C]?: ColumnTypeToTS<M[K]['columns'][C]> | null | undefined; // }; // Relation selection refs without filter methods - supports deep nesting // In selectors, all relation types just provide access to the target table structure // But WITHOUT aggregate functions (only available at root level) type RelationSelectionRefs<M extends Record<string, TableDefinition<M>>, K extends keyof M> = M[K] extends { relations: infer R } ? { [RName in keyof R]: R[RName] extends RelationDefinition<M> ? R[RName]['type'] extends 'hasMany' | 'hasOne' | 'references' ? SelectionRefsWithoutAggregates<M, R[RName]['target']> // Use version without aggregates : never : never; } : {}; // Selection refs without aggregate functions (for use inside aggregate function selectors) type SelectionRefsWithoutAggregates<M extends Record<string, TableDefinition<M>>, Target extends keyof M> = ColumnSelectionRefs<M, Target> & RelationSelectionRefs<M, Target>; // Base aggregate function type type BaseAggregateFunction = { __aggregateFunction: true; __functionType?: 'count' | 'sum' | 'avg' | 'max' | 'min'; }; // Standard aggregate function for count, sum, avg type AggregateFunction = BaseAggregateFunction & { returnType: 'numeric'; __functionType?: 'count' | 'sum' | 'avg'; }; // Special type for max/min that preserves the column type type AggregateMinMaxFunction<T> = BaseAggregateFunction & { returnType: T; __functionType: 'max' | 'min'; }; // Update AggregateFunctions to include the function type and proper return types type AggregateFunctions<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { count(selector: (row: SelectionRefsWithoutAggregates<M, K>) => AnyColumnDefinition<M, K>): AggregateFunction & { __functionType: 'count' }; avg(selector: (row: SelectionRefsWithoutAggregates<M, K>) => NumericColumnDefinition<M, K>): AggregateFunction & { __functionType: 'avg' }; sum(selector: (row: SelectionRefsWithoutAggregates<M, K>) => NumericColumnDefinition<M, K>): AggregateFunction & { __functionType: 'sum' }; max<T extends AnyColumnDefinition<M, K>>(selector: (row: SelectionRefsWithoutAggregates<M, K>) => T): AggregateMinMaxFunction<T> & { __functionType: 'max' }; min<T extends AnyColumnDefinition<M, K>>(selector: (row: SelectionRefsWithoutAggregates<M, K>) => T): AggregateMinMaxFunction<T> & { __functionType: 'min' }; }; // NEW ── any column, any table (local or related) type AnyColumnDefinition< M extends Record<string, TableDefinition<M>>, K extends keyof M > = // columns on the current table M[K]['columns'][keyof M[K]['columns']] | // columns on related tables (M[K] extends { relations: infer R } ? { [RName in keyof R]: R[RName] extends { target: infer Target extends keyof M } ? M[Target]['columns'][keyof M[Target]['columns']] : never; }[keyof R] : never); // CHANGE from NumericColumnValue to NumericColumnDefinition: type NumericColumnDefinition<M extends Record<string, TableDefinition<M>>, K extends keyof M> = // Direct numeric columns { [C in keyof M[K]['columns']]: NormalizeColumn<M[K]['columns'][C]>[' type'] extends 'numeric' ? M[K]['columns'][C] // Return column definition, not TS type : never; }[keyof M[K]['columns']] | // Numeric columns from related tables (M[K] extends { relations: infer R } ? { [RName in keyof R]: R[RName] extends { target: infer Target extends keyof M } ? { [C in keyof M[Target]['columns']]: NormalizeColumn<M[Target]['columns'][C]>[' type'] extends 'numeric' ? M[Target]['columns'][C] // Return column definition, not TS type : never; }[keyof M[Target]['columns']] : never; }[keyof R] : never); // Root selection refs for custom selectors (no filter methods) - supports deep nesting + aggregates type RootSelectionRefs<M extends Record<string, TableDefinition<M>>, Target extends keyof M> = ColumnSelectionRefs<M, Target> & RelationSelectionRefs<M, Target> & AggregateFunctions<M, Target>; // Valid return types for custom selectors - now supports deep paths through any relation type type ValidSelectorReturnTypes<M extends Record<string, TableDefinition<M>>, K extends keyof M> = // Column definitions from any table (M extends Record<string, TableDefinition<M>> ? { [TableKey in keyof M]: M[TableKey]['columns'][keyof M[TableKey]['columns']] }[keyof M] : never) | // Base aggregate function marker (covers both regular and min/max) BaseAggregateFunction; // ADD helper to convert column definition to TypeScript type: type ColumnDefinitionToTS<CD> = CD extends ORMColumnDefinition | ORMJsonColumnDefinition ? ColumnTypeToTS<CD> : never; // Custom selector functions - allows arbitrary property names with selector functions // Uses RootSelectionRefs which supports deep nesting without filter methods type CustomSelectors<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { [key: string]: (row: RootSelectionRefs<M, K>) => ValidSelectorReturnTypes<M, K>; }; // Main FetchStrategy type using union to avoid conflicts export type FetchStrategy<M extends Record<string, TableDefinition<M>>, K extends keyof M> = BaseFetchStrategy<M, K> & (ColumnSelection<M, K> | RelationSelection<M, K> | CustomSelectors<M, K> | (ColumnSelection<M, K> & RelationSelection<M, K>) | (ColumnSelection<M, K> & CustomSelectors<M, K>) | (RelationSelection<M, K> & CustomSelectors<M, K>) | (ColumnSelection<M, K> & RelationSelection<M, K> & CustomSelectors<M, K>)); export type AggregateStrategy<M extends Record<string, TableDefinition<M>>, K extends keyof M> = BaseAggregateStrategy<M, K> | CustomAggregateSelectors<M, K>; type WhereFunc<M extends Record<string, TableDefinition<M>>, K extends keyof M> = (row: RootTableRefs<M, K>) => RawFilter | Array<PrimaryKeyObject<M, K>>; type BaseAggregateStrategy<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { limit?: number; offset?: number; where?: WhereFunc<M, K>; }; type CustomAggregateSelectors<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { [key: string]: (row: RootSelectionRefs<M, K>) => ValidSelectorReturnTypes<M, K>; } & { where?: WhereFunc<M, K>; } & { // Explicitly prevent limit/offset in selectors limit?: never; offset?: never; }; type TrueKeys<M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any>> = { [C in keyof M[K]['columns']]: FS extends Record<C, infer B> ? (B extends true ? C : never) : never; }[keyof M[K]['columns']]; type FalseKeys<M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any>> = { [C in keyof M[K]['columns']]: FS extends Record<C, infer B> ? (B extends false ? C : never) : never; }[keyof M[K]['columns']]; type SelectedColumns<M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any>> = TrueKeys<M, K, FS> extends never ? (FalseKeys<M, K, FS> extends never ? keyof M[K]['columns'] : Exclude<keyof M[K]['columns'], FalseKeys<M, K, FS>>) : TrueKeys<M, K, FS>; type RequiredColumnKeys<M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any>> = { [C in SelectedColumns<M, K, FS>]: IsRequired<M[K]['columns'][C]> extends true ? C : never; }[SelectedColumns<M, K, FS>]; type OptionalColumnKeys<M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any>> = Exclude<SelectedColumns<M, K, FS>, RequiredColumnKeys<M, K, FS>>; type IsActualColumnReference<T, M extends Record<string, TableDefinition<M>>, K extends keyof M> = T extends ColumnTypeToTS<infer CT> ? CT extends ORMColumnDefinition | ORMJsonColumnDefinition ? true : false : false; type IsFromColumnSelectionRefs<M extends Record<string, TableDefinition<M>>, K extends keyof M, T = any> = T extends ColumnSelectionRefs<M, K>[keyof ColumnSelectionRefs<M, K>] ? true : T extends (M[K] extends { relations: infer R } ? { [RName in keyof R]: R[RName] extends { target: infer Target extends keyof M } ? ColumnSelectionRefs<M, Target>[keyof ColumnSelectionRefs<M, Target>] : never; }[keyof R] : never) ? true : T extends number ? true : // Allow aggregate function return type (number) false; type InferSelectorReturnType<T, M extends Record<string, TableDefinition<M>>, K extends keyof M> = IsFromColumnSelectionRefs<M, K, T> extends true ? T : never; type IsAggregateFunction<T> = T extends BaseAggregateFunction ? true : false; type IsRequiredAggregate<T> = T extends BaseAggregateFunction & { __functionType: infer FType } ? FType extends 'count' | 'sum' | 'avg' ? true : false : false; type IsMinMaxAggregate<T> = T extends AggregateMinMaxFunction<any> & { __functionType: infer FType } ? FType extends 'max' | 'min' ? true : false : false; type ExtractMinMaxColumnType<T> = T extends AggregateMinMaxFunction<infer ColType> ? ColumnDefinitionToTS<ColType> : never; // Updated CustomSelectorProperties with conditional nullability type CustomSelectorProperties<M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any>> = { // Required properties (count, sum, avg) - no question mark, no null/undefined [P in keyof FS as P extends keyof M[K]['columns'] ? never : P extends ReservedFetchStrategyProps ? never : P extends (M[K] extends { relations: infer R } ? keyof R : never) ? never : FS[P] extends (row: any) => any ? (FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? IsRequiredAggregate<ReturnType> extends true ? P // Required aggregates : never : never) : never ]: FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? number // Required aggregate functions (count, sum, avg) return required number : never; } & { // Optional properties (max, min, plain columns) - with question mark AND null/undefined [P in keyof FS as P extends keyof M[K]['columns'] ? never : P extends ReservedFetchStrategyProps ? never : P extends (M[K] extends { relations: infer R } ? keyof R : never) ? never : FS[P] extends (row: any) => any ? (FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? IsRequiredAggregate<ReturnType> extends true ? never // Required aggregates are not optional : P // Everything else is optional : never) : never ]?: FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? IsMinMaxAggregate<ReturnType> extends true ? ExtractMinMaxColumnType<ReturnType> | null | undefined : IsAggregateFunction<ReturnType> extends true ? number | null | undefined : ReturnType extends ORMColumnDefinition | ORMJsonColumnDefinition ? ColumnDefinitionToTS<ReturnType> | null | undefined : never : never; }; export type Selection< M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any> > = { [C in RequiredColumnKeys<M, K, FS>]: ColumnTypeToTS<M[K]['columns'][C]>; } & { [C in OptionalColumnKeys<M, K, FS>]?: ColumnTypeToTS<M[K]['columns'][C]> | null | undefined; } & ( M[K] extends { relations: infer R } ? { [RName in keyof R & keyof FS as R[RName] extends { type: 'hasMany' } ? RName : never ]: R[RName] extends { target: infer Target extends keyof M } ? FS[RName] extends true ? Array<DeepExpand<Selection<M, Target, {}>>> : FS[RName] extends Record<string, any> ? Array<DeepExpand<Selection<M, Target, FS[RName]>>> : never : never; } & { [RName in keyof R & keyof FS as R[RName] extends { type: 'hasOne' | 'references' } ? RName : never ]?: R[RName] extends { target: infer Target extends keyof M } ? FS[RName] extends true ? DeepExpand<Selection<M, Target, {}>> | null : FS[RName] extends Record<string, any> ? DeepExpand<Selection<M, Target, FS[RName]>> | null : never : never; } : {} ) & CustomSelectorProperties<M, K, FS>; export type PrimaryKeyArgs<M extends Record<string, TableDefinition<M>>, K extends keyof M> = M[K]['primaryKey'] extends readonly [infer A extends keyof M[K]['columns']] ? [key1: ColumnTypeToTS<M[K]['columns'][A]>] : M[K]['primaryKey'] extends readonly [infer A extends keyof M[K]['columns'], infer B extends keyof M[K]['columns']] ? [key1: ColumnTypeToTS<M[K]['columns'][A]>, key2: ColumnTypeToTS<M[K]['columns'][B]>] : M[K]['primaryKey'] extends readonly [infer A extends keyof M[K]['columns'], infer B extends keyof M[K]['columns'], infer C extends keyof M[K]['columns']] ? [key1: ColumnTypeToTS<M[K]['columns'][A]>, key2: ColumnTypeToTS<M[K]['columns'][B]>, key3: ColumnTypeToTS<M[K]['columns'][C]>] : M[K]['primaryKey'] extends readonly [infer A extends keyof M[K]['columns'], infer B extends keyof M[K]['columns'], infer C extends keyof M[K]['columns'], infer D extends keyof M[K]['columns']] ? [key1: ColumnTypeToTS<M[K]['columns'][A]>, key2: ColumnTypeToTS<M[K]['columns'][B]>, key3: ColumnTypeToTS<M[K]['columns'][C]>, key4: ColumnTypeToTS<M[K]['columns'][D]>] : M[K]['primaryKey'] extends readonly [infer A extends keyof M[K]['columns'], infer B extends keyof M[K]['columns'], infer C extends keyof M[K]['columns'], infer D extends keyof M[K]['columns'], infer E extends keyof M[K]['columns']] ? [key1: ColumnTypeToTS<M[K]['columns'][A]>, key2: ColumnTypeToTS<M[K]['columns'][B]>, key3: ColumnTypeToTS<M[K]['columns'][C]>, key4: ColumnTypeToTS<M[K]['columns'][D]>, key5: ColumnTypeToTS<M[K]['columns'][E]>] : M[K]['primaryKey'] extends readonly [infer A extends keyof M[K]['columns'], infer B extends keyof M[K]['columns'], infer C extends keyof M[K]['columns'], infer D extends keyof M[K]['columns'], infer E extends keyof M[K]['columns'], infer F extends keyof M[K]['columns']] ? [key1: ColumnTypeToTS<M[K]['columns'][A]>, key2: ColumnTypeToTS<M[K]['columns'][B]>, key3: ColumnTypeToTS<M[K]['columns'][C]>, key4: ColumnTypeToTS<M[K]['columns'][D]>, key5: ColumnTypeToTS<M[K]['columns'][E]>, key6: ColumnTypeToTS<M[K]['columns'][F]>] : never; // Helper type to get primary key columns that are required (notNull) type RequiredPrimaryKeyColumns<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { [PK in M[K]['primaryKey'][number]]: PK extends keyof M[K]['columns'] ? IsRequired<M[K]['columns'][PK]> extends true ? PK : never : never; }[M[K]['primaryKey'][number]]; // Helper type to get primary key columns that are optional (nullable) type OptionalPrimaryKeyColumns<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { [PK in M[K]['primaryKey'][number]]: PK extends keyof M[K]['columns'] ? IsRequired<M[K]['columns'][PK]> extends true ? never : PK : never; }[M[K]['primaryKey'][number]]; // Type for insert operations - requires notNull columns but allows notNullExceptInsert to be optional type InsertRow<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { // Required columns (notNull but not notNullExceptInsert) [C in keyof M[K]['columns'] as IsRequiredInsert<M[K]['columns'][C]> extends true ? C : never]: ColumnTypeToTS<M[K]['columns'][C]>; } & { // Optional columns (nullable, or notNullExceptInsert) [C in keyof M[K]['columns'] as IsRequiredInsert<M[K]['columns'][C]> extends true ? never : C]?: ColumnTypeToTS<M[K]['columns'][C]> | null | undefined; }; // Type for updateChanges and replace operations type UpdateChangesRow<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { // Required primary key columns [C in RequiredPrimaryKeyColumns<M, K>]: ColumnTypeToTS<M[K]['columns'][C]>; } & { // Optional primary key columns [C in OptionalPrimaryKeyColumns<M, K>]?: ColumnTypeToTS<M[K]['columns'][C]> | null | undefined; } & { // All other columns are optional [C in Exclude<keyof M[K]['columns'], M[K]['primaryKey'][number]>]?: IsRequired<M[K]['columns'][C]> extends true ? ColumnTypeToTS<M[K]['columns'][C]> : ColumnTypeToTS<M[K]['columns'][C]> | null | undefined; }; // NEW: Extended row types with relations support // Type for relation rows - only respects notNull, ignores notNullExceptInsert type RelationRow<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { // Required columns (notNull = true, ignoring notNullExceptInsert) [C in keyof M[K]['columns'] as IsRequired<M[K]['columns'][C]> extends true ? C : never]: ColumnTypeToTS<M[K]['columns'][C]>; } & { // Optional columns (all others) [C in keyof M[K]['columns'] as IsRequired<M[K]['columns'][C]> extends true ? never : C]?: ColumnTypeToTS<M[K]['columns'][C]> | null | undefined; }; // Helper type to create relation data for insert/update operations type RelationData<M extends Record<string, TableDefinition<M>>, K extends keyof M> = M[K] extends { relations: infer R } ? { [RName in keyof R]?: R[RName] extends RelationDefinition<M> ? R[RName]['type'] extends 'hasMany' ? R[RName]['target'] extends keyof M ? Array<RelationRowWithRelations<M, R[RName]['target']>> : never : R[RName]['type'] extends 'hasOne' | 'references' ? R[RName]['target'] extends keyof M ? RelationRowWithRelations<M, R[RName]['target']> | null : never : never : never; } : {}; // Relation row type with nested relations support type RelationRowWithRelations<M extends Record<string, TableDefinition<M>>, K extends keyof M> = RelationRow<M, K> & RelationData<M, K>; // Extended insert row type with optional relations type InsertRowWithRelations<M extends Record<string, TableDefinition<M>>, K extends keyof M> = InsertRow<M, K> & RelationData<M, K>; // Extended update/replace row type with optional relations type UpdateChangesRowWithRelations<M extends Record<string, TableDefinition<M>>, K extends keyof M> = UpdateChangesRow<M, K> & RelationData<M, K>; // Extended type for bulk update operations with optional relations type UpdateRowWithRelations<M extends Record<string, TableDefinition<M>>, K extends keyof M> = Partial<{ [C in keyof M[K]['columns']]: IsRequired<M[K]['columns'][C]> extends true ? ColumnTypeToTS<M[K]['columns'][C]> : ColumnTypeToTS<M[K]['columns'][C]> | null | undefined; }> & RelationData<M, K>; // REFACTORED: Separate Active Record Methods for Individual Rows vs Arrays // Active record methods for individual rows type ActiveRecordMethods<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { saveChanges(): Promise<void>; saveChanges(concurrency: ConcurrencyConfig<M>[K]): Promise<void>; acceptChanges(): void; clearChanges(): void; refresh(): Promise<void>; refresh<strategy extends FetchStrategy<M, K>>(strategy: strategy): Promise<DeepExpand<Selection<M, K, strategy>> & ActiveRecordMethods<M, K>>; delete(): Promise<void>; delete<strategy extends FetchStrategy<M, K>>(strategy: strategy): Promise<void>; }; // Active record methods for arrays of rows type ArrayActiveRecordMethods<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { saveChanges(): Promise<void>; saveChanges(concurrency: ConcurrencyConfig<M>[K]): Promise<void>; acceptChanges(): void; clearChanges(): void; refresh(): Promise<void>; refresh<strategy extends FetchStrategy<M, K>>(strategy: strategy): Promise<Array<DeepExpand<Selection<M, K, strategy>>>>; delete(): Promise<void>; delete<strategy extends FetchStrategy<M, K>>(strategy: strategy): Promise<void>; }; // Helper type to add individual active record methods to selection results type WithActiveRecord<T, M extends Record<string, TableDefinition<M>>, K extends keyof M> = T & ActiveRecordMethods<M, K>; // Helper type to add array active record methods to arrays without adding them to individual items type WithArrayActiveRecord<T extends Array<any>, M extends Record<string, TableDefinition<M>>, K extends keyof M> = T & ArrayActiveRecordMethods<M, K>; // Separate CustomSelectorProperties for aggregate (allows column name overlap) type AggregateCustomSelectorProperties<M extends Record<string, TableDefinition<M>>, K extends keyof M, FS extends Record<string, any>> = { // Required properties (count, sum, avg) - no question mark, no null/undefined [P in keyof FS as P extends ReservedFetchStrategyProps ? never : P extends (M[K] extends { relations: infer R } ? keyof R : never) ? never : FS[P] extends (row: any) => any ? (FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? IsRequiredAggregate<ReturnType> extends true ? P // Required aggregates : never : never) : never ]: FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? number // Required aggregate functions (count, sum, avg) return required number : never; } & { // Optional properties (max, min, plain columns) - with question mark AND null/undefined [P in keyof FS as P extends ReservedFetchStrategyProps ? never : P extends (M[K] extends { relations: infer R } ? keyof R : never) ? never : FS[P] extends (row: any) => any ? (FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? IsRequiredAggregate<ReturnType> extends true ? never // Required aggregates are not optional : P // Everything else is optional : never) : never ]?: FS[P] extends (row: RootSelectionRefs<M, K>) => infer ReturnType ? IsMinMaxAggregate<ReturnType> extends true ? ExtractMinMaxColumnType<ReturnType> | null | undefined : IsAggregateFunction<ReturnType> extends true ? number | null | undefined : ReturnType extends ORMColumnDefinition | ORMJsonColumnDefinition ? ColumnDefinitionToTS<ReturnType> | null | undefined : never : never; }; export type TableClient<M extends Record<string, TableDefinition<M>>, K extends keyof M> = { // Array methods - return arrays with array-level active record methods, but individual items are plain getAll(): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, {}>>>, M, K>>; getAll<strategy extends FetchStrategy<M, K>>(strategy: strategy): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>; getMany(filter?: RawFilter | Array<PrimaryKeyObject<M, K>>): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, {}>>>, M, K>>; getMany<strategy extends FetchStrategy<M, K>>(filter?: RawFilter | Array<PrimaryKeyObject<M, K>>, strategy?: strategy): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>; // Aggregate methods - return plain objects (no active record methods) aggregate<strategy extends AggregateStrategy<M, K>>(strategy: strategy): Promise<Array<DeepExpand<AggregateCustomSelectorProperties<M, K, strategy>>>>; // Single item methods - return individual objects with individual active record methods getOne<strategy extends FetchStrategy<M, K>>( filter?: RawFilter | Array<PrimaryKeyObject<M, K>> ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K>>; getOne<strategy extends FetchStrategy<M, K>>( filter?: RawFilter | Array<PrimaryKeyObject<M, K>>, strategy?: strategy ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K>>; getById<strategy extends FetchStrategy<M, K>>( ...args: [...PrimaryKeyArgs<M, K>, strategy: strategy] ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K>>; getById( ...args: [...PrimaryKeyArgs<M, K>] ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, {}>>, M, K>>; // UPDATED: Bulk update methods with relations support update( row: UpdateRowWithRelations<M, K>, opts: { where: (row: RootTableRefs<M, K>) => RawFilter } ): Promise<void>; update<strategy extends FetchStrategy<M, K>>( row: UpdateRowWithRelations<M, K>, opts: { where: (row: RootTableRefs<M, K>) => RawFilter }, strategy: strategy ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>; // Count and delete methods (no active record methods needed) count(filter: RawFilter | Array<PrimaryKeyObject<M, K>>,): Promise<number>; delete(filter: RawFilter | Array<PrimaryKeyObject<M, K>>,): Promise<void>; deleteCascade(filter: RawFilter | Array<PrimaryKeyObject<M, K>>,): Promise<void>; // UPDATED: Replace methods with relations support replace( row: Array<UpdateChangesRowWithRelations<M, K>> ): Promise<void>; replace<strategy extends FetchStrategy<M, K>>( row: UpdateChangesRowWithRelations<M, K>, strategy: strategy ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K>>; replace<strategy extends FetchStrategy<M, K>>( rows: Array<UpdateChangesRowWithRelations<M, K>>, strategy: strategy ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>; // UPDATED: UpdateChanges methods with relations support updateChanges( row: UpdateChangesRowWithRelations<M, K>, originalRow: UpdateChangesRowWithRelations<M, K> ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, {}>>, M, K>>; updateChanges( rows: Array<UpdateChangesRowWithRelations<M, K>>, originalRows: Array<UpdateChangesRowWithRelations<M, K>> ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, {}>>>, M, K>>; updateChanges<strategy extends FetchStrategy<M, K>>( row: UpdateChangesRowWithRelations<M, K>, originalRow: UpdateChangesRowWithRelations<M, K>, strategy: strategy ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K>>; updateChanges<strategy extends FetchStrategy<M, K>>( rows: Array<UpdateChangesRowWithRelations<M, K>>, originalRows: Array<UpdateChangesRowWithRelations<M, K>>, strategy: strategy ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>; // UPDATED: Insert methods with relations support - no active record methods for insertAndForget, appropriate methods for others insertAndForget( row: InsertRowWithRelations<M, K> ): Promise<void>; insertAndForget( rows: Array<InsertRowWithRelations<M, K>> ): Promise<void>; insert( row: InsertRowWithRelations<M, K> ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, {}>>, M, K>>; insert( rows: Array<InsertRowWithRelations<M, K>> ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, {}>>>, M, K>>; insert<strategy extends FetchStrategy<M, K>>( row: InsertRowWithRelations<M, K>, strategy: strategy ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K>>; insert<strategy extends FetchStrategy<M, K>>( rows: Array<InsertRowWithRelations<M, K>>, strategy: strategy ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>; // UPDATED: Proxify methods with relations support proxify( row: UpdateChangesRowWithRelations<M, K> ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, {}>>, M, K>>; proxify( rows: Array<UpdateChangesRowWithRelations<M, K>> ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, {}>>>, M, K>>; proxify<strategy extends FetchStrategy<M, K>>( row: UpdateChangesRowWithRelations<M, K>, strategy: strategy ): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K>>; proxify<strategy extends FetchStrategy<M, K>>( rows: Array<UpdateChangesRowWithRelations<M, K>>, strategy: strategy ): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>; // Patch method patch<strategy extends FetchStrategy<M, K>>( patches: JsonPatch, strategy: strategy, concurrency?: ConcurrencyConfig<M>[K] ): Promise<void>; // TypeScript type helpers tsType(): DeepExpand<Selection<M, K, {}>>; tsType<strategy extends FetchStrategy<M, K>>( strategy: strategy ): DeepExpand<Selection<M, K, strategy>>; }; // Rest of the type definitions remain the same... export type ConcurrencyStrategy = 'optimistic' | 'overwrite' | 'skipOnConflict'; export interface ColumnConcurrency { readonly?: boolean; concurrency?: ConcurrencyStrategy; } export type ConcurrencyConfig<M extends Record<string, TableDefinition<M>>> = { [K in keyof M]?: ColumnConcurrency & { [C in keyof M[K]['columns']]?: { concurrency: ConcurrencyStrategy; }; } & ( M[K] extends { relations: infer R } ? { [RName in keyof R]?: ConcurrencyConfig<M>[R[RName] extends { target: infer T extends keyof M } ? T : never]; } : {} ); }; export type DbOptions<M extends Record<string, TableDefinition<M>>> = ConcurrencyConfig<M> & ColumnConcurrency & { db?: Pool | ((connectors: Connectors) => Pool | Promise<Pool>); }; export type DbConcurrency<M extends Record<string, TableDefinition<M>>> = ConcurrencyConfig<M> & ColumnConcurrency; type JsonPatch = Array<{ op: 'add' | 'remove' | 'replace' | 'copy' | 'move' | 'test'; path: string; value?: any; from?: string; }>; interface WithInterceptors { request: AxiosInterceptorManager<InternalAxiosRequestConfig>; response: AxiosInterceptorManager<AxiosResponse>; } interface Connectors { http(url: string): Pool; d1(database: D1Database): Pool; postgres(connectionString: string, options?: PoolOptions): Pool; pglite(config?: PGliteOptions | string | undefined, options?: PoolOptions): Pool; sqlite(connectionString: string, options?: PoolOptions): Pool; sap(connectionString: string, options?: PoolOptions): Pool; mssql(connectionConfig: ConnectionConfiguration, options?: PoolOptions): Pool; mssql(connectionString: string, options?: PoolOptions): Pool; oracle(config: PoolAttributes, options?: PoolOptions): Pool; } type DbConnectable<M extends Record<string, TableDefinition<M>>> = { http(url: string): DBClient<M>; d1(database: D1Database): DBClient<M>; postgres(connectionString: string, options?: PoolOptions): DBClient<M>; pglite(config?: PGliteOptions | string | undefined, options?: PoolOptions): DBClient<M>; sqlite(connectionString: string, options?: PoolOptions): DBClient<M>; sap(connectionString: string, options?: PoolOptions): DBClient<M>; mssql(connectionConfig: ConnectionConfiguration, options?: PoolOptions): DBClient<M>; mssql(connectionString: string, options?: PoolOptions): DBClient<M>; mssqlNative(connectionString: string, options?: PoolOptions): DBClient<M>; mysql(connectionString: string, options?: PoolOptions): DBClient<M>; oracle(config: PoolAttributes, options?: PoolOptions): DBClient<M>; }; export interface Pool { end(): Promise<void>; } export interface PoolOptions { size?: number; } export type DBClient<M extends Record<string, TableDefinition<M>>> = { [TableName in keyof M]: RootTableRefs<M, TableName> & TableClient<M, TableName>; } & { close(): Promise<void>; filter: Filter; and(f: Filter | RawFilter[], ...filters: RawFilter[]): Filter; or(f: Filter | RawFilter[], ...filters: RawFilter[]): Filter; not(): Filter; query(filter: RawFilter | string): Promise<unknown[]>; query<T>(filter: RawFilter | string): Promise<T[]>; createPatch(original: any[], modified: any[]): JsonPatch; createPatch(original: any, modified: any): JsonPatch; ( config?: DbOptions<M> ): DBClient<M>; transaction( fn: (db: DBClient<M>) => Promise<unknown> ): Promise<void>; express(): import('express').RequestHandler; express(config: ExpressConfig<M>): import('express').RequestHandler; readonly metaData: DbConcurrency<M>; interceptors: WithInterceptors; } & WithInterceptors & DbConnectable<M>; type ExpressConfig<M extends Record<string, TableDefinition<M>>> = { [TableName in keyof M]?: ExpressTableConfig<M>; } & { db?: Pool | ((connectors: Connectors) => Pool | Promise<Pool>); } type ExpressTableConfig<M extends Record<string, TableDefinition<M>>> = { baseFilter?: RawFilter | ((db: DBClient<M>, req: import('express').Request, res: import('express').Response) => RawFilter); } export type DeepExpand<T> = T extends Date ? T : T extends Array<infer U> ? Array<DeepExpand<U>> : T extends object ? { [K in keyof T]: DeepExpand<T[K]> } : T; export function db<M extends Record<string, TableDefinition<M>>>(): DBClient<M>;