import { RefCollectionName, RefDefinition, RelationshipRef } from '../../query/types/index.js'; import { StringKey } from '../../types.js'; import { JsonType, RecordType } from '../data-types/index.js'; import { CollectionNameFromModels, Models } from './models.js'; export type SchemaPaths, CN extends CollectionNameFromModels> = RecordPaths; type MAX_RELATION_DEPTH = 2; type DEPTH = 0 | 1 | 2; type IncrementDepth = D extends 0 ? 1 : D extends 1 ? 2 : never; export type ModelPaths, CN extends CollectionNameFromModels, RDepth extends DEPTH = 0> = SchemaPaths | RelationshipPaths; export type RelationshipPaths, CN extends CollectionNameFromModels, RDepth extends DEPTH = 0> = RDepth extends MAX_RELATION_DEPTH ? string : { [K in RelationshipRef]: `${K & string}.${ModelPaths, IncrementDepth>}`; }[RelationshipRef]; export type ResolveModelPath, CN extends CollectionNameFromModels, Path extends string, Current extends RecordType = M[CN]['schema']> = Path extends `${infer Head}.${infer Tail}` ? Head extends StringKey ? ResolveModelPath : Head extends RelationshipRef ? ResolveModelPath, Tail, M[CN]['schema']> : never : Current['properties'][Path]; export type ModelRelationshipPaths, CN extends CollectionNameFromModels, RDepth extends DEPTH = 0> = RDepth extends MAX_RELATION_DEPTH ? string : { [K in RelationshipRef]: `${K & string}` | `${K & string}.${ModelRelationshipPaths, IncrementDepth>}`; }[RelationshipRef]; export type ResolveRelationshipPath, CN extends CollectionNameFromModels, Path extends string> = Path extends `${infer Head}.${infer Tail}` ? Head extends RelationshipRef ? ResolveRelationshipPath, Tail> : never : Path extends RelationshipRef ? RefDefinition : never; /** * Expand a record type into a union of all possible paths, including nested records and the root time of a nested record */ type RecordPaths = R extends RecordType ? { [K in keyof R['properties']]: R['properties'][K] extends RecordType ? `${K & string}` | `${K & string}.${RecordPaths}` : R['properties'][K] extends JsonType ? `${K & string}` | `${K & string}.${string}` : K & string; }[StringKey] : never; /** * SplitPath<"a.b.c", "."> --> ["a", "b", "c"] */ type SplitPath = S extends `${infer Head}${Delimiter}${infer Tail}` ? [Head, ...SplitPath] : [S]; /** * Utility to turn a union of object types into a single * intersection type. For example: * * { a: number } | { b: string } * becomes: * { a: number } & { b: string } */ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; /** * PathFilteredHelper: * 1. If "a" is a key of T, we keep T["a"] under the key "a", * 2. then recursively process the remainder of the path ("b") on T["a"]. * 3. If we exhaust the path, we simply return T (meaning keep the entire sub-object). */ type PathFilteredHelper = Keys extends [ infer Head, ...infer Tail ] ? Head extends StringKey ? { [K in Head]: PathFilteredHelper>; } : {} : T; /** * PathFiltered takes each path in the union, * runs it through PathFilteredHelper, and intersects the results. * * Example: * PathFiltered * => { age: number } & { address: { street: string } } */ export type PathFiltered = UnionToIntersection> : never : never>; export {};