@proofkit/fmodata
Version:
FileMaker OData API client
134 lines (122 loc) • 6.32 kB
text/typescript
import type { Column } from "../../orm/column";
import type { CountedListResult } from "../../types";
/**
* Type-safe orderBy type that provides better DX than odata-query's default.
*
* Supported forms:
* - `keyof T` - single field name (defaults to ascending)
* - `[keyof T, 'asc' | 'desc']` - single field with explicit direction
* - `Array<[keyof T, 'asc' | 'desc']>` - multiple fields with directions
*
* This type intentionally EXCLUDES `Array<keyof T>` to avoid ambiguity
* between [field1, field2] and [field, direction].
*/
export type TypeSafeOrderBy<T> =
| (keyof T & string) // Single field name
| [keyof T & string, "asc" | "desc"] // Single field with direction
| [keyof T & string, "asc" | "desc"][]; // Multiple fields with directions
// Internal type for expand configuration
export type { ExpandConfig } from "../builders/shared-types";
// Type to represent expanded relations
// biome-ignore lint/suspicious/noExplicitAny: Dynamic schema and selected types from user input
export type ExpandedRelations = Record<string, { schema: any; selected: any; nested?: ExpandedRelations }>;
/**
* Extract the value type from a Column.
* This uses the phantom type stored in Column to get the actual value type (output type for reading).
*/
// biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer
type ExtractColumnType<C> = C extends Column<infer T, any, any, any> ? T : never;
/**
* Map a select object to its return type.
* For each key in the select object, extract the type from the corresponding Column.
*/
type MapSelectToReturnType<
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
TSelect extends Record<string, Column<any, any, any, any>>,
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape
_TSchema extends Record<string, any>,
> = {
[K in keyof TSelect]: ExtractColumnType<TSelect[K]>;
};
/**
* Helper: Resolve a single expand's return type, including nested expands
*/
// biome-ignore lint/suspicious/noExplicitAny: Dynamic schema and selected types from user input
export type ResolveExpandType<Exp extends { schema: any; selected: any; nested?: ExpandedRelations }> = // Handle the selected fields
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
(Exp["selected"] extends Record<string, Column<any, any, any, any>>
? MapSelectToReturnType<Exp["selected"], Exp["schema"]>
: Exp["selected"] extends keyof Exp["schema"]
? Pick<Exp["schema"], Exp["selected"]>
: Exp["schema"]) &
// Recursively handle nested expands
// biome-ignore lint/complexity/noBannedTypes: Empty object type represents no nested expands
(Exp["nested"] extends ExpandedRelations ? ResolveExpandedRelations<Exp["nested"]> : {});
/**
* Helper: Resolve all expanded relations recursively
*/
export type ResolveExpandedRelations<Exps extends ExpandedRelations> = {
[K in keyof Exps]: ResolveExpandType<Exps[K]>[];
};
/**
* System columns option for select() method.
* Allows explicitly requesting ROWID and/or ROWMODID when using select().
*/
export interface SystemColumnsOption {
ROWID?: boolean;
ROWMODID?: boolean;
}
export type { CountedListResult } from "../../types";
/**
* Extract system columns type from SystemColumnsOption.
* Returns an object type with ROWID and/or ROWMODID properties when set to true.
*/
export type SystemColumnsFromOption<T extends SystemColumnsOption | undefined> = (T extends { ROWID: true }
? { ROWID: number }
: // biome-ignore lint/complexity/noBannedTypes: Empty object type represents no ROWID field
{}) &
// biome-ignore lint/complexity/noBannedTypes: Empty object type represents no ROWMODID field
(T extends { ROWMODID: true } ? { ROWMODID: number } : {});
export type QueryReturnType<
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any schema shape
T extends Record<string, any>,
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
Selected extends keyof T | Record<string, Column<any, any, any, any>>,
SingleMode extends "exact" | "maybe" | false,
IsCount extends boolean,
Expands extends ExpandedRelations,
IncludeCount extends boolean,
SystemCols extends SystemColumnsOption | undefined = undefined,
> = IsCount extends true
? number
: IncludeCount extends true
? CountedListResult<
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
[Selected] extends [Record<string, Column<any, any, any, any>>]
? MapSelectToReturnType<Selected, T> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>
: [Selected] extends [keyof T]
? Pick<T, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>
: never
>
: // Use tuple wrapping [Selected] extends [...] to prevent distribution over unions
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
[Selected] extends [Record<string, Column<any, any, any, any>>]
? SingleMode extends "exact"
? MapSelectToReturnType<Selected, T> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>
: SingleMode extends "maybe"
?
| (MapSelectToReturnType<Selected, T> &
ResolveExpandedRelations<Expands> &
SystemColumnsFromOption<SystemCols>)
| null
: (MapSelectToReturnType<Selected, T> &
ResolveExpandedRelations<Expands> &
SystemColumnsFromOption<SystemCols>)[]
: // Use tuple wrapping to prevent distribution over union of keys
[Selected] extends [keyof T]
? SingleMode extends "exact"
? Pick<T, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>
: SingleMode extends "maybe"
? (Pick<T, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>) | null
: (Pick<T, Selected> & ResolveExpandedRelations<Expands> & SystemColumnsFromOption<SystemCols>)[]
: never;