Skip to content

Commit 5f5549b

Browse files
authored
fix(document): types when multiple roots (#1128)
1 parent 9e362c4 commit 5f5549b

File tree

22 files changed

+398
-218
lines changed

22 files changed

+398
-218
lines changed
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* This example shows how to use the TypeScript interface for GraphQL arguments.
2+
* This example shows how to use the TypeScript interface for whole GraphQL documents.
33
*/
44

55
import { Pokemon } from '../$/generated-clients/pokemon/__.js'
@@ -8,15 +8,37 @@ import { showJson } from '../$/helpers.js'
88
const pokemon = Pokemon.create()
99

1010
const pokemons = await pokemon.document({
11-
query: {
12-
pokemonsQuery: {
13-
pokemons: [`pokemons2`, {
14-
$: { filter: { name: { in: [`Pikachu`, `Charizard`] } } },
11+
query: { // A root type.
12+
pokemonsAndTrainers: { // A name of an the operation.
13+
trainers: { // A root field.
14+
name: true, // A field.
15+
},
16+
pokemons: {
17+
$: { // A field's arguments
18+
filter: { name: { in: [`Pikachu`, `Charizard`] } },
19+
},
1520
name: true,
16-
trainer: { name: true },
17-
}],
21+
trainer: {
22+
name: true,
23+
},
24+
},
25+
},
26+
},
27+
mutation: {
28+
makeSomeNewPokemons: {
29+
addPokemon: [
30+
['addAngryPikachu', {
31+
$: { name: `AngryPikachu`, attack: 100, defense: 100, hp: 100 },
32+
name: true,
33+
}],
34+
['addAngryCharizard', {
35+
$: { name: `AngryCharizard`, attack: 100, defense: 100, hp: 100 },
36+
name: true,
37+
}],
38+
],
1839
},
1940
},
2041
})
42+
.run('pokemonsAndTrainers')
2143

2244
showJson(pokemons)

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': '1727291929241'
7+
'x-sent-at-time': '1727310087052'
88
},
99
signal: undefined,
1010
method: 'post',

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': '104',
19-
date: 'Wed, 25 Sep 2024 19:18:49 GMT',
19+
date: 'Thu, 26 Sep 2024 00:21:27 GMT',
2020
connection: 'keep-alive',
2121
'keep-alive': 'timeout=5'
2222
},
Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,25 @@
11
---------------------------------------- SHOW ----------------------------------------
2-
{}
2+
{
3+
"trainers": [
4+
{
5+
"name": "Ash"
6+
},
7+
{
8+
"name": "Misty"
9+
}
10+
],
11+
"pokemons": [
12+
{
13+
"name": "Pikachu",
14+
"trainer": {
15+
"name": "Ash"
16+
}
17+
},
18+
{
19+
"name": "Charizard",
20+
"trainer": {
21+
"name": "Ash"
22+
}
23+
}
24+
]
25+
}

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: '9d154073558dfe77a07840d133b4358c',
13-
parentId: '57916921f30d3744',
12+
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
13+
parentId: '78959de15b116846',
1414
traceState: undefined,
1515
name: 'encode',
16-
id: 'dcdd7c9ea7a24491',
16+
id: 'e78df0ee2a54acb6',
1717
kind: 0,
18-
timestamp: 1727291930021000,
19-
duration: 1392.459,
18+
timestamp: 1727310087760000,
19+
duration: 554.291,
2020
attributes: {},
2121
status: { code: 0 },
2222
events: [],
@@ -33,14 +33,14 @@
3333
}
3434
},
3535
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
36-
traceId: '9d154073558dfe77a07840d133b4358c',
37-
parentId: '57916921f30d3744',
36+
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
37+
parentId: '78959de15b116846',
3838
traceState: undefined,
3939
name: 'pack',
40-
id: 'd1ca1155f050f30c',
40+
id: '88b5c48ab9be7a36',
4141
kind: 0,
42-
timestamp: 1727291930024000,
43-
duration: 16745.417,
42+
timestamp: 1727310087845000,
43+
duration: 17482.625,
4444
attributes: {},
4545
status: { code: 0 },
4646
events: [],
@@ -57,14 +57,14 @@
5757
}
5858
},
5959
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
60-
traceId: '9d154073558dfe77a07840d133b4358c',
61-
parentId: '57916921f30d3744',
60+
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
61+
parentId: '78959de15b116846',
6262
traceState: undefined,
6363
name: 'exchange',
64-
id: 'c9978fd02d8f3228',
64+
id: 'd4a611cb0daf3ac1',
6565
kind: 0,
66-
timestamp: 1727291930042000,
67-
duration: 24516.084,
66+
timestamp: 1727310087867000,
67+
duration: 48095.333,
6868
attributes: {},
6969
status: { code: 0 },
7070
events: [],
@@ -81,14 +81,14 @@
8181
}
8282
},
8383
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
84-
traceId: '9d154073558dfe77a07840d133b4358c',
85-
parentId: '57916921f30d3744',
84+
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
85+
parentId: '78959de15b116846',
8686
traceState: undefined,
8787
name: 'unpack',
88-
id: '663a79043ef1feea',
88+
id: '20d213f3a3f86f1d',
8989
kind: 0,
90-
timestamp: 1727291930067000,
91-
duration: 1665.125,
90+
timestamp: 1727310087916000,
91+
duration: 1125.833,
9292
attributes: {},
9393
status: { code: 0 },
9494
events: [],
@@ -105,14 +105,14 @@
105105
}
106106
},
107107
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
108-
traceId: '9d154073558dfe77a07840d133b4358c',
109-
parentId: '57916921f30d3744',
108+
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
109+
parentId: '78959de15b116846',
110110
traceState: undefined,
111111
name: 'decode',
112-
id: '9fd8d38e892a7a50',
112+
id: 'c35ae080bfb451be',
113113
kind: 0,
114-
timestamp: 1727291930068000,
115-
duration: 184.333,
114+
timestamp: 1727310087917000,
115+
duration: 181.958,
116116
attributes: {},
117117
status: { code: 0 },
118118
events: [],
@@ -129,14 +129,14 @@
129129
}
130130
},
131131
instrumentationScope: { name: 'graffle', version: undefined, schemaUrl: undefined },
132-
traceId: '9d154073558dfe77a07840d133b4358c',
132+
traceId: 'f384fda0c6aa8cdc0600586696d83aac',
133133
parentId: undefined,
134134
traceState: undefined,
135135
name: 'request',
136-
id: '57916921f30d3744',
136+
id: '78959de15b116846',
137137
kind: 0,
138-
timestamp: 1727291930020000,
139-
duration: 48270.291,
138+
timestamp: 1727310087759000,
139+
duration: 158234.5,
140140
attributes: {},
141141
status: { code: 0 },
142142
events: [],

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
"test:examples": "vitest --config vitest.examples.config.ts --dir tests/examples",
9292
"test": "vitest",
9393
"test:web": "vitest --environment jsdom",
94-
"test:types": "vitest --typecheck src/**/**/*.test-d.ts",
94+
"test:types": "vitest --typecheck --dir src --testNamePattern .*.test-d.ts",
9595
"test:coverage": "pnpm test -- --coverage",
9696
"release:stable": "dripip stable",
9797
"release:preview": "dripip preview",
Lines changed: 48 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, expectTypeOf, test } from 'vitest'
1+
import { expectTypeOf, test } from 'vitest'
22
import { Graffle } from '../../../../tests/_/schema/generated/__.js'
33
import * as Schema from '../../../../tests/_/schema/schema.js'
44
import { MutationOnly } from '../../../../tests/_/schemaMutationOnly/generated/__.js'
@@ -16,65 +16,57 @@ test(`requires input`, () => {
1616
// graffle.document({})
1717
})
1818

19-
describe(`input`, () => {
20-
test(`document with one query`, () => {
21-
const run = graffle.document({ query: { foo: { id: true } } }).run
22-
expectTypeOf(run).toMatchTypeOf<(...params: ['foo'] | [] | [undefined]) => Promise<any>>()
23-
})
19+
test(`document with one query`, async () => {
20+
const run = graffle.document({ query: { foo: { id: true } } }).run
21+
type $Parameters = Parameters<typeof run>
22+
expectTypeOf<$Parameters>().toEqualTypeOf<[]>()
23+
const result = await run()
24+
expectTypeOf(result).toEqualTypeOf<{ id: string | null }>()
25+
})
2426

25-
test(`document with two queries`, () => {
26-
const run = graffle.document({
27-
query: {
28-
foo: { id: true },
29-
bar: { id: true },
30-
},
31-
}).run
32-
expectTypeOf(run).toMatchTypeOf<(name: 'foo' | 'bar') => Promise<any>>()
33-
})
27+
test(`document with two queries`, async () => {
28+
const run = graffle.document({
29+
query: {
30+
foo: { id: true },
31+
bar: { date: true },
32+
},
33+
}).run
34+
type $Parameters = Parameters<typeof run>
35+
expectTypeOf<$Parameters>().toEqualTypeOf<['foo' | 'bar']>()
36+
const result = await run(`foo`)
37+
expectTypeOf(result).toEqualTypeOf<{ id: string | null }>()
38+
})
3439

35-
test(`root operation not available if it is not in schema`, () => {
36-
const queryOnly = QueryOnly.create({
37-
schema: SchemaQueryOnly.schema,
38-
})
39-
queryOnly.document({
40-
query: { foo: { id: true } },
41-
// todo
42-
// // @ts-expect-error mutation not in schema
43-
// mutation: { foo: { id: true } },
44-
})
45-
const mutationOnly = MutationOnly.create({
46-
schema: SchemaMutationOnly.schema,
47-
})
48-
mutationOnly.document({
49-
// @ts-expect-error query not in schema
50-
foo: { query: { id: true } },
51-
bar: { mutation: { id: true } },
52-
})
53-
})
40+
test(`document with two queries of different root types`, async () => {
41+
const run = graffle.document({
42+
query: {
43+
foo: { id: true },
44+
},
45+
mutation: {
46+
bar: { idNonNull: true },
47+
},
48+
}).run
49+
type $Parameters = Parameters<typeof run>
50+
expectTypeOf<$Parameters>().toEqualTypeOf<['foo' | 'bar']>()
51+
const result = await run(`foo`)
52+
expectTypeOf(result).toEqualTypeOf<{ id: string | null }>()
5453
})
5554

56-
describe(`document(...).run()`, () => {
57-
test(`document with one query`, () => {
58-
{
59-
const result = graffle.document({ query: { x: { id: true } } }).run()
60-
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
61-
}
62-
{
63-
const result = graffle.document({ query: { x: { id: true } } }).run(`x`)
64-
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
65-
}
66-
{
67-
const result = graffle.document({ query: { x: { id: true } } }).run(undefined)
68-
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
69-
}
55+
test(`root operation not available if it is not in schema`, () => {
56+
const queryOnly = QueryOnly.create({
57+
schema: SchemaQueryOnly.schema,
58+
})
59+
queryOnly.document({
60+
query: { foo: { id: true } },
61+
// @ts-expect-error mutation not in schema
62+
mutation: { foo: { id: true } },
63+
})
64+
const mutationOnly = MutationOnly.create({
65+
schema: SchemaMutationOnly.schema,
7066
})
71-
test(`document with two queries`, () => {
72-
const result = graffle.document({
73-
query: {
74-
foo: { id: true },
75-
bar: { id: true },
76-
},
77-
}).run(`foo`)
78-
expectTypeOf(result).resolves.toEqualTypeOf<{ id: string | null }>()
67+
mutationOnly.document({
68+
mutation: { bar: { id: true } },
69+
// @ts-expect-error query not in schema
70+
query: { foo: { id: true } },
7971
})
8072
})

src/layers/6_client/requestMethods/document.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,12 @@ describe(`document with two queries`, () => {
7373

7474
test(`document with one query`, async () => {
7575
const { run } = graffle.document({ query: { foo: { id: true } } })
76-
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
7776
await expect(run()).resolves.toEqual({ id: db.id1 })
78-
await expect(run(undefined)).resolves.toEqual({ id: db.id1 })
7977
})
8078

8179
test(`document with one mutation`, async () => {
8280
const { run } = graffle.document({ mutation: { foo: { id: true } } })
83-
await expect(run(`foo`)).resolves.toEqual({ id: db.id1 })
8481
await expect(run()).resolves.toEqual({ id: db.id1 })
85-
await expect(run(undefined)).resolves.toEqual({ id: db.id1 })
8682
})
8783

8884
test(`error`, async () => {

src/layers/6_client/requestMethods/document.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,21 @@ export type DocumentRunner<
1414
$$Name extends Document.GetOperationNames<$$Document> = Document.GetOperationNames<$$Document>
1515
> = {
1616
run: <
17-
$Name extends $$Name,
18-
$Params extends (IsTupleMultiple<UnionToTuple<$$Name>> extends true ? [name: $Name] : ([] | [name: $Name | undefined])),
17+
$Params extends (IsTupleMultiple<UnionToTuple<$$Name>> extends true ? [name: $$Name] : []),
18+
const $Name extends string = $Params extends [] ? $$Name : $Params[0],
1919
>(...params: $Params) =>
20+
// [$Name]
21+
// ResultSet.Root<
22+
// AddTypenameToSelectedRootTypeResultFields<
23+
// $$Config,
24+
// $$Index,
25+
// Document.GetRootTypeNameOfOperation<$$Document, $Name>,
26+
// Document.GetOperation<$$Document, $Name>
27+
// >,
28+
// $$Index,
29+
// Document.GetRootTypeNameOfOperation<$$Document, $Name>
30+
// >
31+
//
2032
Promise<
2133
ResolveOutputReturnRootType<
2234
$$Config,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<p class="ExampleLinks">Examples <span class="ExampleLinksTitleSeparator">-></span> <a href="../../examples/raw/raw-document-node">Raw Document Node</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-document-node-typed">Raw Document Node Typed</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string">Raw String</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string-typed">Raw String Typed</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/transport-http/headers">Headers</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/transport-http/raw">Raw</a></p>
1+
<p class="ExampleLinks">Examples <span class="ExampleLinksTitleSeparator">-></span> <a href="../../examples/transport-http/headers">Headers</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/transport-http/raw">Raw</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-document-node">Raw Document Node</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-document-node-typed">Raw Document Node Typed</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string">Raw String</a> <span class="ExampleLinksSeparator"></span> <a href="../../examples/raw/raw-string-typed">Raw String Typed</a></p>

0 commit comments

Comments
 (0)