import { Primitive } from "./utils.js"; // eslint-disable-next-line @typescript-eslint/no-unused-vars const FILTER_OPERATORS = [ "equals", "notEquals", "greaterThan", "greaterThanOrEqual", "lessThan", "lessThanOrEqual", "contains", "startsWith", "endsWith", "in", "notIn", "between", "isNull", "isNotNull", ] as const; export type FilterOperator = (typeof FILTER_OPERATORS)[number]; export type StringOperators = | "equals" | "notEquals" | "contains" | "startsWith" | "endsWith" | "in" | "notIn" | "isNull" | "isNotNull"; export type NumberOperators = | "equals" | "notEquals" | "greaterThan" | "greaterThanOrEqual" | "lessThan" | "lessThanOrEqual" | "in" | "notIn" | "between" | "isNull" | "isNotNull"; export type DateOperators = | "equals" | "notEquals" | "greaterThan" | "greaterThanOrEqual" | "lessThan" | "lessThanOrEqual" | "in" | "notIn" | "between" | "isNull" | "isNotNull"; export type BooleanOperators = "equals" | "notEquals" | "isNull" | "isNotNull"; export type ArrayOperators = "in" | "notIn" | "isNull" | "isNotNull"; export type OperatorsForType = T extends string ? StringOperators : T extends number ? NumberOperators : T extends Date ? DateOperators : T extends boolean ? BooleanOperators : T extends Array ? ArrayOperators : FilterOperator; export type FilterValueFor = | T | (T extends number | Date ? [T, T] : never) | T[] | null; /** * Resolve individual entity prop types for criteria: * - Date → Date (preserved, so DateOperators are used) * - Array → recurse into elements * - Id / ValueObject (value + equals) → unwrap to primitive value * - Everything else → keep as-is */ type ResolveEntityProp = T extends Date ? Date : T extends Array ? ResolveEntityProp[] : T extends { value: infer V; equals(...args: any[]): any } ? V : T; /** * Extract the plain object type from an entity for criteria field resolution. * - For entities (with `props`), resolves from raw props preserving Date types. * - For objects with toJSON(), uses the return type of toJSON(). * - Otherwise, uses the type as is. */ type ExtractPlainType = T extends { props: infer P } ? { [K in keyof P]: ResolveEntityProp } : T extends { toJSON(): infer R } ? R : T; export type PathValue< T, P extends string, > = P extends `${infer K}.${infer Rest}` ? K extends keyof ExtractPlainType ? ExtractPlainType[K] extends Array ? PathValue : PathValue[K], Rest> : never : P extends keyof ExtractPlainType ? ExtractPlainType[P] : never; export interface Filter { field: TField; operator: unknown extends TValue ? FilterOperator : OperatorsForType; value: TValue; options?: CriteriaOptions; } export type TypedFilter = { [K in FieldPath]: { field: K; operator: OperatorsForType>>; value: FilterValueFor>>; options?: CriteriaOptions; }; }[FieldPath]; export type CriteriaAdapter = { [K in FieldPath]?: FieldPath; }; export type OrderDirection = "asc" | "desc"; export interface Order { field: string; direction: OrderDirection; } export type TypedOrder = { field: FieldPath; direction: OrderDirection; }; export interface Pagination { page: number; limit: number; offset: number; } export type Search = string; export type QueryParamsObject = { filters?: Record; pagination?: Omit; orderBy?: string[]; search?: string; }; export interface PaginationMeta { page: number; limit: number; total: number; totalPages: number; hasNext: boolean; hasPrevious: boolean; } export interface CriteriaOptions { quantifier?: "some" | "every" | "none"; } type ExcludeBuiltInKeys = Exclude; export type FieldPath = ExtractPlainType extends Primitive ? never : { [K in ExcludeBuiltInKeys> & string]: NonNullable< ExtractPlainType[K] > extends Primitive ? K : NonNullable[K]> extends Array ? U extends Primitive ? K : K | `${K}.${FieldPath}` : `${K}.${FieldPath[K]>>}`; }[ExcludeBuiltInKeys> & string];