/** * Puri Subset 타입 시스템 * * SubsetQuery와 LoaderQuery를 기반으로 최종 결과 타입을 추론하는 타입 유틸리티들. * 핵심 개념: * - SubsetQuery: 기본 쿼리 (join, select) * - LoaderQuery: 1:N, N:M 관계 데이터 로딩 * - Hydrate: flat한 결과를 중첩 객체로 변환 (예: user__name → { user: { name } }) */ import { type Puri } from "./puri"; import { type PuriWrapper } from "./puri-wrapper"; import { type Expand } from "./puri.types"; /** * SubsetQuery 함수 시그니처 * PuriWrapper를 받아 Puri 쿼리 빌더를 반환 */ export type PuriSubsetFn = (qbWrapper: PuriWrapper) => Puri; /** * Puri 인스턴스에서 TResult 타입 추출 */ export type ExtractPuriResult = T extends Puri ? R : never; /** * Loader 쿼리 빌더 함수 시그니처 * @param qbWrapper - Puri 래퍼 * @param fromIds - 부모 레코드 ID 배열 */ export type PuriLoaderQbFn = (qbWrapper: PuriWrapper, fromIds: number[] | string[]) => Puri; /** * 단일 Loader 정의 * 1:N 또는 N:M 관계 데이터를 로드하는 설정 */ export type GenericPuriLoader = { /** 결과 객체에서 사용할 필드명 */ as: string; /** 부모 레코드와 연결할 참조 필드명 */ refId: string; /** 데이터 로딩 쿼리 빌더 */ qb: PuriLoaderQbFn; /** 중첩 로더 (재귀적 로딩 지원) */ loaders?: GenericPuriLoader[]; }; /** * 모델별 Loader 쿼리 컬렉션 * 각 SubsetKey에 대해 Loader 배열을 정의 */ export type PuriLoaderQueries = Record; /** * 구분자(__) 앞부분 추출 * @example ExtractHead<"user__name"> = "user" */ type ExtractHead = K extends `${infer Head}__${string}` ? Head : never; /** * 구분자(__) 뒷부분 추출 * @example ExtractTail<"user__profile__name", "user"> = "profile__name" */ type ExtractTail = K extends `${Head}__${infer Tail}` ? Tail : never; /** * Flat 객체를 중첩 객체로 변환 * * 런타임 hydrate 함수와 동일한 동작을 타입 레벨에서 구현. * `__`로 구분된 키를 중첩 객체 구조로 변환. * * @example * type Input = { id: number; user__name: string; user__profile__bio: string } * type Output = Hydrate * // { id: number; user: { name: string; profile: { bio: string } } } */ export type Hydrate = Expand>; type HydrateInner = { [K in keyof T as K extends `${string}__${string}` ? never : K]: T[K]; } & { [K in ExtractHead]: Expand : never]: T[P]; }>>; }; /** * Loader 쿼리 함수에서 결과 타입 추출 (refId 제외, Hydrate 적용) */ type ExtractLoaderResult = TLoaderQb extends PuriLoaderQbFn ? Expand>, "refId">>> : never; /** * Loader 배열에서 결과 객체 타입 빌드 (재귀적으로 중첩 loader 처리) * * @example * type Loaders = [ * { as: "employees"; refId: "id"; qb: ...; loaders: [{ as: "projects"; ... }] } * ] * type Result = LoadersResult * // { employees: Array<{ ...employee fields; projects: Array<...> }> } */ export type LoadersResult = Expand<{ [L in TLoaders[number] as L["as"]]: WithLoaders, L["loaders"]>[]; }>; /** * 기본 결과와 Loader 결과 병합 */ type WithLoaders = TLoaders extends GenericPuriLoader[] ? Expand>> : TBase; /** * 단일 Subset 함수 + Loader에서 최종 결과 타입 추론 */ type InferSubsetWithLoaders Puri, TLoaders extends GenericPuriLoader[] | undefined = undefined> = Expand>, TLoaders>>>; /** * 전체 SubsetQueries + LoaderQueries 객체에서 전체 결과 맵 생성 * * @template TSubsetMap - Subset 함수들의 모음 객체 * @template TLoaderMap - Loader 배열들의 모음 객체 * * @example * type Result = InferAllSubsets< * { A: () => Puri<...>, P: () => Puri<...> }, * { A: [...loaders], P: [] } * > * // { A: InferredTypeA; P: InferredTypeP } */ export type InferAllSubsets any>, TLoaderMap extends Partial>> = { [K in keyof TSubsetMap]: InferSubsetWithLoaders; }; /** * Knex QueryBuilder에서 clear 가능한 statement 목록 */ export type ClearStatements = "with" | "select" | "columns" | "hintComments" | "where" | "union" | "using" | "join" | "group" | "order" | "having" | "limit" | "offset" | "counter" | "counters"; export {}; //# sourceMappingURL=puri-subset.types.d.ts.map