UNPKG

@supabase/postgrest-js

Version:
603 lines (571 loc) 21.7 kB
import { Ast } from './parser' import { AggregateFunctions, ContainsNull, GenericRelationship, GenericSchema, GenericTable, IsNonEmptyArray, TablesAndViews, UnionToArray, } from './types' export type IsAny<T> = 0 extends 1 & T ? true : false export type SelectQueryError<Message extends string> = { error: true } & Message /* ** Because of pg-meta types generation there is some cases where a same relationship can be duplicated ** if the relation is across schemas and views this ensure that we dedup those relations and treat them ** as postgrest would. ** This is no longer the case and has been patched here: https://github.com/supabase/postgres-meta/pull/809 ** But we still need this for retro-compatibilty with older generated types ** TODO: Remove this in next major version */ export type DeduplicateRelationships<T extends readonly unknown[]> = T extends readonly [ infer First, ...infer Rest ] ? First extends Rest[number] ? DeduplicateRelationships<Rest extends readonly unknown[] ? Rest : []> : [First, ...DeduplicateRelationships<Rest extends readonly unknown[] ? Rest : []>] : T export type GetFieldNodeResultName<Field extends Ast.FieldNode> = Field['alias'] extends string ? Field['alias'] : Field['aggregateFunction'] extends AggregateFunctions ? Field['aggregateFunction'] : Field['name'] type FilterRelationNodes<Nodes extends Ast.Node[]> = UnionToArray< { [K in keyof Nodes]: Nodes[K] extends Ast.SpreadNode ? Nodes[K]['target'] : Nodes[K] extends Ast.FieldNode ? IsNonEmptyArray<Nodes[K]['children']> extends true ? Nodes[K] : never : never }[number] > type ResolveRelationships< Schema extends GenericSchema, RelationName extends string, Relationships extends GenericRelationship[], Nodes extends Ast.FieldNode[] > = UnionToArray<{ [K in keyof Nodes]: Nodes[K] extends Ast.FieldNode ? ResolveRelationship<Schema, Relationships, Nodes[K], RelationName> extends infer Relation ? Relation extends { relation: { referencedRelation: string foreignKeyName: string match: string } from: string } ? { referencedTable: Relation['relation']['referencedRelation'] fkName: Relation['relation']['foreignKeyName'] from: Relation['from'] match: Relation['relation']['match'] fieldName: GetFieldNodeResultName<Nodes[K]> } : Relation : never : never }>[0] /** * Checks if a relation is implicitly referenced twice, requiring disambiguation */ type IsDoubleReference<T, U> = T extends { referencedTable: infer RT fieldName: infer FN match: infer M } ? M extends 'col' | 'refrel' ? U extends { referencedTable: RT; fieldName: FN; match: M } ? true : false : false : false /** * Compares one element with all other elements in the array to find duplicates */ type CheckDuplicates<Arr extends any[], Current> = Arr extends [infer Head, ...infer Tail] ? IsDoubleReference<Current, Head> extends true ? Head | CheckDuplicates<Tail, Current> // Return the Head if duplicate : CheckDuplicates<Tail, Current> // Otherwise, continue checking : never /** * Iterates over the elements of the array to find duplicates */ type FindDuplicatesWithinDeduplicated<Arr extends any[]> = Arr extends [infer Head, ...infer Tail] ? CheckDuplicates<Tail, Head> | FindDuplicatesWithinDeduplicated<Tail> : never type FindDuplicates<Arr extends any[]> = FindDuplicatesWithinDeduplicated< DeduplicateRelationships<Arr> > export type CheckDuplicateEmbededReference< Schema extends GenericSchema, RelationName extends string, Relationships extends GenericRelationship[], Nodes extends Ast.Node[] > = FilterRelationNodes<Nodes> extends infer RelationsNodes ? RelationsNodes extends Ast.FieldNode[] ? ResolveRelationships< Schema, RelationName, Relationships, RelationsNodes > extends infer ResolvedRels ? ResolvedRels extends unknown[] ? FindDuplicates<ResolvedRels> extends infer Duplicates ? Duplicates extends never ? false : Duplicates extends { fieldName: infer FieldName } ? FieldName extends string ? { [K in FieldName]: SelectQueryError<`table "${RelationName}" specified more than once use hinting for desambiguation`> } : false : false : false : false : false : false : false /** * Returns a boolean representing whether there is a foreign key referencing * a given relation. */ type HasFKeyToFRel<FRelName, Relationships> = Relationships extends [infer R] ? R extends { referencedRelation: FRelName } ? true : false : Relationships extends [infer R, ...infer Rest] ? HasFKeyToFRel<FRelName, [R]> extends true ? true : HasFKeyToFRel<FRelName, Rest> : false /** * Checks if there is more than one relation to a given foreign relation name in the Relationships. */ type HasMultipleFKeysToFRelDeduplicated<FRelName, Relationships> = Relationships extends [ infer R, ...infer Rest ] ? R extends { referencedRelation: FRelName } ? HasFKeyToFRel<FRelName, Rest> extends true ? true : HasMultipleFKeysToFRelDeduplicated<FRelName, Rest> : HasMultipleFKeysToFRelDeduplicated<FRelName, Rest> : false type HasMultipleFKeysToFRel< FRelName, Relationships extends unknown[] > = HasMultipleFKeysToFRelDeduplicated<FRelName, DeduplicateRelationships<Relationships>> type CheckRelationshipError< Schema extends GenericSchema, Relationships extends GenericRelationship[], CurrentTableOrView extends keyof TablesAndViews<Schema> & string, FoundRelation > = FoundRelation extends SelectQueryError<string> ? FoundRelation : // If the relation is a reverse relation with no hint (matching by name) FoundRelation extends { relation: { referencedRelation: infer RelatedRelationName name: string } direction: 'reverse' } ? RelatedRelationName extends string ? // We check if there is possible confusion with other relations with this table HasMultipleFKeysToFRel<RelatedRelationName, Relationships> extends true ? // If there is, postgrest will fail at runtime, and require desambiguation via hinting SelectQueryError<`Could not embed because more than one relationship was found for '${RelatedRelationName}' and '${CurrentTableOrView}' you need to hint the column with ${RelatedRelationName}!<columnName> ?`> : FoundRelation : never : // Same check for forward relationships, but we must gather the relationships from the found relation FoundRelation extends { relation: { referencedRelation: infer RelatedRelationName name: string } direction: 'forward' from: infer From } ? RelatedRelationName extends string ? From extends keyof TablesAndViews<Schema> & string ? HasMultipleFKeysToFRel< RelatedRelationName, TablesAndViews<Schema>[From]['Relationships'] > extends true ? SelectQueryError<`Could not embed because more than one relationship was found for '${From}' and '${RelatedRelationName}' you need to hint the column with ${From}!<columnName> ?`> : FoundRelation : never : never : FoundRelation /** * Resolves relationships for embedded resources and retrieves the referenced Table */ export type ResolveRelationship< Schema extends GenericSchema, Relationships extends GenericRelationship[], Field extends Ast.FieldNode, CurrentTableOrView extends keyof TablesAndViews<Schema> & string > = ResolveReverseRelationship< Schema, Relationships, Field, CurrentTableOrView > extends infer ReverseRelationship ? ReverseRelationship extends false ? CheckRelationshipError< Schema, Relationships, CurrentTableOrView, ResolveForwardRelationship<Schema, Field, CurrentTableOrView> > : CheckRelationshipError<Schema, Relationships, CurrentTableOrView, ReverseRelationship> : never /** * Resolves reverse relationships (from children to parent) */ type ResolveReverseRelationship< Schema extends GenericSchema, Relationships extends GenericRelationship[], Field extends Ast.FieldNode, CurrentTableOrView extends keyof TablesAndViews<Schema> & string > = FindFieldMatchingRelationships<Schema, Relationships, Field> extends infer FoundRelation ? FoundRelation extends never ? false : FoundRelation extends { referencedRelation: infer RelatedRelationName } ? RelatedRelationName extends string ? RelatedRelationName extends keyof TablesAndViews<Schema> ? // If the relation was found via hinting we just return it without any more checks FoundRelation extends { hint: string } ? { referencedTable: TablesAndViews<Schema>[RelatedRelationName] relation: FoundRelation direction: 'reverse' from: CurrentTableOrView } : // If the relation was found via implicit relation naming, we must ensure there is no conflicting matches HasMultipleFKeysToFRel<RelatedRelationName, Relationships> extends true ? SelectQueryError<`Could not embed because more than one relationship was found for '${RelatedRelationName}' and '${CurrentTableOrView}' you need to hint the column with ${RelatedRelationName}!<columnName> ?`> : { referencedTable: TablesAndViews<Schema>[RelatedRelationName] relation: FoundRelation direction: 'reverse' from: CurrentTableOrView } : SelectQueryError<`Relation '${RelatedRelationName}' not found in schema.`> : false : false : false export type FindMatchingTableRelationships< Schema extends GenericSchema, Relationships extends GenericRelationship[], value extends string > = Relationships extends [infer R, ...infer Rest] ? Rest extends GenericRelationship[] ? R extends { referencedRelation: infer ReferencedRelation } ? ReferencedRelation extends keyof Schema['Tables'] ? R extends { foreignKeyName: value } ? R & { match: 'fkname' } : R extends { referencedRelation: value } ? R & { match: 'refrel' } : R extends { columns: [value] } ? R & { match: 'col' } : FindMatchingTableRelationships<Schema, Rest, value> : FindMatchingTableRelationships<Schema, Rest, value> : false : false : false export type FindMatchingViewRelationships< Schema extends GenericSchema, Relationships extends GenericRelationship[], value extends string > = Relationships extends [infer R, ...infer Rest] ? Rest extends GenericRelationship[] ? R extends { referencedRelation: infer ReferencedRelation } ? ReferencedRelation extends keyof Schema['Views'] ? R extends { foreignKeyName: value } ? R & { match: 'fkname' } : R extends { referencedRelation: value } ? R & { match: 'refrel' } : R extends { columns: [value] } ? R & { match: 'col' } : FindMatchingViewRelationships<Schema, Rest, value> : FindMatchingViewRelationships<Schema, Rest, value> : false : false : false export type FindMatchingHintTableRelationships< Schema extends GenericSchema, Relationships extends GenericRelationship[], hint extends string, name extends string > = Relationships extends [infer R, ...infer Rest] ? Rest extends GenericRelationship[] ? R extends { referencedRelation: infer ReferencedRelation } ? ReferencedRelation extends name ? R extends { foreignKeyName: hint } ? R & { match: 'fkname' } : R extends { referencedRelation: hint } ? R & { match: 'refrel' } : R extends { columns: [hint] } ? R & { match: 'col' } : FindMatchingHintTableRelationships<Schema, Rest, hint, name> : FindMatchingHintTableRelationships<Schema, Rest, hint, name> : false : false : false export type FindMatchingHintViewRelationships< Schema extends GenericSchema, Relationships extends GenericRelationship[], hint extends string, name extends string > = Relationships extends [infer R, ...infer Rest] ? Rest extends GenericRelationship[] ? R extends { referencedRelation: infer ReferencedRelation } ? ReferencedRelation extends name ? R extends { foreignKeyName: hint } ? R & { match: 'fkname' } : R extends { referencedRelation: hint } ? R & { match: 'refrel' } : R extends { columns: [hint] } ? R & { match: 'col' } : FindMatchingHintViewRelationships<Schema, Rest, hint, name> : FindMatchingHintViewRelationships<Schema, Rest, hint, name> : false : false : false type IsColumnsNullable< Table extends Pick<GenericTable, 'Row'>, Columns extends (keyof Table['Row'])[] > = Columns extends [infer Column, ...infer Rest] ? Column extends keyof Table['Row'] ? ContainsNull<Table['Row'][Column]> extends true ? true : IsColumnsNullable<Table, Rest extends (keyof Table['Row'])[] ? Rest : []> : false : false // Check weither or not a 1-1 relation is nullable by checking against the type of the columns export type IsRelationNullable< Table extends GenericTable, Relation extends GenericRelationship > = IsColumnsNullable<Table, Relation['columns']> type TableForwardRelationships< Schema extends GenericSchema, TName > = TName extends keyof TablesAndViews<Schema> ? UnionToArray< RecursivelyFindRelationships<Schema, TName, keyof TablesAndViews<Schema>> > extends infer R ? R extends (GenericRelationship & { from: keyof TablesAndViews<Schema> })[] ? R : [] : [] : [] type RecursivelyFindRelationships< Schema extends GenericSchema, TName, Keys extends keyof TablesAndViews<Schema> > = Keys extends infer K ? K extends keyof TablesAndViews<Schema> ? FilterRelationships<TablesAndViews<Schema>[K]['Relationships'], TName, K> extends never ? RecursivelyFindRelationships<Schema, TName, Exclude<Keys, K>> : | FilterRelationships<TablesAndViews<Schema>[K]['Relationships'], TName, K> | RecursivelyFindRelationships<Schema, TName, Exclude<Keys, K>> : false : false type FilterRelationships<R, TName, From> = R extends readonly (infer Rel)[] ? Rel extends { referencedRelation: TName } ? Rel & { from: From } : never : never export type ResolveForwardRelationship< Schema extends GenericSchema, Field extends Ast.FieldNode, CurrentTableOrView extends keyof TablesAndViews<Schema> & string > = FindFieldMatchingRelationships< Schema, TablesAndViews<Schema>[Field['name']]['Relationships'], Ast.FieldNode & { name: CurrentTableOrView; hint: Field['hint'] } > extends infer FoundByName ? FoundByName extends GenericRelationship ? { referencedTable: TablesAndViews<Schema>[Field['name']] relation: FoundByName direction: 'forward' from: Field['name'] type: 'found-by-name' } : FindFieldMatchingRelationships< Schema, TableForwardRelationships<Schema, CurrentTableOrView>, Field > extends infer FoundByMatch ? FoundByMatch extends GenericRelationship & { from: keyof TablesAndViews<Schema> } ? { referencedTable: TablesAndViews<Schema>[FoundByMatch['from']] relation: FoundByMatch direction: 'forward' from: CurrentTableOrView type: 'found-by-match' } : FindJoinTableRelationship< Schema, CurrentTableOrView, Field['name'] > extends infer FoundByJoinTable ? FoundByJoinTable extends GenericRelationship ? { referencedTable: TablesAndViews<Schema>[FoundByJoinTable['referencedRelation']] relation: FoundByJoinTable & { match: 'refrel' } direction: 'forward' from: CurrentTableOrView type: 'found-by-join-table' } : SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`> : SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`> : SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`> : SelectQueryError<`could not find the relation between ${CurrentTableOrView} and ${Field['name']}`> /** * Given a CurrentTableOrView, finds all join tables to this relation. * For example, if products and categories are linked via product_categories table: * * @example * Given: * - CurrentTableView = 'products' * - FieldName = "categories" * * It should return this relationship from product_categories: * { * foreignKeyName: "product_categories_category_id_fkey", * columns: ["category_id"], * isOneToOne: false, * referencedRelation: "categories", * referencedColumns: ["id"] * } */ type ResolveJoinTableRelationship< Schema extends GenericSchema, CurrentTableOrView extends keyof TablesAndViews<Schema> & string, FieldName extends string > = { [TableName in keyof TablesAndViews<Schema>]: DeduplicateRelationships< TablesAndViews<Schema>[TableName]['Relationships'] > extends readonly (infer Rel)[] ? Rel extends { referencedRelation: CurrentTableOrView } ? DeduplicateRelationships< TablesAndViews<Schema>[TableName]['Relationships'] > extends readonly (infer OtherRel)[] ? OtherRel extends { referencedRelation: FieldName } ? OtherRel : never : never : never : never }[keyof TablesAndViews<Schema>] export type FindJoinTableRelationship< Schema extends GenericSchema, CurrentTableOrView extends keyof TablesAndViews<Schema> & string, FieldName extends string > = ResolveJoinTableRelationship<Schema, CurrentTableOrView, FieldName> extends infer Result ? [Result] extends [never] ? false : Result : never /** * Finds a matching relationship based on the FieldNode's name and optional hint. */ export type FindFieldMatchingRelationships< Schema extends GenericSchema, Relationships extends GenericRelationship[], Field extends Ast.FieldNode > = Field extends { hint: string } ? FindMatchingHintTableRelationships< Schema, Relationships, Field['hint'], Field['name'] > extends GenericRelationship ? FindMatchingHintTableRelationships<Schema, Relationships, Field['hint'], Field['name']> & { branch: 'found-in-table-via-hint' hint: Field['hint'] } : FindMatchingHintViewRelationships< Schema, Relationships, Field['hint'], Field['name'] > extends GenericRelationship ? FindMatchingHintViewRelationships<Schema, Relationships, Field['hint'], Field['name']> & { branch: 'found-in-view-via-hint' hint: Field['hint'] } : SelectQueryError<'Failed to find matching relation via hint'> : FindMatchingTableRelationships<Schema, Relationships, Field['name']> extends GenericRelationship ? FindMatchingTableRelationships<Schema, Relationships, Field['name']> & { branch: 'found-in-table-via-name' name: Field['name'] } : FindMatchingViewRelationships<Schema, Relationships, Field['name']> extends GenericRelationship ? FindMatchingViewRelationships<Schema, Relationships, Field['name']> & { branch: 'found-in-view-via-name' name: Field['name'] } : SelectQueryError<'Failed to find matching relation via name'> export type JsonPathToAccessor<Path extends string> = Path extends `${infer P1}->${infer P2}` ? P2 extends `>${infer Rest}` // Handle ->> operator ? JsonPathToAccessor<`${P1}.${Rest}`> : P2 extends string // Handle -> operator ? JsonPathToAccessor<`${P1}.${P2}`> : Path : Path extends `>${infer Rest}` // Clean up any remaining > characters ? JsonPathToAccessor<Rest> : Path extends `${infer P1}::${infer _}` // Handle type casting ? JsonPathToAccessor<P1> : Path extends `${infer P1}${')' | ','}${infer _}` // Handle closing parenthesis and comma ? P1 : Path export type JsonPathToType<T, Path extends string> = Path extends '' ? T : ContainsNull<T> extends true ? JsonPathToType<Exclude<T, null>, Path> : Path extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? JsonPathToType<T[Key], Rest> : never : Path extends keyof T ? T[Path] : never export type IsStringUnion<T> = string extends T ? false : T extends string ? [T] extends [never] ? false : true : false type ComputedField< Schema extends GenericSchema, RelationName extends keyof TablesAndViews<Schema>, FieldName extends keyof TablesAndViews<Schema>[RelationName]['Row'] > = FieldName extends keyof Schema['Functions'] ? Schema['Functions'][FieldName] extends { Args: { '': TablesAndViews<Schema>[RelationName]['Row'] } Returns: any } ? FieldName : never : never // Given a relation name (Table or View) extract all the "computed fields" based on the Row // object, and the schema functions definitions export type GetComputedFields< Schema extends GenericSchema, RelationName extends keyof TablesAndViews<Schema> > = { [K in keyof TablesAndViews<Schema>[RelationName]['Row']]: ComputedField<Schema, RelationName, K> }[keyof TablesAndViews<Schema>[RelationName]['Row']]