Skip to content

Commit

Permalink
feat(extension/schema-errors): introduce' (#1189)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Oct 16, 2024
1 parent 17dd0b4 commit 1c183af
Show file tree
Hide file tree
Showing 71 changed files with 1,293 additions and 1,295 deletions.
7 changes: 3 additions & 4 deletions src/entrypoints/utilities-for-generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ export * from '../layers/2_Select/__.js'
export { type SchemaIndex as SchemaIndexBase } from '../layers/4_generator/generators/SchemaIndex.js'
export type {
ConfigGetOutputError,
ResolveOutputReturnRootField,
ResolveOutputReturnRootType,
HandleOutput,
HandleOutputGraffleRootField,
} from '../layers/6_client/handleOutput.js'
export { type DocumentRunner } from '../layers/6_client/requestMethods/document.js'
export type { Config } from '../layers/6_client/Settings/Config.js'
export { type AddTypenameToSelectedRootTypeResultFields } from '../layers/6_client/Settings/Config.js'
export { type SchemaDrivenDataMap } from '../layers/7_extensions/CustomScalars/schemaDrivenDataMap/types.js'
export { HKT } from '../lib/hkt/__.js'
export { type Exact, type ExactNonEmpty, type UnionExpanded } from '../lib/prelude.js'
export { TypeFunction } from '../lib/type-function/__.js'
4 changes: 2 additions & 2 deletions src/layers/1_Schema/Output/Output.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TSError } from '../../../lib/TSError.js'
import type { TSErrorDescriptive } from '../../../lib/TSError.js'
import { readMaybeThunk } from '../core/helpers.js'
import type { Any, Named } from './typeGroups.js'
import type { __typename } from './types/__typename.js'
Expand All @@ -20,7 +20,7 @@ export type Unwrap<$Type extends any> =
$Type extends Nullable<infer $innerType> ? Unwrap<$innerType> :
$Type extends __typename ? $Type['type'] :
$Type extends Named ? $Type :
TSError<'Unwrap', 'Unknown $Type', { $Type: $Type }>
TSErrorDescriptive<'Unwrap', 'Unknown $Type', { $Type: $Type }>

// dprint-ignore
export type UnwrapNullable<$Type> =
Expand Down
4 changes: 2 additions & 2 deletions src/layers/2_Select/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ export type GetRootTypeNameOfOperation<$Document extends SomeDocument, $Name ext
export type GetOperation<$Document extends SomeDocument, $Name extends string> =
FirstNonUnknownNever<[
// @ts-expect-error could be unknown
$Document[OperationType.Mutation][$Name],
$Document[OperationTypeNode.MUTATION][$Name],
// @ts-expect-error could be unknown
$Document[OperationType.Query][$Name]
$Document[OperationTypeNode.QUERY][$Name]
]>

export interface OperationNormalized {
Expand Down
2 changes: 2 additions & 0 deletions src/layers/2_Select/selectionSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { Directive } from './Directive/__.js'
import type { Indicator } from './Indicator/__.js'
import { type SelectAlias } from './SelectAlias.js'

export type RootType = AnySelectionSet

export type AnySelectionSet = {
[k: string]: FieldValue | ArgsObject
} // & SpecialFields
Expand Down
4 changes: 2 additions & 2 deletions src/layers/3_InferResult/Field.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Simplify } from 'type-fest'
import type { TSError } from '../../lib/TSError.js'
import type { TSErrorDescriptive } from '../../lib/TSError.js'
import type { Schema } from '../1_Schema/__.js'
import type { Select } from '../2_Select/__.js'
import type { SchemaIndex } from '../4_generator/generators/SchemaIndex.js'
Expand Down Expand Up @@ -33,7 +33,7 @@ type FieldType<
$Type extends Schema.Object$2 ? Object<$SelectionSet, $Schema, $Type> :
$Type extends Schema.Interface ? Interface<$SelectionSet, $Schema, $Type> :
$Type extends Schema.Union ? Union<$SelectionSet, $Schema, $Type> :
TSError<'FieldType', `Unknown type`, { $Type: $Type; $SelectionSet: $SelectionSet; $Schema:$Schema }>
TSErrorDescriptive<'FieldType', `Unknown type`, { $Type: $Type; $SelectionSet: $SelectionSet; $Schema:$Schema }>

// dprint-ignore
type FieldDirectiveInclude<$SelectionSet> =
Expand Down
14 changes: 7 additions & 7 deletions src/layers/3_InferResult/Object.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Simplify } from 'type-fest'
import { type StringKeyof } from '../../lib/prelude.js'
import type { TSError } from '../../lib/TSError.js'
import type { TSErrorDescriptive } from '../../lib/TSError.js'
import type { Schema } from '../1_Schema/__.js'
import type { Select } from '../2_Select/__.js'
import type { SchemaIndex } from '../4_generator/generators/SchemaIndex.js'
Expand All @@ -20,18 +20,18 @@ export type Object<$SelectionSet, $Schema extends SchemaIndex, $Node extends Sch
)>

// dprint-ignore
type SelectionNonSelectAlias<$SelectionSet , $Schema extends SchemaIndex, $Node extends Schema.Output.Object$2> =
type SelectionNonSelectAlias<$SelectionSet , $Schema extends SchemaIndex, $SchemaNode extends Schema.Output.Object$2> =
{
[$Select in PickSelectsPositiveIndicatorAndNotSelectAlias<$SelectionSet>]:
$Select extends keyof $Node['fields']
? Field<$SelectionSet[$Select], $Node['fields'][$Select], $Schema>
: Errors.UnknownFieldName<$Select, $Node>
[$Key in PickSelectsPositiveIndicatorAndNotSelectAlias<$SelectionSet>]:
$Key extends keyof $SchemaNode['fields']
? Field<$SelectionSet[$Key], $SchemaNode['fields'][$Key], $Schema>
: Errors.UnknownFieldName<$Key, $SchemaNode>
}

// dprint-ignore
export namespace Errors {
export type UnknownFieldName<$FieldName extends string, $Object extends Schema.Object$2 | Schema.Output.RootType> =
TSError<'Object', `field "${$FieldName}" does not exist on object "${$Object['fields']['__typename']['type']['type']}"`>
TSErrorDescriptive<'Object', `field "${$FieldName}" does not exist on object "${$Object['fields']['__typename']['type']['type']}"`>
}

// dprint-ignore
Expand Down
124 changes: 62 additions & 62 deletions src/layers/3_InferResult/__.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type * as Schema from '../../../tests/_/schemas/kitchen-sink/graffle/modules/SchemaBuildtime.js'
import type { Index } from '../../../tests/_/schemas/kitchen-sink/graffle/modules/SchemaIndex.js'
import type * as SelectionSets from '../../../tests/_/schemas/kitchen-sink/graffle/modules/SelectionSets.js'
import { AssertEqual } from '../../lib/assert-equal.js'
import { assertEqual } from '../../lib/assert-equal.js'
import type { InferResult } from './__.js'
import type { PickSelectsPositiveIndicatorAndNotSelectAlias } from './Object.js'

Expand All @@ -10,118 +10,118 @@ type $<$SelectionSet extends SelectionSets.Query> = InferResult.Query<$Selection
// dprint-ignore
{

AssertEqual<PickSelectsPositiveIndicatorAndNotSelectAlias<{ a: true }>, 'a'>()
AssertEqual<PickSelectsPositiveIndicatorAndNotSelectAlias<{ a: ['b', true]; b: true }>, 'b'>()
assertEqual<PickSelectsPositiveIndicatorAndNotSelectAlias<{ a: true }>, 'a'>()
assertEqual<PickSelectsPositiveIndicatorAndNotSelectAlias<{ a: ['b', true]; b: true }>, 'b'>()

}

// dprint-ignore
{

AssertEqual<$<{ __typename: true }>, { __typename: 'Query' }>()
assertEqual<$<{ __typename: true }>, { __typename: 'Query' }>()

// Scalar
AssertEqual<$<{ id: true }>, { id: null | string }>()
assertEqual<$<{ id: true }>, { id: null | string }>()
// AssertEqual<RS<{ id: 1 }>, { id: null | string }>()
// non-nullable
AssertEqual<$<{ idNonNull: true }>, { idNonNull: string }>()
assertEqual<$<{ idNonNull: true }>, { idNonNull: string }>()
// indicator negative
AssertEqual<$<{ id: true; string: false }>, { id: null | string }>()
assertEqual<$<{ id: true; string: false }>, { id: null | string }>()
// AssertEqual<RS<{ id: true; string: 0 }>, { id: null | string }>()
AssertEqual<$<{ id: true; string: undefined }>, { id: null | string }>()
assertEqual<$<{ id: true; string: undefined }>, { id: null | string }>()

// Custom Scalar
AssertEqual<$<{ date: true }>, { date: null | Date }>()
assertEqual<$<{ date: true }>, { date: null | Date }>()

// List
AssertEqual<$<{ listIntNonNull: true }>, { listIntNonNull: number[] }>()
AssertEqual<$<{ listInt: true }>, { listInt: null|(null|number)[] }>()
AssertEqual<$<{ listListIntNonNull: true }>, { listListIntNonNull: number[][] }>()
AssertEqual<$<{ listListInt: true }>, { listListInt: null|((null|(null|number)[])[]) }>()
assertEqual<$<{ listIntNonNull: true }>, { listIntNonNull: number[] }>()
assertEqual<$<{ listInt: true }>, { listInt: null|(null|number)[] }>()
assertEqual<$<{ listListIntNonNull: true }>, { listListIntNonNull: number[][] }>()
assertEqual<$<{ listListInt: true }>, { listListInt: null|((null|(null|number)[])[]) }>()

// Enum
AssertEqual<$<{ abcEnum: true }>, { abcEnum: null|'A'|'B'|'C' }>()
assertEqual<$<{ abcEnum: true }>, { abcEnum: null|'A'|'B'|'C' }>()

// Object
AssertEqual<$<{ object: { id: true } }>, { object: null | { id: string | null } }>()
assertEqual<$<{ object: { id: true } }>, { object: null | { id: string | null } }>()
// non-nullable
AssertEqual<$<{ objectNonNull: { id: true } }>, { objectNonNull: { id: string | null } }>()
assertEqual<$<{ objectNonNull: { id: true } }>, { objectNonNull: { id: string | null } }>()
// with args
AssertEqual<$<{ objectWithArgs: { $: { id: 'abc' }; id: true }}>, { objectWithArgs: null | { id: string | null } }>()
assertEqual<$<{ objectWithArgs: { $: { id: 'abc' }; id: true }}>, { objectWithArgs: null | { id: string | null } }>()

// scalars-wildcard
AssertEqual<$<{ objectNonNull: { $scalars: true } }>, { objectNonNull: { __typename: "Object1"; string: null|string; int: null|number; float: null|number; boolean: null|boolean; id: null|string } }>()
assertEqual<$<{ objectNonNull: { $scalars: true } }>, { objectNonNull: { __typename: "Object1"; string: null|string; int: null|number; float: null|number; boolean: null|boolean; id: null|string } }>()
// scalars-wildcard with nested object
AssertEqual<$<{ objectNested: { $scalars: true } }>, { objectNested: null | { __typename: "ObjectNested"; id: null|string } }>()
assertEqual<$<{ objectNested: { $scalars: true } }>, { objectNested: null | { __typename: "ObjectNested"; id: null|string } }>()
// __typename
AssertEqual<$<{ objectNonNull: { __typename: true } }>, { objectNonNull: { __typename: "Object1" } }>()
assertEqual<$<{ objectNonNull: { __typename: true } }>, { objectNonNull: { __typename: "Object1" } }>()

// Union
AssertEqual<$<{ unionFooBar: { __typename: true } }>, { unionFooBar: null | { __typename: "Foo" } | { __typename: "Bar" } }>()
AssertEqual<$<{ unionFooBar: { ___on_Foo: { __typename: true } } }>, { unionFooBar: null | {} | { __typename: "Foo" } }>()
AssertEqual<$<{ unionFooBar: { ___on_Foo: { id: true } } }>, { unionFooBar: null | {} | { id: null|string } }>()
AssertEqual<$<{ unionFooBar: { __typename: true; ___on_Foo: { id: true } } }>, { unionFooBar: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>()
assertEqual<$<{ unionFooBar: { __typename: true } }>, { unionFooBar: null | { __typename: "Foo" } | { __typename: "Bar" } }>()
assertEqual<$<{ unionFooBar: { ___on_Foo: { __typename: true } } }>, { unionFooBar: null | {} | { __typename: "Foo" } }>()
assertEqual<$<{ unionFooBar: { ___on_Foo: { id: true } } }>, { unionFooBar: null | {} | { id: null|string } }>()
assertEqual<$<{ unionFooBar: { __typename: true; ___on_Foo: { id: true } } }>, { unionFooBar: null | { __typename: "Bar" } | { __typename: "Foo"; id: null|string } }>()
// with Args
AssertEqual<$<{ unionFooBarWithArgs: { $: { id: `abc` }, ___on_Foo: { id: true } } }>, { unionFooBarWithArgs: null | {} | { id: null|string } }>()
assertEqual<$<{ unionFooBarWithArgs: { $: { id: `abc` }, ___on_Foo: { id: true } } }>, { unionFooBarWithArgs: null | {} | { id: null|string } }>()


// Union fragments Case
AssertEqual<$<{ lowerCaseUnion: { __typename:true, ___on_lowerCaseObject: { id: true }, ___on_lowerCaseObject2: { int: true } } }>, { lowerCaseUnion: null | { __typename: 'lowerCaseObject'; id: null|string } | { __typename: 'lowerCaseObject2'; int: null|number } }>()
assertEqual<$<{ lowerCaseUnion: { __typename:true, ___on_lowerCaseObject: { id: true }, ___on_lowerCaseObject2: { int: true } } }>, { lowerCaseUnion: null | { __typename: 'lowerCaseObject'; id: null|string } | { __typename: 'lowerCaseObject2'; int: null|number } }>()


// Interface
AssertEqual<$<{ interface: { ___on_Object1ImplementingInterface: { id: true }}}>, { interface: null | { id: null | string} | {} }>()
AssertEqual<$<{ interface: { ___on_Object1ImplementingInterface: { int: true }}}>, { interface: null | { int: null | number} | {} }>()
AssertEqual<$<{ interface: { id: true }}>, { interface: null | { id: null | string} }>()
AssertEqual<$<{ interface: { id: true, ___on_Object1ImplementingInterface: { id: true } }}>, { interface: null | { id: null | string} }>()
AssertEqual<$<{ interface: { id: true, ___on_Object1ImplementingInterface: { int: true } }}>, { interface: null | { id: null | string} | { id: null | string; int: null | number }}>()
AssertEqual<$<{ interface: { __typename:true }}>, { interface: null | { __typename: 'Object1ImplementingInterface' } | { __typename: 'Object2ImplementingInterface' } }>()
AssertEqual<$<{ interface: { ___on_Object1ImplementingInterface: { __typename: true } }}>, { interface: null | { __typename: 'Object1ImplementingInterface' } | {} }>()
AssertEqual<$<{ interface: { $scalars: true }}>, { interface: null | { __typename: 'Object1ImplementingInterface', id: null | string, int: null|number} | { __typename: 'Object2ImplementingInterface', id: null | string; boolean:null|boolean} }>()
assertEqual<$<{ interface: { ___on_Object1ImplementingInterface: { id: true }}}>, { interface: null | { id: null | string} | {} }>()
assertEqual<$<{ interface: { ___on_Object1ImplementingInterface: { int: true }}}>, { interface: null | { int: null | number} | {} }>()
assertEqual<$<{ interface: { id: true }}>, { interface: null | { id: null | string} }>()
assertEqual<$<{ interface: { id: true, ___on_Object1ImplementingInterface: { id: true } }}>, { interface: null | { id: null | string} }>()
assertEqual<$<{ interface: { id: true, ___on_Object1ImplementingInterface: { int: true } }}>, { interface: null | { id: null | string} | { id: null | string; int: null | number }}>()
assertEqual<$<{ interface: { __typename:true }}>, { interface: null | { __typename: 'Object1ImplementingInterface' } | { __typename: 'Object2ImplementingInterface' } }>()
assertEqual<$<{ interface: { ___on_Object1ImplementingInterface: { __typename: true } }}>, { interface: null | { __typename: 'Object1ImplementingInterface' } | {} }>()
assertEqual<$<{ interface: { $scalars: true }}>, { interface: null | { __typename: 'Object1ImplementingInterface', id: null | string, int: null|number} | { __typename: 'Object2ImplementingInterface', id: null | string; boolean:null|boolean} }>()
// with args
AssertEqual<$<{ interfaceWithArgs: { $:{id:'abc'}; id: true }}>, { interfaceWithArgs: null | { id: null | string } }>()
assertEqual<$<{ interfaceWithArgs: { $:{id:'abc'}; id: true }}>, { interfaceWithArgs: null | { id: null | string } }>()

// todo alias on interfaces, interface fragments
// Alias
// scalar
AssertEqual<$<{ id: ['x', true] }>, { x: null | string }>()
AssertEqual<$<{ idNonNull: ['x', true] }>, { x: string }>()
assertEqual<$<{ id: ['x', true] }>, { x: null | string }>()
assertEqual<$<{ idNonNull: ['x', true] }>, { x: string }>()
// object
AssertEqual<$<{ object: ['x', { id: true }] }>, { x: { id: null|string } | null }>()
assertEqual<$<{ object: ['x', { id: true }] }>, { x: { id: null|string } | null }>()
// argument
AssertEqual<$<{objectWithArgs: ['x', { $: {id:''}; id:true }]}>, { x: { id: null|string } | null }>()
assertEqual<$<{objectWithArgs: ['x', { $: {id:''}; id:true }]}>, { x: { id: null|string } | null }>()
// multi
AssertEqual<$<{ id: [['id1', true],['id2', true]] }>, { id1: null | string; id2: null | string }>()
assertEqual<$<{ id: [['id1', true],['id2', true]] }>, { id1: null | string; id2: null | string }>()
// AssertEqual<RS<{ id_as: true }>, { id_as: InferResult.Errors.UnknownFieldName<'id_as', Schema.Root.Query> }>()
// AssertEqual<RS<{ id_as_$: true }>, { id_as_$: InferResult.Errors.UnknownFieldName<'id_as_$', Schema.Root.Query> }>()
// union fragment
AssertEqual<$<{ unionFooBar: { ___on_Foo: { id: ['id2', true] } } }>, { unionFooBar: null | {} | { id2: null|string } }>()
assertEqual<$<{ unionFooBar: { ___on_Foo: { id: ['id2', true] } } }>, { unionFooBar: null | {} | { id2: null|string } }>()

// Directive @include
// On scalar non-nullable
AssertEqual<$<{ idNonNull: { $include: boolean } }>, { idNonNull: null|string }>()
AssertEqual<$<{ idNonNull: { $include: {if:boolean} } }>, { idNonNull: null|string }>()
AssertEqual<$<{ idNonNull: { $include: true } }>, { idNonNull: string }>()
AssertEqual<$<{ idNonNull: { $include: {if:true} } }>, { idNonNull: string }>()
AssertEqual<$<{ idNonNull: { $include: false } }>, { idNonNull: null }>()
AssertEqual<$<{ idNonNull: { $include: {if:false} } }>, { idNonNull: null }>()
assertEqual<$<{ idNonNull: { $include: boolean } }>, { idNonNull: null|string }>()
assertEqual<$<{ idNonNull: { $include: {if:boolean} } }>, { idNonNull: null|string }>()
assertEqual<$<{ idNonNull: { $include: true } }>, { idNonNull: string }>()
assertEqual<$<{ idNonNull: { $include: {if:true} } }>, { idNonNull: string }>()
assertEqual<$<{ idNonNull: { $include: false } }>, { idNonNull: null }>()
assertEqual<$<{ idNonNull: { $include: {if:false} } }>, { idNonNull: null }>()
// On scalar nullable
AssertEqual<$<{ id: { $include: boolean } }>, { id: null|string }>()
AssertEqual<$<{ id: { $include: false } }>, { id: null }>()
AssertEqual<$<{ id: { $include: true } }>, { id: null|string }>()
assertEqual<$<{ id: { $include: boolean } }>, { id: null|string }>()
assertEqual<$<{ id: { $include: false } }>, { id: null }>()
assertEqual<$<{ id: { $include: true } }>, { id: null|string }>()

// Directive @skip
// On scalar non-nullable
AssertEqual<$<{ idNonNull: { $skip: boolean } }>, { idNonNull: null|string }>()
AssertEqual<$<{ idNonNull: { $skip: {if:boolean} } }>, { idNonNull: null|string }>()
AssertEqual<$<{ idNonNull: { $skip: true } }>, { idNonNull: null }>()
AssertEqual<$<{ idNonNull: { $skip: {if:true} } }>, { idNonNull: null }>()
AssertEqual<$<{ idNonNull: { $skip: false } }>, { idNonNull: string }>()
AssertEqual<$<{ idNonNull: { $skip: {if:false} } }>, { idNonNull: string }>()
assertEqual<$<{ idNonNull: { $skip: boolean } }>, { idNonNull: null|string }>()
assertEqual<$<{ idNonNull: { $skip: {if:boolean} } }>, { idNonNull: null|string }>()
assertEqual<$<{ idNonNull: { $skip: true } }>, { idNonNull: null }>()
assertEqual<$<{ idNonNull: { $skip: {if:true} } }>, { idNonNull: null }>()
assertEqual<$<{ idNonNull: { $skip: false } }>, { idNonNull: string }>()
assertEqual<$<{ idNonNull: { $skip: {if:false} } }>, { idNonNull: string }>()
// On scalar nullable
AssertEqual<$<{ id: { $skip: boolean } }>, { id: null|string }>()
AssertEqual<$<{ id: { $skip: false } }>, { id: null|string }>()
AssertEqual<$<{ id: { $skip: true } }>, { id: null }>()
assertEqual<$<{ id: { $skip: boolean } }>, { id: null|string }>()
assertEqual<$<{ id: { $skip: false } }>, { id: null|string }>()
assertEqual<$<{ id: { $skip: true } }>, { id: null }>()

// Directive @defer
// todo
Expand All @@ -134,13 +134,13 @@ AssertEqual<$<{ id: { $skip: true } }>, { id: null }>()

// Arguments
// scalar
AssertEqual<$<{ stringWithArgs: true }>, { stringWithArgs: null | string }>()
AssertEqual<$<{ stringWithArgs: { $: { string: '' } } }>, { stringWithArgs: null | string }>()
assertEqual<$<{ stringWithArgs: true }>, { stringWithArgs: null | string }>()
assertEqual<$<{ stringWithArgs: { $: { string: '' } } }>, { stringWithArgs: null | string }>()

// Errors
// @ts-expect-error invalid query
type Result = $<{ id2: true }>
// unknown field
AssertEqual<Result, { id2: InferResult.Errors.UnknownFieldName<'id2', Schema.Root.Query> }>()
assertEqual<Result, { id2: InferResult.Errors.UnknownFieldName<'id2', Schema.Root.Query> }>()

}
Loading

0 comments on commit 1c183af

Please sign in to comment.