groq-builder
Version:
A **schema-aware**, strongly-typed GROQ query builder. It enables you to build GROQ queries using **auto-completion**, **type-checking**, and **runtime validation**.
54 lines (53 loc) • 4.21 kB
TypeScript
import { GroqBuilder } from "../groq-builder";
import { Empty, IsAny, LiteralUnion, Simplify, SimplifyDeep, StringKeys, TypeMismatchError, UndefinedToNull, ValueOf } from "../types/utils";
import { FragmentInputTypeTag, IGroqBuilder, Parser, ParserWithWidenedInput } from "../types/public-types";
import { Path, PathEntries, PathValue } from "../types/path-types";
import { DeepRequired } from "../types/deep-required";
import { QueryConfig } from "../types/schema-types";
import { ConditionalKey, ExtractConditionalProjectionTypes } from "./conditional-types";
export type ProjectionKey<TResultItem> = IsAny<TResultItem> extends true ? string : ProjectionKeyImpl<Simplify<PathEntries<DeepRequired<TResultItem>>>>;
type ProjectionKeyImpl<Entries> = ValueOf<{
[Key in keyof Entries]: Entries[Key] extends Array<any> ? `${StringKeys<Key>}[]` | Key : Key;
}>;
export type ProjectionKeyValue<TResultItem, TKey> = UndefinedToNull<PathValue<TResultItem, Extract<TKey extends `${infer TPath}[]` ? TPath : TKey, Path<TResultItem>>>>;
export type ProjectionMap<TResultItem> = {
[P in LiteralUnion<keyof TResultItem, string>]?: ProjectionFieldConfig<TResultItem, P extends keyof TResultItem ? UndefinedToNull<TResultItem[P]> : any>;
} & {
"..."?: true | Parser;
};
export type ProjectionMapOrCallback<TResultItem, TQueryConfig extends QueryConfig> = ProjectionMap<TResultItem> | ((q: GroqBuilder<TResultItem, TQueryConfig>) => ProjectionMap<TResultItem>);
export type ProjectionFieldConfig<TResultItem, TFieldType> = true | ProjectionKey<TResultItem> | ParserWithWidenedInput<TFieldType> | readonly [ProjectionKey<TResultItem>, ParserWithWidenedInput<TFieldType>] | IGroqBuilder;
export type ExtractProjectionResult<TResultItem, TProjectionMap> = (TProjectionMap extends {
"...": true;
} ? TResultItem : Empty) & (TProjectionMap extends {
"...": Parser<TResultItem, infer TOutput>;
} ? TOutput : Empty) & ExtractConditionalProjectionTypes<TProjectionMap> & ExtractProjectionResultFields<TResultItem, Omit<TProjectionMap, "..." | typeof FragmentInputTypeTag | ConditionalKey<string>>>;
type ExtractProjectionResultFields<TResultItem, TProjectionMap> = {
[P in keyof TProjectionMap]: TProjectionMap[P] extends IGroqBuilder<infer TValue> ? TValue : TProjectionMap[P] extends boolean ? P extends keyof TResultItem ? UndefinedToNull<TResultItem[P]> : TypeMismatchError<{
error: `⛔️ 'true' can only be used for known properties ⛔️`;
expected: keyof TResultItem;
actual: P;
}> : TProjectionMap[P] extends string ? TProjectionMap[P] extends ProjectionKey<TResultItem> ? ProjectionKeyValue<TResultItem, TProjectionMap[P]> : TypeMismatchError<{
error: `⛔️ Naked projections must be known properties ⛔️`;
expected: SimplifyDeep<ProjectionKey<TResultItem>>;
actual: TProjectionMap[P];
}> : TProjectionMap[P] extends readonly [infer TKey, infer TParser] ? TKey extends ProjectionKey<TResultItem> ? TParser extends Parser<infer TParserInput, infer TParserOutput> ? ValidateParserInput<ProjectionKeyValue<TResultItem, TKey>, TParserInput, TParserOutput> : TypeMismatchError<{
error: `⛔️ Naked projections must be known properties ⛔️`;
expected: SimplifyDeep<ProjectionKey<TResultItem>>;
actual: TKey;
}> : TypeMismatchError<{
error: `⛔️ Naked projections must be known properties ⛔️`;
expected: SimplifyDeep<ProjectionKey<TResultItem>>;
actual: TKey;
}> : TProjectionMap[P] extends Parser<infer TParserInput, infer TParserOutput> ? P extends keyof TResultItem ? ValidateParserInput<UndefinedToNull<TResultItem[P]>, TParserInput, TParserOutput> : TypeMismatchError<{
error: `⛔️ Parser can only be used with known properties ⛔️`;
expected: keyof TResultItem;
actual: P;
}> : never;
};
export type ValidateParserInput<TIncomingValue, TParserInput, TParserOutput> = TIncomingValue extends TParserInput ? TParserOutput : IsAny<TIncomingValue> extends true ? TParserOutput : TypeMismatchError<{
error: `⛔️ Parser expects a different input type ⛔️`;
expected: TParserInput;
actual: TIncomingValue;
}>;
export {};