Skip to content

Commit 3535e9e

Browse files
authored
fix(document-builder): infer object inline fragments (#1267)
1 parent 6246294 commit 3535e9e

File tree

43 files changed

+814
-488
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+814
-488
lines changed

examples/__outputs__/10_transport-http/transport-http_extension_headers__dynamicHeaders.output.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
headers: Headers {
55
accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8',
66
'content-type': 'application/json',
7-
'x-sent-at-time': '1731942324944'
7+
'x-sent-at-time': '1731961845707'
88
},
99
method: 'post',
1010
url: 'http://localhost:3000/graphql',

examples/__outputs__/20_output/output_envelope.output.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
headers: Headers {
1717
'content-type': 'application/graphql-response+json; charset=utf-8',
1818
'content-length': '142',
19-
date: 'Mon, 18 Nov 2024 15:05:25 GMT',
19+
date: 'Mon, 18 Nov 2024 20:30:46 GMT',
2020
connection: 'keep-alive',
2121
'keep-alive': 'timeout=5'
2222
},

examples/__outputs__/60_extension/extension_introspection__introspection.output.txt

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -546,47 +546,71 @@
546546
name: 'attack',
547547
description: null,
548548
args: [],
549-
type: { kind: 'SCALAR', name: 'Int', ofType: null },
549+
type: {
550+
kind: 'NON_NULL',
551+
name: null,
552+
ofType: { kind: 'SCALAR', name: 'Int', ofType: null }
553+
},
550554
isDeprecated: false,
551555
deprecationReason: null
552556
},
553557
{
554558
name: 'birthday',
555559
description: null,
556560
args: [],
557-
type: { kind: 'SCALAR', name: 'Date', ofType: null },
561+
type: {
562+
kind: 'NON_NULL',
563+
name: null,
564+
ofType: { kind: 'SCALAR', name: 'Date', ofType: null }
565+
},
558566
isDeprecated: false,
559567
deprecationReason: null
560568
},
561569
{
562570
name: 'defense',
563571
description: null,
564572
args: [],
565-
type: { kind: 'SCALAR', name: 'Int', ofType: null },
573+
type: {
574+
kind: 'NON_NULL',
575+
name: null,
576+
ofType: { kind: 'SCALAR', name: 'Int', ofType: null }
577+
},
566578
isDeprecated: false,
567579
deprecationReason: null
568580
},
569581
{
570582
name: 'hp',
571583
description: null,
572584
args: [],
573-
type: { kind: 'SCALAR', name: 'Int', ofType: null },
585+
type: {
586+
kind: 'NON_NULL',
587+
name: null,
588+
ofType: { kind: 'SCALAR', name: 'Int', ofType: null }
589+
},
574590
isDeprecated: false,
575591
deprecationReason: null
576592
},
577593
{
578594
name: 'id',
579595
description: null,
580596
args: [],
581-
type: { kind: 'SCALAR', name: 'ID', ofType: null },
597+
type: {
598+
kind: 'NON_NULL',
599+
name: null,
600+
ofType: { kind: 'SCALAR', name: 'ID', ofType: null }
601+
},
582602
isDeprecated: false,
583603
deprecationReason: null
584604
},
585605
{
586606
name: 'name',
587607
description: null,
588608
args: [],
589-
type: { kind: 'SCALAR', name: 'String', ofType: null },
609+
type: {
610+
kind: 'NON_NULL',
611+
name: null,
612+
ofType: { kind: 'SCALAR', name: 'String', ofType: null }
613+
},
590614
isDeprecated: false,
591615
deprecationReason: null
592616
},
@@ -602,7 +626,11 @@
602626
name: 'type',
603627
description: null,
604628
args: [],
605-
type: { kind: 'ENUM', name: 'PokemonType', ofType: null },
629+
type: {
630+
kind: 'NON_NULL',
631+
name: null,
632+
ofType: { kind: 'ENUM', name: 'PokemonType', ofType: null }
633+
},
606634
isDeprecated: false,
607635
deprecationReason: null
608636
}

examples/__outputs__/60_extension/extension_opentelemetry__opentelemetry.output.txt

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
}
1010
},
1111
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
12-
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
13-
parentId: '209715d4b5564e5f',
12+
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
13+
parentId: '832b1b8dea636439',
1414
traceState: undefined,
1515
name: 'encode',
16-
id: '1c887e1c1ec9a2f0',
16+
id: 'b4ac64f2faa2f565',
1717
kind: 0,
18-
timestamp: 1731942325967000,
19-
duration: 1660.917,
18+
timestamp: 1731961846500000,
19+
duration: 1634.667,
2020
attributes: {},
2121
status: { code: 0 },
2222
events: [],
@@ -33,14 +33,14 @@
3333
}
3434
},
3535
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
36-
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
37-
parentId: '209715d4b5564e5f',
36+
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
37+
parentId: '832b1b8dea636439',
3838
traceState: undefined,
3939
name: 'pack',
40-
id: 'a809cd5489d8667e',
40+
id: '53e4263bd960a2e0',
4141
kind: 0,
42-
timestamp: 1731942325969000,
43-
duration: 25164.041,
42+
timestamp: 1731961846508000,
43+
duration: 16836.25,
4444
attributes: {},
4545
status: { code: 0 },
4646
events: [],
@@ -57,14 +57,14 @@
5757
}
5858
},
5959
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
60-
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
61-
parentId: '209715d4b5564e5f',
60+
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
61+
parentId: '832b1b8dea636439',
6262
traceState: undefined,
6363
name: 'exchange',
64-
id: 'b0a98d222477503a',
64+
id: '49774b87c330f8a6',
6565
kind: 0,
66-
timestamp: 1731942325995000,
67-
duration: 20493.166,
66+
timestamp: 1731961846525000,
67+
duration: 100583.459,
6868
attributes: {},
6969
status: { code: 0 },
7070
events: [],
@@ -81,14 +81,14 @@
8181
}
8282
},
8383
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
84-
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
85-
parentId: '209715d4b5564e5f',
84+
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
85+
parentId: '832b1b8dea636439',
8686
traceState: undefined,
8787
name: 'unpack',
88-
id: '653173e30aed7697',
88+
id: 'b2ab76711cac0cda',
8989
kind: 0,
90-
timestamp: 1731942326016000,
91-
duration: 951.5,
90+
timestamp: 1731961846626000,
91+
duration: 949.5,
9292
attributes: {},
9393
status: { code: 0 },
9494
events: [],
@@ -105,14 +105,14 @@
105105
}
106106
},
107107
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
108-
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
109-
parentId: '209715d4b5564e5f',
108+
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
109+
parentId: '832b1b8dea636439',
110110
traceState: undefined,
111111
name: 'decode',
112-
id: 'fb042b294d2cd179',
112+
id: '02b77d59425b58b8',
113113
kind: 0,
114-
timestamp: 1731942326017000,
115-
duration: 441.958,
114+
timestamp: 1731961846627000,
115+
duration: 445,
116116
attributes: {},
117117
status: { code: 0 },
118118
events: [],
@@ -129,14 +129,14 @@
129129
}
130130
},
131131
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
132-
traceId: 'd1df7288d6e61f6468a98edf7a68ef88',
132+
traceId: 'b3ebc1b598698f83310bd235fcb07eab',
133133
parentId: undefined,
134134
traceState: undefined,
135135
name: 'request',
136-
id: '209715d4b5564e5f',
136+
id: '832b1b8dea636439',
137137
kind: 0,
138-
timestamp: 1731942325966000,
139-
duration: 51312.625,
138+
timestamp: 1731961846499000,
139+
duration: 128745.833,
140140
attributes: {},
141141
status: { code: 0 },
142142
events: [],

src/documentBuilder/InferResult/OutputField.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import type { TSErrorDescriptive } from '../../lib/ts-error.js'
33
import type { Schema } from '../../types/Schema/__.js'
44
import type { InlineType } from '../../types/SchemaDrivenDataMap/InlineType.js'
55
import type { Select } from '../Select/__.js'
6+
import type { FieldDirectiveInclude, FieldDirectiveSkip } from './directive.js'
67
import type { Interface } from './Interface.js'
78
import type { OutputObject } from './OutputObject.js'
89
import type { Union } from './Union.js'
910

1011
// dprint-ignore
1112
export type OutputField<$SelectionSet, $Field extends Schema.OutputField, $Schema extends Schema> =
12-
$SelectionSet extends Select.Directive.Include.FieldStates.Negative | Select.Directive.Skip.FieldStates.Positive ?
13-
null :
14-
(
13+
$SelectionSet extends Select.Directive.Include.FieldStates.Negative | Select.Directive.Skip.FieldStates.Positive
14+
? null
15+
: (
1516
| FieldDirectiveInclude<$SelectionSet>
1617
| FieldDirectiveSkip<$SelectionSet>
1718
| SimplifyNullable<
@@ -28,7 +29,9 @@ type FieldType<
2829
$SelectionSet,
2930
$Node extends Schema.NamedOutputTypes,
3031
> =
31-
$Node extends Schema.OutputObject ? OutputObject<$SelectionSet, $Schema, $Node> :
32+
$Node extends Schema.OutputObject ? $SelectionSet extends object
33+
? OutputObject<$SelectionSet, $Schema, $Node>
34+
: TSErrorDescriptive<'FieldType', 'When $Node extends Schema.OutputObject then $SelectionSet must extend object', { $Type: $Node; $SelectionSet: $SelectionSet; $Schema:$Schema } > :
3235
$Node extends Schema.Scalar ? Schema.Scalar.GetDecoded<$Node> : // TODO use TS compiler API to extract this type at build time.
3336
$Node extends Schema.Scalar.ScalarCodecless ? Schema.Scalar.GetDecoded<GetCodecForCodecless<$Schema, $Node>> :
3437
$Node extends Schema.__typename ? $Node['value'] :
@@ -45,15 +48,3 @@ type GetCodecForCodecless<
4548
$Node['name'] extends keyof $Schema['scalarRegistry']['map']
4649
? $Schema['scalarRegistry']['map'][$Node['name']]
4750
: Schema.Scalar.String
48-
49-
// dprint-ignore
50-
type FieldDirectiveInclude<$SelectionSet> =
51-
$SelectionSet extends Select.Directive.Include.Field ? $SelectionSet extends Select.Directive.Include.FieldStates.Positive ? never
52-
: null
53-
: never
54-
55-
// dprint-ignore
56-
type FieldDirectiveSkip<$SelectionSet> =
57-
$SelectionSet extends Select.Directive.Skip.Field ? $SelectionSet extends Select.Directive.Skip.FieldStates.Negative ? never
58-
: null
59-
: never
Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,85 @@
1-
import type { SimplifyExcept, StringKeyof } from '../../lib/prelude.js'
1+
import type { IsNever } from 'type-fest'
2+
import { assertEqual } from '../../lib/assert-equal.js'
3+
import type { AssertExtendsObject, GetOrNever, SimplifyExcept, StringKeyof } from '../../lib/prelude.js'
24
import type { TSErrorDescriptive } from '../../lib/ts-error.js'
35
import type { Schema } from '../../types/Schema/__.js'
46
import type { Select } from '../Select/__.js'
57
import type { Alias } from './Alias.js'
8+
import type { IsNeverViaDirective, IsNullableViaDirective, OmitDirectiveAndArgumentKeys } from './directive.js'
69
import type { OutputField } from './OutputField.js'
710
import type { ScalarsWildcard } from './ScalarsWildcard.js'
811

912
// dprint-ignore
10-
export type OutputObject<$SelectionSet, $Schema extends Schema, $Node extends Schema.OutputObject> =
13+
export type OutputObject<
14+
$SelectionSet extends object,
15+
$Schema extends Schema,
16+
$Node extends Schema.OutputObject
17+
> =
18+
SimplifyExcept<
19+
$Schema['scalars']['typesDecoded'],
20+
& OutputObject_<$SelectionSet, $Schema, $Node>
21+
& InlineFragmentKeys<$SelectionSet, $Schema, $Node>
22+
>
23+
24+
// dprint-ignore
25+
type OutputObject_<
26+
$SelectionSet extends object,
27+
$Schema extends Schema,
28+
$Node extends Schema.OutputObject,
29+
> =
1130
Select.SelectScalarsWildcard.IsSelectScalarsWildcard<$SelectionSet> extends true
31+
// todo this needs to be an extension and/or only available when sddm is present
1232
// todo what about when scalars wildcard is combined with other fields like relations?
1333
? ScalarsWildcard<$SelectionSet, $Schema, $Node>
14-
: SimplifyExcept<
15-
$Schema['scalars']['typesDecoded'],
16-
& NonAlias<$SelectionSet, $Schema, $Node>
34+
:
35+
& NonAliasKeys<$SelectionSet, $Schema, $Node>
1736
& Alias<$Schema, $Node, $SelectionSet>
18-
>
1937

20-
type NonAlias<$SelectionSet, $Schema extends Schema, $Node extends Schema.OutputObject> = {
21-
[$Key in PickSelectsPositiveIndicatorAndNotSelectAlias<$SelectionSet>]: $Key extends keyof $Node['fields']
22-
? OutputField<$SelectionSet[$Key], $Node['fields'][$Key], $Schema>
23-
: Errors.UnknownFieldName<$Key, $Node>
38+
// dprint-ignore
39+
type NonAliasKeys<$SelectionSet, $Schema extends Schema, $Node extends Schema.OutputObject> = {
40+
[$Key in PickPositiveIndicatorAndNotAlias<$SelectionSet>]:
41+
$Key extends keyof $Node['fields']
42+
? OutputField<$SelectionSet[$Key], $Node['fields'][$Key], $Schema>
43+
: Errors.UnknownFieldName<$Key, $Node>
2444
}
2545

2646
// dprint-ignore
27-
export type PickSelectsPositiveIndicatorAndNotSelectAlias<$SelectionSet> = StringKeyof<
47+
type InlineFragmentKeys<$SelectionSet extends object, $Schema extends Schema, $Node extends Schema.OutputObject> =
48+
InlineFragmentKey_<
49+
AssertExtendsObject<
50+
GetOrNever<$SelectionSet, Select.InlineFragment.Key>
51+
>,
52+
$Schema,
53+
$Node
54+
>
55+
56+
// dprint-ignore
57+
type InlineFragmentKey_<$SelectionSet extends object, $Schema extends Schema, $Node extends Schema.OutputObject> =
58+
IsNever<$SelectionSet> extends true
59+
? {}
60+
: IsNeverViaDirective<$SelectionSet> extends true
61+
? {}
62+
: IsNullableViaDirective<$SelectionSet> extends true
63+
? MakeObjectSelectionResultNullable<
64+
OutputObject_<OmitDirectiveAndArgumentKeys<$SelectionSet>, $Schema, $Node>
65+
>
66+
: OutputObject_<OmitDirectiveAndArgumentKeys<$SelectionSet>, $Schema, $Node>
67+
68+
type MakeObjectSelectionResultNullable<$Result extends object> = {
69+
[_ in keyof $Result]: null | $Result[_]
70+
}
71+
72+
// dprint-ignore
73+
type PickPositiveIndicatorAndNotAlias<$SelectionSet> = StringKeyof<
2874
{
2975
[
30-
$FieldName in keyof $SelectionSet as $SelectionSet[$FieldName] extends Select.Indicator.Negative
76+
$Key in keyof $SelectionSet as $SelectionSet[$Key] extends Select.Indicator.Negative
3177
? never
32-
: $SelectionSet[$FieldName] extends any[]
78+
: $SelectionSet[$Key] extends any[]
3379
? never
34-
: $FieldName
80+
: $Key extends Select.InlineFragment.Key
81+
? never
82+
: $Key
3583
]: 0
3684
}
3785
>
@@ -42,3 +90,18 @@ export namespace Errors {
4290
$Object extends Schema.OutputObject,
4391
> = TSErrorDescriptive<'Object', `field "${$FieldName}" does not exist on object "${$Object['name']}"`>
4492
}
93+
94+
//
95+
//
96+
//
97+
// Internal Tests
98+
//
99+
//
100+
//
101+
// dprint-ignore
102+
{
103+
104+
assertEqual<PickPositiveIndicatorAndNotAlias<{ a: true }> , 'a'>()
105+
assertEqual<PickPositiveIndicatorAndNotAlias<{ a: ['b', true]; b: true }> , 'b'>()
106+
107+
}

0 commit comments

Comments
 (0)