Created
May 2, 2019 23:21
-
-
Save icanhasjonas/fde5a52dcc44d51f94d0b2184564b225 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type ExtractArgType<T> = T extends ArgBuilder<infer U> ? U : T | |
type ExtractTypeBuilderType<T> = T extends TypeBuilder<infer U> ? U : T | |
type ExtractFieldType<T> = T extends FieldBuilder<string, {}, infer U, boolean> ? U : T | |
type AcceptedType = string | number | boolean | TypeBuilder<any> | |
class SchemaBuilder<T = {}> { | |
query<Builder extends TypeBuilder<{}>>(builder: (typeBuilder: TypeBuilder) => Builder) { | |
const result = builder(new TypeBuilder()) | |
return (this as unknown) as SchemaBuilder<T & ExtractTypeBuilderType<Builder>> | |
} | |
} | |
class TypeBuilder<T = {}> { | |
description(text: string) { | |
return this | |
} | |
field<FieldName extends string, Builder extends FieldBuilder<FieldName, {}, any, true>>(name: FieldName, builder: (fieldBuilder: FieldBuilder<FieldName>) => Builder) { | |
const result = builder(new FieldBuilder<FieldName>()) | |
return (this as unknown) as TypeBuilder<T & { [P in FieldName]: ExtractFieldType<Builder> }> | |
} | |
} | |
class FieldBuilder<FieldName extends string, ResolverArgs extends {} = {}, ResolvedType = never, IsResolved extends boolean = false> { | |
description(text: string) { | |
return this | |
} | |
type<T>() { | |
return (this as unknown) as FieldBuilder<FieldName, ResolverArgs, T, IsResolved> | |
} | |
arg<ArgName extends string, TArg extends ArgBuilder<any> = ArgBuilder<string>>(name: ArgName, builder?: (argBuilder: ArgBuilder) => TArg) { | |
return (this as unknown) as FieldBuilder<FieldName, ResolverArgs & { [P in ArgName]: ExtractArgType<TArg> }, ResolvedType, IsResolved> | |
} | |
resolve<T extends Promise<ResolvedType> | ResolvedType>(resolver: (args: ResolverArgs) => T) { | |
return (this as unknown) as FieldBuilder<FieldName, ResolverArgs, ResolvedType, true> | |
} | |
} | |
class ArgBuilder<T = string> { | |
description(text: string) { | |
return this | |
} | |
type<ArgType extends AcceptedType>(t?: ArgType) { | |
return (this as unknown) as ArgBuilder<ExtractTypeBuilderType<ArgType>> | |
} | |
required() { | |
return this | |
} | |
default(value: ExtractTypeBuilderType<T>) { | |
return this | |
} | |
} | |
function createRangeQuery<T>() { | |
return new TypeBuilder() | |
.field('eq', x => x.type<T>().description('match exactly')) | |
.field('neq', x => x.type<T>().description('don\'t match')) | |
.field('lt', x => x.type<T>().description('less than')) | |
.field('lte', x => x.type<T>().description('less than or equal')) | |
.field('gt', x => x.type<T>().description('greater than')) | |
.field('gte', x => x.type<T>().description('greater than or equal')) | |
.field('in', x => x.type<T[]>().description('in set')) | |
.field('nin', x => x.type<T[]>().description('not in set')) | |
} | |
const s = new SchemaBuilder().query(q => | |
q | |
.description('This is the query') | |
.field('hello', f => | |
f | |
.type<string>() | |
.description('greet someone with {name}') | |
.arg('name', x => x.required().description('the name of the greeting')) | |
.arg('filter', x => | |
x | |
.required() | |
.description('any filter of stuff') | |
.type(createRangeQuery<number>()), | |
) | |
.arg('repeat', x => | |
x | |
.description('number of times to repeat greeting') | |
.type<number>() | |
.default(1), | |
) | |
.resolve(async ({ name, repeat, filter }) => { | |
return `Hello, ${name}` | |
}), | |
) | |
.field('apa', x => x), | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment