Skip to content

Commit 9be0f08

Browse files
authored
feat(input): allow request configuration (#1059)
1 parent 5d04f27 commit 9be0f08

File tree

35 files changed

+534
-380
lines changed

35 files changed

+534
-380
lines changed

examples/$generated-clients/SocialStudies/Global.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import type { Index } from './Index.js'
22

33
declare global {
4-
export namespace GraphQLRequestTypes {
4+
export namespace GraffleGlobalTypes {
55
export interface Schemas {
66
SocialStudies: {
7+
name: 'SocialStudies'
78
index: Index
89
customScalars: {}
910
featureOptions: {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
url: 'https://countries.trevorblades.com/graphql',
3+
body: '{"query":"{ languages { code } }"}',
4+
method: 'POST',
5+
headers: Headers {
6+
authorization: 'Bearer MY_TOKEN',
7+
accept: 'application/graphql-response+json',
8+
'content-type': 'application/json'
9+
},
10+
mode: 'cors'
11+
}

examples/transport-http_headers.ts renamed to examples/transport-http_RequestInput.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import { publicGraphQLSchemaEndpoints } from './$helpers.js'
55
const graffle = Graffle
66
.create({
77
schema: publicGraphQLSchemaEndpoints.SocialStudies,
8-
headers: { authorization: `Bearer MY_TOKEN` },
8+
request: {
9+
headers: {
10+
authorization: `Bearer MY_TOKEN`,
11+
},
12+
mode: `cors`,
13+
},
914
})
1015
.use(async ({ exchange }) => {
11-
show(exchange.input.request.headers)
16+
show(exchange.input.request)
1217
return exchange()
1318
})
1419

examples/transport-http_headers.output.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/entrypoints/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { create as createSelect, select } from '../layers/5_select/select.js'
22
export { type Client, create } from '../layers/6_client/client.js'
33
export { createPrefilled, type InputPrefilled } from '../layers/6_client/prefilled.js'
4-
export { type Input } from '../layers/6_client/Settings/Input.js'
4+
export { type InputStatic } from '../layers/6_client/Settings/Input.js'

src/layers/1_Schema/core/Index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { GlobalRegistry } from '../../2_generator/globalRegistry.js'
22
import type { Output } from '../Output/__.js'
33

4+
/**
5+
* A generic schema index type. Any particular schema index will be a subtype of this, with
6+
* additional specificity such as on objects where here `Record` is used.
7+
*/
48
export interface Index {
59
name: GlobalRegistry.SchemaNames
610
Root: {

src/layers/2_generator/code/global.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ export const { moduleName: moduleNameGlobal, generate: generateGlobal } = create
2828

2929
code.push(`
3030
declare global {
31-
export namespace GraphQLRequestTypes {
31+
export namespace GraffleGlobalTypes {
3232
export interface Schemas {
3333
${config.name}: {
34+
name: '${config.name}'
3435
index: Index
3536
customScalars: {
3637
${

src/layers/2_generator/globalRegistry.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import type { TSError } from '../../lib/TSError.js'
33
import type { Schema } from '../1_Schema/__.js'
44

55
declare global {
6-
export namespace GraphQLRequestTypes {
6+
export namespace GraffleGlobalTypes {
77
interface Schemas {}
88
// Use this is for manual internal type testing.
99
// interface SchemasAlwaysEmpty {}
1010
}
1111
}
1212

1313
type SomeSchema = {
14+
name: string
1415
index: Schema.Index
1516
customScalars: Record<string, Schema.Scalar.Scalar>
1617
featureOptions: {
@@ -23,6 +24,7 @@ type SomeSchema = {
2324
}
2425

2526
type ZeroSchema = {
27+
name: GlobalRegistry.DefaultSchemaName
2628
index: { name: never }
2729
featureOptions: {
2830
schemaErrors: false
@@ -33,35 +35,45 @@ type ZeroSchema = {
3335
export type GlobalRegistry = Record<string, SomeSchema>
3436

3537
export namespace GlobalRegistry {
36-
export type Schemas = GraphQLRequestTypes.Schemas
38+
export type DefaultSchemaName = 'default'
39+
40+
export type Schemas = GraffleGlobalTypes.Schemas
3741

3842
export type IsEmpty = keyof Schemas extends never ? true : false
3943

40-
export type SchemaList = IsEmpty extends true ? ZeroSchema : Values<Schemas>
44+
export type SchemaUnion = IsEmpty extends true ? ZeroSchema : Values<Schemas>
4145

42-
export type DefaultSchemaName = 'default'
43-
44-
export type SchemaNames = keyof GraphQLRequestTypes.Schemas extends never
46+
export type SchemaNames = keyof GraffleGlobalTypes.Schemas extends never
4547
? TSError<'SchemaNames', 'No schemas have been registered. Did you run graffle generate?'>
46-
: keyof GraphQLRequestTypes.Schemas
48+
: keyof GraffleGlobalTypes.Schemas
49+
50+
// dprint-ignore
51+
export type HasDefaultUrlForSchema<$Schema extends SchemaUnion> =
52+
$Schema['defaultSchemaUrl'] extends null
53+
? false
54+
: true
4755

48-
export type HasSchemaErrors<$Schema extends SchemaList> = $Schema['featureOptions']['schemaErrors']
56+
// dprint-ignore
57+
export type HasSchemaErrors<$Schema extends SchemaUnion> =
58+
$Schema['featureOptions']['schemaErrors']
4959

5060
export type HasSchemaErrorsViaName<$Name extends SchemaNames> =
5161
// todo use conditional types?
5262
// eslint-disable-next-line
5363
// @ts-ignore passes after generation
54-
GraphQLRequestTypes.Schemas[$Name]['featureOptions']['schemaErrors']
64+
GraffleGlobalTypes.Schemas[$Name]['featureOptions']['schemaErrors']
5565

5666
// eslint-disable-next-line
5767
// @ts-ignore passes after generation
58-
export type GetSchemaIndex<$Name extends SchemaNames> = GraphQLRequestTypes.Schemas[$Name]['index']
68+
export type GetSchemaIndex<$Name extends SchemaNames> = GraffleGlobalTypes.Schemas[$Name]['index']
5969

6070
// eslint-disable-next-line
6171
// @ts-ignore passes after generation
6272
export type SchemaIndexDefault = GetSchemaIndex<DefaultSchemaName>
6373

64-
export type GetSchemaIndexOrDefault<$Name extends SchemaNames | undefined> = $Name extends SchemaNames
65-
? GetSchemaIndex<$Name>
66-
: SchemaIndexDefault
74+
// dprint-ignore
75+
export type GetSchemaIndexOrDefault<$Name extends SchemaNames | undefined> =
76+
$Name extends SchemaNames
77+
? GetSchemaIndex<$Name>
78+
: SchemaIndexDefault
6779
}

src/layers/3_SelectionSet/encode.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,17 @@ const testEachArgs = [
2222
]
2323
) => {
2424
const [description, ss] = args.length === 1 ? [undefined, args[0]] : args
25-
const context: Context = { schemaIndex, config: { output: outputConfigDefault, transport: `memory` } }
25+
const context: Context = {
26+
schemaIndex,
27+
config: {
28+
output: outputConfigDefault,
29+
transport: `memory`,
30+
name: schemaIndex[`name`],
31+
// eslint-disable-next-line
32+
initialInput: {} as any,
33+
requestInputOptions: {},
34+
},
35+
}
2636
const graphqlDocumentString = rootTypeSelectionSet(context, schemaIndex[`Root`][`Query`], ss as any)
2737
// Should parse, ensures is syntactically valid graphql document.
2838
const document = parse(graphqlDocumentString)

src/layers/5_core/core.ts

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { GraphQLObjectSelection } from '../3_SelectionSet/encode.js'
1212
import * as Result from '../4_ResultSet/customScalars.js'
1313
import type { GraffleExecutionResultVar } from '../6_client/client.js'
1414
import type { Config } from '../6_client/Settings/Config.js'
15+
import { mergeRequestInputOptions, type RequestInput } from '../6_client/Settings/inputIncrementable/request.js'
1516
import type {
1617
ContextInterfaceRaw,
1718
ContextInterfaceTyped,
@@ -48,9 +49,7 @@ type TransportInput<$Config extends Config, $HttpProperties = {}, $MemoryPropert
4849
TransportHttp extends $Config['transport']
4950
? ({
5051
transport: TransportHttp
51-
transportConstructorConfig: {
52-
headers?: HeadersInit
53-
}
52+
5453
} & $HttpProperties)
5554
: never
5655
)
@@ -94,29 +93,6 @@ export type HookDefPack<$Config extends Config> = {
9493
}>
9594
}
9695

97-
export type RequestInput = {
98-
url: string | URL
99-
method:
100-
| 'get'
101-
| 'post'
102-
| 'put'
103-
| 'delete'
104-
| 'patch'
105-
| 'head'
106-
| 'options'
107-
| 'trace'
108-
| 'GET'
109-
| 'POST'
110-
| 'PUT'
111-
| 'DELETE'
112-
| 'PATCH'
113-
| 'HEAD'
114-
| 'OPTIONS'
115-
| 'TRACE'
116-
headers?: HeadersInit
117-
body: BodyInit
118-
}
119-
12096
export type HookDefExchange<$Config extends Config> = {
12197
slots: {
12298
fetch: typeof fetch
@@ -231,26 +207,26 @@ export const anyware = Anyware.create<HookSequence, HookMap, ExecutionResult>({
231207
}
232208
case `http`: {
233209
// TODO thrown error here is swallowed in examples.
234-
const headers = mergeHeadersInit(
235-
input.transportConstructorConfig.headers ?? {},
236-
input.headers ?? {},
237-
)
238-
// @see https://graphql.github.io/graphql-over-http/draft/#sec-Accept
239-
headers.set(`accept`, CONTENT_TYPE_GQL)
240-
// @see https://graphql.github.io/graphql-over-http/draft/#sec-POST
241-
// todo if body is something else, say upload extension turns it into a FormData, then fetch will automatically set the content-type header.
242-
// ... however we should not rely on that behavior, and instead error here if there is no content type header and we cannot infer it here?
243-
if (typeof input.body === `string`) {
244-
headers.set(`content-type`, CONTENT_TYPE_JSON)
210+
const request: RequestInput = {
211+
url: input.url,
212+
body: input.body,
213+
// @see https://graphql.github.io/graphql-over-http/draft/#sec-POST
214+
method: `POST`,
215+
...mergeRequestInputOptions(input.context.config.requestInputOptions, {
216+
headers: mergeHeadersInit(input.headers, {
217+
// @see https://graphql.github.io/graphql-over-http/draft/#sec-Accept
218+
accept: CONTENT_TYPE_GQL,
219+
// todo if body is something else, say upload extension turns it into a FormData, then fetch will automatically set the content-type header.
220+
// ... however we should not rely on that behavior, and instead error here if there is no content type header and we cannot infer it here?
221+
...(typeof input.body === `string`
222+
? { 'content-type': CONTENT_TYPE_JSON }
223+
: {}),
224+
}),
225+
}),
245226
}
246227
return {
247228
...input,
248-
request: {
249-
url: input.url,
250-
body: input.body,
251-
method: `POST`,
252-
headers,
253-
},
229+
request,
254230
}
255231
}
256232
default:
@@ -266,13 +242,8 @@ export const anyware = Anyware.create<HookSequence, HookMap, ExecutionResult>({
266242
run: async ({ input, slots }) => {
267243
switch (input.transport) {
268244
case `http`: {
269-
const response = await slots.fetch(
270-
new Request(input.request.url, {
271-
method: input.request.method,
272-
headers: input.request.headers,
273-
body: input.request.body,
274-
}),
275-
)
245+
const request = new Request(input.request.url, input.request)
246+
const response = await slots.fetch(request)
276247
return {
277248
...input,
278249
response,

0 commit comments

Comments
 (0)