import { EmptyObject, getClonedQueryData, MergeQuery, Query, QueryReturnType, SetQueryReturns, WhereResult, } from 'pqb'; export type QueryMethods = Record< string, // eslint-disable-next-line @typescript-eslint/no-explicit-any (q: T, ...args: any[]) => any >; type QueryOne = SetQueryReturns< T, Exclude >; export type MethodsBase = { queryMethods?: QueryMethods; queryOneMethods?: QueryMethods>; queryWithWhereMethods?: QueryMethods>; queryOneWithWhereMethods?: QueryMethods>>; methods?: Record; }; export type MapQueryMethods< T extends Query, BaseQuery extends Query, Methods, > = Methods extends QueryMethods ? { [K in keyof Methods]: Methods[K] extends ( // eslint-disable-next-line @typescript-eslint/no-explicit-any q: any, ...args: infer Args ) => // eslint-disable-next-line @typescript-eslint/no-explicit-any infer Result ? ( this: T, ...args: Args ) => Result extends Query ? MergeQuery : Result : never; } : EmptyObject; export type MapMethods< T extends Query, Methods extends MethodsBase, > = MapQueryMethods & MapQueryMethods, QueryOne, Methods['queryOneMethods']> & MapQueryMethods< WhereResult, WhereResult, Methods['queryWithWhereMethods'] > & MapQueryMethods< QueryOne>, QueryOne>, Methods['queryOneWithWhereMethods'] > & (Methods['methods'] extends Record ? Methods['methods'] : EmptyObject); export type Repo< T extends Query, Methods extends MethodsBase, Mapped = MapMethods, > = ((q: Q) => Q & Mapped) & T & Mapped; export const createRepo = >( model: T, methods: Methods, ): Repo => { const queryMethods = { ...methods.queryMethods, ...methods.queryOneMethods, ...methods.queryWithWhereMethods, ...methods.queryOneWithWhereMethods, }; const plainMethods = methods.methods; const repo = (q: Query) => { const proto = Object.create(q.__model); proto.__model = proto; const result = Object.create(proto); result.query = getClonedQueryData(q.query); if (plainMethods) { Object.assign(proto.__model, plainMethods); } for (const key in queryMethods) { const method = queryMethods[key] as (...args: unknown[]) => unknown; (proto.__model as unknown as Record)[key] = function ( ...args: unknown[] ) { return method(this, ...args); }; } return result; }; const q = repo(model); return new Proxy(repo, { get(_, key) { return q[key]; }, }) as unknown as Repo; };