Skip to content

Commit

Permalink
fix(document-builder): infer object inline fragments (#1267)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Nov 18, 2024
1 parent 6246294 commit 3535e9e
Show file tree
Hide file tree
Showing 43 changed files with 814 additions and 488 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
headers: Headers {
accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8',
'content-type': 'application/json',
'x-sent-at-time': '1731942324944'
'x-sent-at-time': '1731961845707'
},
method: 'post',
url: 'http://localhost:3000/graphql',
Expand Down
2 changes: 1 addition & 1 deletion examples/__outputs__/20_output/output_envelope.output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
headers: Headers {
'content-type': 'application/graphql-response+json; charset=utf-8',
'content-length': '142',
date: 'Mon, 18 Nov 2024 15:05:25 GMT',
date: 'Mon, 18 Nov 2024 20:30:46 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5'
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,47 +546,71 @@
name: 'attack',
description: null,
args: [],
type: { kind: 'SCALAR', name: 'Int', ofType: null },
type: {
kind: 'NON_NULL',
name: null,
ofType: { kind: 'SCALAR', name: 'Int', ofType: null }
},
isDeprecated: false,
deprecationReason: null
},
{
name: 'birthday',
description: null,
args: [],
type: { kind: 'SCALAR', name: 'Date', ofType: null },
type: {
kind: 'NON_NULL',
name: null,
ofType: { kind: 'SCALAR', name: 'Date', ofType: null }
},
isDeprecated: false,
deprecationReason: null
},
{
name: 'defense',
description: null,
args: [],
type: { kind: 'SCALAR', name: 'Int', ofType: null },
type: {
kind: 'NON_NULL',
name: null,
ofType: { kind: 'SCALAR', name: 'Int', ofType: null }
},
isDeprecated: false,
deprecationReason: null
},
{
name: 'hp',
description: null,
args: [],
type: { kind: 'SCALAR', name: 'Int', ofType: null },
type: {
kind: 'NON_NULL',
name: null,
ofType: { kind: 'SCALAR', name: 'Int', ofType: null }
},
isDeprecated: false,
deprecationReason: null
},
{
name: 'id',
description: null,
args: [],
type: { kind: 'SCALAR', name: 'ID', ofType: null },
type: {
kind: 'NON_NULL',
name: null,
ofType: { kind: 'SCALAR', name: 'ID', ofType: null }
},
isDeprecated: false,
deprecationReason: null
},
{
name: 'name',
description: null,
args: [],
type: { kind: 'SCALAR', name: 'String', ofType: null },
type: {
kind: 'NON_NULL',
name: null,
ofType: { kind: 'SCALAR', name: 'String', ofType: null }
},
isDeprecated: false,
deprecationReason: null
},
Expand All @@ -602,7 +626,11 @@
name: 'type',
description: null,
args: [],
type: { kind: 'ENUM', name: 'PokemonType', ofType: null },
type: {
kind: 'NON_NULL',
name: null,
ofType: { kind: 'ENUM', name: 'PokemonType', ofType: null }
},
isDeprecated: false,
deprecationReason: null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
parentId: '209715d4b5564e5f',
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
parentId: '832b1b8dea636439',
traceState: undefined,
name: 'encode',
id: '1c887e1c1ec9a2f0',
id: 'b4ac64f2faa2f565',
kind: 0,
timestamp: 1731942325967000,
duration: 1660.917,
timestamp: 1731961846500000,
duration: 1634.667,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -33,14 +33,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
parentId: '209715d4b5564e5f',
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
parentId: '832b1b8dea636439',
traceState: undefined,
name: 'pack',
id: 'a809cd5489d8667e',
id: '53e4263bd960a2e0',
kind: 0,
timestamp: 1731942325969000,
duration: 25164.041,
timestamp: 1731961846508000,
duration: 16836.25,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -57,14 +57,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
parentId: '209715d4b5564e5f',
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
parentId: '832b1b8dea636439',
traceState: undefined,
name: 'exchange',
id: 'b0a98d222477503a',
id: '49774b87c330f8a6',
kind: 0,
timestamp: 1731942325995000,
duration: 20493.166,
timestamp: 1731961846525000,
duration: 100583.459,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -81,14 +81,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
parentId: '209715d4b5564e5f',
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
parentId: '832b1b8dea636439',
traceState: undefined,
name: 'unpack',
id: '653173e30aed7697',
id: 'b2ab76711cac0cda',
kind: 0,
timestamp: 1731942326016000,
duration: 951.5,
timestamp: 1731961846626000,
duration: 949.5,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -105,14 +105,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
parentId: '209715d4b5564e5f',
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
parentId: '832b1b8dea636439',
traceState: undefined,
name: 'decode',
id: 'fb042b294d2cd179',
id: '02b77d59425b58b8',
kind: 0,
timestamp: 1731942326017000,
duration: 441.958,
timestamp: 1731961846627000,
duration: 445,
attributes: {},
status: { code: 0 },
events: [],
Expand All @@ -129,14 +129,14 @@
}
},
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
parentId: undefined,
traceState: undefined,
name: 'request',
id: '209715d4b5564e5f',
id: '832b1b8dea636439',
kind: 0,
timestamp: 1731942325966000,
duration: 51312.625,
timestamp: 1731961846499000,
duration: 128745.833,
attributes: {},
status: { code: 0 },
events: [],
Expand Down
23 changes: 7 additions & 16 deletions src/documentBuilder/InferResult/OutputField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import type { TSErrorDescriptive } from '../../lib/ts-error.js'
import type { Schema } from '../../types/Schema/__.js'
import type { InlineType } from '../../types/SchemaDrivenDataMap/InlineType.js'
import type { Select } from '../Select/__.js'
import type { FieldDirectiveInclude, FieldDirectiveSkip } from './directive.js'
import type { Interface } from './Interface.js'
import type { OutputObject } from './OutputObject.js'
import type { Union } from './Union.js'

// dprint-ignore
export type OutputField<$SelectionSet, $Field extends Schema.OutputField, $Schema extends Schema> =
$SelectionSet extends Select.Directive.Include.FieldStates.Negative | Select.Directive.Skip.FieldStates.Positive ?
null :
(
$SelectionSet extends Select.Directive.Include.FieldStates.Negative | Select.Directive.Skip.FieldStates.Positive
? null
: (
| FieldDirectiveInclude<$SelectionSet>
| FieldDirectiveSkip<$SelectionSet>
| SimplifyNullable<
Expand All @@ -28,7 +29,9 @@ type FieldType<
$SelectionSet,
$Node extends Schema.NamedOutputTypes,
> =
$Node extends Schema.OutputObject ? OutputObject<$SelectionSet, $Schema, $Node> :
$Node extends Schema.OutputObject ? $SelectionSet extends object
? OutputObject<$SelectionSet, $Schema, $Node>
: TSErrorDescriptive<'FieldType', 'When $Node extends Schema.OutputObject then $SelectionSet must extend object', { $Type: $Node; $SelectionSet: $SelectionSet; $Schema:$Schema } > :
$Node extends Schema.Scalar ? Schema.Scalar.GetDecoded<$Node> : // TODO use TS compiler API to extract this type at build time.
$Node extends Schema.Scalar.ScalarCodecless ? Schema.Scalar.GetDecoded<GetCodecForCodecless<$Schema, $Node>> :
$Node extends Schema.__typename ? $Node['value'] :
Expand All @@ -45,15 +48,3 @@ type GetCodecForCodecless<
$Node['name'] extends keyof $Schema['scalarRegistry']['map']
? $Schema['scalarRegistry']['map'][$Node['name']]
: Schema.Scalar.String

// dprint-ignore
type FieldDirectiveInclude<$SelectionSet> =
$SelectionSet extends Select.Directive.Include.Field ? $SelectionSet extends Select.Directive.Include.FieldStates.Positive ? never
: null
: never

// dprint-ignore
type FieldDirectiveSkip<$SelectionSet> =
$SelectionSet extends Select.Directive.Skip.Field ? $SelectionSet extends Select.Directive.Skip.FieldStates.Negative ? never
: null
: never
91 changes: 77 additions & 14 deletions src/documentBuilder/InferResult/OutputObject.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,85 @@
import type { SimplifyExcept, StringKeyof } from '../../lib/prelude.js'
import type { IsNever } from 'type-fest'
import { assertEqual } from '../../lib/assert-equal.js'
import type { AssertExtendsObject, GetOrNever, SimplifyExcept, StringKeyof } from '../../lib/prelude.js'
import type { TSErrorDescriptive } from '../../lib/ts-error.js'
import type { Schema } from '../../types/Schema/__.js'
import type { Select } from '../Select/__.js'
import type { Alias } from './Alias.js'
import type { IsNeverViaDirective, IsNullableViaDirective, OmitDirectiveAndArgumentKeys } from './directive.js'
import type { OutputField } from './OutputField.js'
import type { ScalarsWildcard } from './ScalarsWildcard.js'

// dprint-ignore
export type OutputObject<$SelectionSet, $Schema extends Schema, $Node extends Schema.OutputObject> =
export type OutputObject<
$SelectionSet extends object,
$Schema extends Schema,
$Node extends Schema.OutputObject
> =
SimplifyExcept<
$Schema['scalars']['typesDecoded'],
& OutputObject_<$SelectionSet, $Schema, $Node>
& InlineFragmentKeys<$SelectionSet, $Schema, $Node>
>

// dprint-ignore
type OutputObject_<
$SelectionSet extends object,
$Schema extends Schema,
$Node extends Schema.OutputObject,
> =
Select.SelectScalarsWildcard.IsSelectScalarsWildcard<$SelectionSet> extends true
// todo this needs to be an extension and/or only available when sddm is present
// todo what about when scalars wildcard is combined with other fields like relations?
? ScalarsWildcard<$SelectionSet, $Schema, $Node>
: SimplifyExcept<
$Schema['scalars']['typesDecoded'],
& NonAlias<$SelectionSet, $Schema, $Node>
:
& NonAliasKeys<$SelectionSet, $Schema, $Node>
& Alias<$Schema, $Node, $SelectionSet>
>

type NonAlias<$SelectionSet, $Schema extends Schema, $Node extends Schema.OutputObject> = {
[$Key in PickSelectsPositiveIndicatorAndNotSelectAlias<$SelectionSet>]: $Key extends keyof $Node['fields']
? OutputField<$SelectionSet[$Key], $Node['fields'][$Key], $Schema>
: Errors.UnknownFieldName<$Key, $Node>
// dprint-ignore
type NonAliasKeys<$SelectionSet, $Schema extends Schema, $Node extends Schema.OutputObject> = {
[$Key in PickPositiveIndicatorAndNotAlias<$SelectionSet>]:
$Key extends keyof $Node['fields']
? OutputField<$SelectionSet[$Key], $Node['fields'][$Key], $Schema>
: Errors.UnknownFieldName<$Key, $Node>
}

// dprint-ignore
export type PickSelectsPositiveIndicatorAndNotSelectAlias<$SelectionSet> = StringKeyof<
type InlineFragmentKeys<$SelectionSet extends object, $Schema extends Schema, $Node extends Schema.OutputObject> =
InlineFragmentKey_<
AssertExtendsObject<
GetOrNever<$SelectionSet, Select.InlineFragment.Key>
>,
$Schema,
$Node
>

// dprint-ignore
type InlineFragmentKey_<$SelectionSet extends object, $Schema extends Schema, $Node extends Schema.OutputObject> =
IsNever<$SelectionSet> extends true
? {}
: IsNeverViaDirective<$SelectionSet> extends true
? {}
: IsNullableViaDirective<$SelectionSet> extends true
? MakeObjectSelectionResultNullable<
OutputObject_<OmitDirectiveAndArgumentKeys<$SelectionSet>, $Schema, $Node>
>
: OutputObject_<OmitDirectiveAndArgumentKeys<$SelectionSet>, $Schema, $Node>

type MakeObjectSelectionResultNullable<$Result extends object> = {
[_ in keyof $Result]: null | $Result[_]
}

// dprint-ignore
type PickPositiveIndicatorAndNotAlias<$SelectionSet> = StringKeyof<
{
[
$FieldName in keyof $SelectionSet as $SelectionSet[$FieldName] extends Select.Indicator.Negative
$Key in keyof $SelectionSet as $SelectionSet[$Key] extends Select.Indicator.Negative
? never
: $SelectionSet[$FieldName] extends any[]
: $SelectionSet[$Key] extends any[]
? never
: $FieldName
: $Key extends Select.InlineFragment.Key
? never
: $Key
]: 0
}
>
Expand All @@ -42,3 +90,18 @@ export namespace Errors {
$Object extends Schema.OutputObject,
> = TSErrorDescriptive<'Object', `field "${$FieldName}" does not exist on object "${$Object['name']}"`>
}

//
//
//
// Internal Tests
//
//
//
// dprint-ignore
{

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

}
Loading

0 comments on commit 3535e9e

Please sign in to comment.