Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(document-builder): infer object inline fragments #1267

Merged
merged 5 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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