@avantstay/graphql-ts-client 11.0.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/chunk-I4QOADOI.mjs +27 -0
- package/dist/endpoint.d.ts +1 -1
- package/dist/endpoint.mjs +275 -0
- package/dist/index.d.ts +4 -7
- package/dist/index.js +67 -51
- package/dist/index.mjs +530 -0
- package/dist/{types-4ada662b.d.ts → types-23d2d6aa.d.ts} +6 -1
- package/package.json +44 -43
- package/src/__snapshots__/generateTypescriptClient.test.ts.snap +52 -0
- package/src/__testClient.d.ts +7 -19
- package/src/__testClient.js +1 -26
- package/src/__testClient.mjs +87 -0
- package/src/endpoint.ts +5 -9
- package/src/generateTypescriptClient.test.ts +3 -3
- package/src/generateTypescriptClient.ts +33 -20
- package/src/graphqlRequest.test.ts +3 -4
- package/src/jsonToGraphQLQuery.ts +10 -7
- package/src/types.ts +29 -22
|
@@ -76,6 +76,58 @@ const client = {
|
|
|
76
76
|
mutations: {}
|
|
77
77
|
};
|
|
78
78
|
var stdin_default = client;
|
|
79
|
+
",
|
|
80
|
+
"mjs": "import { getApiEndpointCreator } from "./endpoint";
|
|
81
|
+
const formatGraphQL = (query) => query;
|
|
82
|
+
const typesTree = {};
|
|
83
|
+
let verbose = false;
|
|
84
|
+
let headers = {};
|
|
85
|
+
let url = "https://sample.endpoint.com/graphl";
|
|
86
|
+
let retryConfig = {
|
|
87
|
+
max: 0,
|
|
88
|
+
before: void 0,
|
|
89
|
+
waitBeforeRetry: 0
|
|
90
|
+
};
|
|
91
|
+
let responseListeners = [];
|
|
92
|
+
let errorsParser = void 0;
|
|
93
|
+
let apiEndpoint = getApiEndpointCreator({
|
|
94
|
+
getClient: () => ({ url, headers, retryConfig }),
|
|
95
|
+
responseListeners,
|
|
96
|
+
maxAge: 3e4,
|
|
97
|
+
verbose,
|
|
98
|
+
typesTree,
|
|
99
|
+
formatGraphQL,
|
|
100
|
+
errorsParser
|
|
101
|
+
});
|
|
102
|
+
const client = {
|
|
103
|
+
addResponseListener: (listener) => responseListeners.push(listener),
|
|
104
|
+
setHeader: (key, value) => {
|
|
105
|
+
headers[key] = value;
|
|
106
|
+
},
|
|
107
|
+
setHeaders: (newHeaders) => {
|
|
108
|
+
headers = newHeaders;
|
|
109
|
+
},
|
|
110
|
+
setRetryConfig: (options) => {
|
|
111
|
+
if (!Number.isInteger(options.max) || options.max < 0) {
|
|
112
|
+
throw new Error("retryOptions.max should be a non-negative integer");
|
|
113
|
+
}
|
|
114
|
+
retryConfig = {
|
|
115
|
+
max: options.max,
|
|
116
|
+
waitBeforeRetry: options.waitBeforeRetry,
|
|
117
|
+
before: options.before
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
setUrl: (_url) => url = _url,
|
|
121
|
+
queries: {
|
|
122
|
+
hello: apiEndpoint("query", "hello")
|
|
123
|
+
},
|
|
124
|
+
mutations: {}
|
|
125
|
+
};
|
|
126
|
+
var stdin_default = client;
|
|
127
|
+
export {
|
|
128
|
+
client,
|
|
129
|
+
stdin_default as default
|
|
130
|
+
};
|
|
79
131
|
",
|
|
80
132
|
"typings": "// noinspection TypeScriptUnresolvedVariable, ES6UnusedImports, JSUnusedLocalSymbols, TypeScriptCheckImport
|
|
81
133
|
import { DeepRequired } from "ts-essentials"
|
package/src/__testClient.d.ts
CHANGED
|
@@ -39,16 +39,6 @@ export interface Book {
|
|
|
39
39
|
author?: string
|
|
40
40
|
type?: BookType
|
|
41
41
|
dateCreated?: ISODate
|
|
42
|
-
sequence?: BookSequence
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @deprecated Avoid directly using this interface. Instead, create a type alias based on the query/mutation return type.
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
export interface BookSequence {
|
|
50
|
-
previousBook?: Book
|
|
51
|
-
nextBook?: Book
|
|
52
42
|
}
|
|
53
43
|
|
|
54
44
|
/**
|
|
@@ -88,12 +78,6 @@ export interface BookSelection {
|
|
|
88
78
|
author?: boolean
|
|
89
79
|
type?: boolean
|
|
90
80
|
dateCreated?: boolean
|
|
91
|
-
sequence?: BookSequenceSelection
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface BookSequenceSelection {
|
|
95
|
-
previousBook?: BookSelection
|
|
96
|
-
nextBook?: BookSelection
|
|
97
81
|
}
|
|
98
82
|
|
|
99
83
|
export interface BookSearchParamsAllOptionalSelection {
|
|
@@ -123,8 +107,9 @@ export declare const myApiClient: {
|
|
|
123
107
|
__headers?: { [key: string]: string }
|
|
124
108
|
__retry?: boolean
|
|
125
109
|
__alias?: string
|
|
110
|
+
__url?: string
|
|
126
111
|
} & BookSelection,
|
|
127
|
-
|
|
112
|
+
Book[],
|
|
128
113
|
AllEnums
|
|
129
114
|
>
|
|
130
115
|
booksWithOptionalParams: Endpoint<
|
|
@@ -132,9 +117,10 @@ export declare const myApiClient: {
|
|
|
132
117
|
__headers?: { [key: string]: string }
|
|
133
118
|
__retry?: boolean
|
|
134
119
|
__alias?: string
|
|
120
|
+
__url?: string
|
|
135
121
|
__args?: BooksWithOptionalParamsArgs
|
|
136
122
|
} & BookSelection,
|
|
137
|
-
|
|
123
|
+
Book[],
|
|
138
124
|
AllEnums
|
|
139
125
|
>
|
|
140
126
|
booksWithRequiredParams: Endpoint<
|
|
@@ -142,9 +128,10 @@ export declare const myApiClient: {
|
|
|
142
128
|
__headers?: { [key: string]: string }
|
|
143
129
|
__retry?: boolean
|
|
144
130
|
__alias?: string
|
|
131
|
+
__url?: string
|
|
145
132
|
__args: BooksWithRequiredParamsArgs
|
|
146
133
|
} & BookSelection,
|
|
147
|
-
|
|
134
|
+
Book[],
|
|
148
135
|
AllEnums
|
|
149
136
|
>
|
|
150
137
|
failingQuery: Endpoint<
|
|
@@ -152,6 +139,7 @@ export declare const myApiClient: {
|
|
|
152
139
|
__headers?: { [key: string]: string }
|
|
153
140
|
__retry?: boolean
|
|
154
141
|
__alias?: string
|
|
142
|
+
__url?: string
|
|
155
143
|
__args: FailingQueryArgs
|
|
156
144
|
},
|
|
157
145
|
string,
|
package/src/__testClient.js
CHANGED
|
@@ -39,7 +39,6 @@ const typesTree = {
|
|
|
39
39
|
Query: {
|
|
40
40
|
get booksWithOptionalParams() {
|
|
41
41
|
return {
|
|
42
|
-
__fields: typesTree.Book,
|
|
43
42
|
__args: {
|
|
44
43
|
params: "BookSearchParamsAllOptional!"
|
|
45
44
|
}
|
|
@@ -47,7 +46,6 @@ const typesTree = {
|
|
|
47
46
|
},
|
|
48
47
|
get booksWithRequiredParams() {
|
|
49
48
|
return {
|
|
50
|
-
__fields: typesTree.Book,
|
|
51
49
|
__args: {
|
|
52
50
|
params: "BookSearchParamsSomeRequired!"
|
|
53
51
|
}
|
|
@@ -60,30 +58,7 @@ const typesTree = {
|
|
|
60
58
|
}
|
|
61
59
|
};
|
|
62
60
|
},
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
__fields: typesTree.Book
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
Book: {
|
|
70
|
-
get sequence() {
|
|
71
|
-
return {
|
|
72
|
-
__fields: typesTree.BookSequence
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
BookSequence: {
|
|
77
|
-
get previousBook() {
|
|
78
|
-
return {
|
|
79
|
-
__fields: typesTree.Book
|
|
80
|
-
};
|
|
81
|
-
},
|
|
82
|
-
get nextBook() {
|
|
83
|
-
return {
|
|
84
|
-
__fields: typesTree.Book
|
|
85
|
-
};
|
|
86
|
-
}
|
|
61
|
+
booksWithoutParams: {}
|
|
87
62
|
}
|
|
88
63
|
};
|
|
89
64
|
let verbose = false;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { getApiEndpointCreator } from "./endpoint";
|
|
2
|
+
import { format as formatCode } from "prettier/standalone";
|
|
3
|
+
import parserGraphql from "prettier/parser-graphql";
|
|
4
|
+
const formatGraphQL = (query) => formatCode(query, { parser: "graphql", plugins: [parserGraphql] });
|
|
5
|
+
const BookType = {
|
|
6
|
+
dolor: "DOLOR",
|
|
7
|
+
ipsum: "IPSUM",
|
|
8
|
+
sit: "SIT"
|
|
9
|
+
};
|
|
10
|
+
const typesTree = {
|
|
11
|
+
Query: {
|
|
12
|
+
get booksWithOptionalParams() {
|
|
13
|
+
return {
|
|
14
|
+
__args: {
|
|
15
|
+
params: "BookSearchParamsAllOptional!"
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
get booksWithRequiredParams() {
|
|
20
|
+
return {
|
|
21
|
+
__args: {
|
|
22
|
+
params: "BookSearchParamsSomeRequired!"
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
get failingQuery() {
|
|
27
|
+
return {
|
|
28
|
+
__args: {
|
|
29
|
+
id: "String!"
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
booksWithoutParams: {}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
let verbose = false;
|
|
37
|
+
let headers = {};
|
|
38
|
+
let url = "http://localhost:4123/graphql";
|
|
39
|
+
let retryConfig = {
|
|
40
|
+
max: 0,
|
|
41
|
+
before: void 0,
|
|
42
|
+
waitBeforeRetry: 0
|
|
43
|
+
};
|
|
44
|
+
let responseListeners = [];
|
|
45
|
+
let errorsParser = void 0;
|
|
46
|
+
let apiEndpoint = getApiEndpointCreator({
|
|
47
|
+
getClient: () => ({ url, headers, retryConfig }),
|
|
48
|
+
responseListeners,
|
|
49
|
+
maxAge: 3e4,
|
|
50
|
+
verbose,
|
|
51
|
+
typesTree,
|
|
52
|
+
formatGraphQL,
|
|
53
|
+
errorsParser
|
|
54
|
+
});
|
|
55
|
+
const myApiClient = {
|
|
56
|
+
addResponseListener: (listener) => responseListeners.push(listener),
|
|
57
|
+
setHeader: (key, value) => {
|
|
58
|
+
headers[key] = value;
|
|
59
|
+
},
|
|
60
|
+
setHeaders: (newHeaders) => {
|
|
61
|
+
headers = newHeaders;
|
|
62
|
+
},
|
|
63
|
+
setRetryConfig: (options) => {
|
|
64
|
+
if (!Number.isInteger(options.max) || options.max < 0) {
|
|
65
|
+
throw new Error("retryOptions.max should be a non-negative integer");
|
|
66
|
+
}
|
|
67
|
+
retryConfig = {
|
|
68
|
+
max: options.max,
|
|
69
|
+
waitBeforeRetry: options.waitBeforeRetry,
|
|
70
|
+
before: options.before
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
setUrl: (_url) => url = _url,
|
|
74
|
+
queries: {
|
|
75
|
+
booksWithoutParams: apiEndpoint("query", "booksWithoutParams"),
|
|
76
|
+
booksWithOptionalParams: apiEndpoint("query", "booksWithOptionalParams"),
|
|
77
|
+
booksWithRequiredParams: apiEndpoint("query", "booksWithRequiredParams"),
|
|
78
|
+
failingQuery: apiEndpoint("query", "failingQuery")
|
|
79
|
+
},
|
|
80
|
+
mutations: {}
|
|
81
|
+
};
|
|
82
|
+
var stdin_default = myApiClient;
|
|
83
|
+
export {
|
|
84
|
+
BookType,
|
|
85
|
+
stdin_default as default,
|
|
86
|
+
myApiClient
|
|
87
|
+
};
|
package/src/endpoint.ts
CHANGED
|
@@ -2,14 +2,10 @@ import memoize from 'moize'
|
|
|
2
2
|
import { graphqlRequest } from './graphqlRequest'
|
|
3
3
|
import { jsonToGraphQLQuery } from './jsonToGraphQLQuery'
|
|
4
4
|
import { logRequest } from './logging'
|
|
5
|
-
import {ClientConfig, Endpoint, GraphQLClientError, IResponseListener, Projection, ResponseListenerInfo} from './types'
|
|
5
|
+
import { ClientConfig, Endpoint, GraphQLClientError, IResponseListener, Projection, ResponseListenerInfo } from './types'
|
|
6
6
|
|
|
7
7
|
const executeListeners = (listeners: IResponseListener[], data: ResponseListenerInfo) =>
|
|
8
|
-
setTimeout(() =>
|
|
9
|
-
listeners.forEach(runResponseListener =>
|
|
10
|
-
runResponseListener(data)
|
|
11
|
-
)
|
|
12
|
-
)
|
|
8
|
+
setTimeout(() => listeners.forEach(runResponseListener => runResponseListener(data)))
|
|
13
9
|
|
|
14
10
|
export const getApiEndpointCreator =
|
|
15
11
|
(apiConfig: {
|
|
@@ -61,11 +57,11 @@ export const getApiEndpointCreator =
|
|
|
61
57
|
shouldRetry,
|
|
62
58
|
failureMode,
|
|
63
59
|
queryName: alias,
|
|
64
|
-
client: {...clientConfig, url },
|
|
60
|
+
client: { ...clientConfig, url },
|
|
65
61
|
requestHeaders,
|
|
66
62
|
query,
|
|
67
63
|
variables,
|
|
68
|
-
errorsParser: apiConfig.errorsParser
|
|
64
|
+
errorsParser: apiConfig.errorsParser,
|
|
69
65
|
})
|
|
70
66
|
|
|
71
67
|
const response = { data, warnings, headers, status, errors }
|
|
@@ -78,7 +74,7 @@ export const getApiEndpointCreator =
|
|
|
78
74
|
})
|
|
79
75
|
}
|
|
80
76
|
|
|
81
|
-
executeListeners(apiConfig.responseListeners,{
|
|
77
|
+
executeListeners(apiConfig.responseListeners, {
|
|
82
78
|
...responseListenerData,
|
|
83
79
|
response,
|
|
84
80
|
})
|
|
@@ -125,12 +125,12 @@ describe('Generated Client', () => {
|
|
|
125
125
|
expect(responseData?.response.errors.length).toBeGreaterThan(0)
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
it('should generate proper code from SDL', ()=>{
|
|
128
|
+
it('should generate proper code from SDL', () => {
|
|
129
129
|
const sdlString = `
|
|
130
130
|
type Query {
|
|
131
131
|
hello: String
|
|
132
132
|
}
|
|
133
|
-
|
|
134
|
-
expect(generateTypescriptClientFromSDL(sdlString, {endpoint: 'https://sample.endpoint.com/graphl'})).toMatchSnapshot()
|
|
133
|
+
`
|
|
134
|
+
expect(generateTypescriptClientFromSDL(sdlString, { endpoint: 'https://sample.endpoint.com/graphl' })).toMatchSnapshot()
|
|
135
135
|
})
|
|
136
136
|
})
|
|
@@ -6,8 +6,8 @@ import * as fs from 'fs'
|
|
|
6
6
|
import { PathLike } from 'fs'
|
|
7
7
|
import {
|
|
8
8
|
buildSchema,
|
|
9
|
-
graphqlSync,
|
|
10
9
|
getIntrospectionQuery,
|
|
10
|
+
graphqlSync,
|
|
11
11
|
IntrospectionEnumType,
|
|
12
12
|
IntrospectionField,
|
|
13
13
|
IntrospectionInputObjectType,
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
IntrospectionObjectType,
|
|
16
16
|
IntrospectionOutputTypeRef,
|
|
17
17
|
IntrospectionType,
|
|
18
|
-
Source,
|
|
19
18
|
} from 'graphql'
|
|
20
19
|
import kebabCase from 'lodash/kebabCase'
|
|
21
20
|
import orderBy from 'lodash/orderBy'
|
|
@@ -25,6 +24,7 @@ import os from 'os'
|
|
|
25
24
|
import path from 'path'
|
|
26
25
|
import * as prettier from 'prettier'
|
|
27
26
|
import pkg from '../package.json'
|
|
27
|
+
import { TypescriptClientOutput } from './types'
|
|
28
28
|
|
|
29
29
|
const tempDir = fs.realpathSync(os.tmpdir())
|
|
30
30
|
|
|
@@ -314,7 +314,7 @@ type IClientOptions = {
|
|
|
314
314
|
errorsParser?: (errors: any[]) => any
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
type FetchIntrospectionOptions = Omit<
|
|
317
|
+
type FetchIntrospectionOptions = Omit<IClientOptions, 'output' | 'introspectionEndpoint'>
|
|
318
318
|
|
|
319
319
|
function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Omit<IClientOptions, 'output'>) {
|
|
320
320
|
const typesHash = md5(`${JSON.stringify(options)}__${JSON.stringify(types)}`)
|
|
@@ -322,7 +322,11 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
|
|
|
322
322
|
const clientCacheFilePath = path.resolve(tempDir, clientCacheFileName)
|
|
323
323
|
|
|
324
324
|
if (!options.skipCache && fs.existsSync(clientCacheFilePath)) {
|
|
325
|
-
|
|
325
|
+
const output: Partial<TypescriptClientOutput> = JSON.parse(fs.readFileSync(clientCacheFilePath, { encoding: 'utf8' }))
|
|
326
|
+
|
|
327
|
+
if (output.js && output.mjs && output.typings) {
|
|
328
|
+
return output as TypescriptClientOutput
|
|
329
|
+
}
|
|
326
330
|
}
|
|
327
331
|
|
|
328
332
|
const queries = (<IntrospectionObjectType>types.find(it => it.name === 'Query'))?.fields || []
|
|
@@ -472,8 +476,9 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
|
|
|
472
476
|
|
|
473
477
|
export default ${clientName}`
|
|
474
478
|
|
|
475
|
-
const output = {
|
|
479
|
+
const output: TypescriptClientOutput = {
|
|
476
480
|
js: esbuild.transformSync(jsCode, { format: 'cjs', loader: 'js' }).code,
|
|
481
|
+
mjs: esbuild.transformSync(jsCode, { format: 'esm', loader: 'js' }).code,
|
|
477
482
|
typings: prettier.format(typingsCode, { semi: false, parser: 'typescript' }),
|
|
478
483
|
}
|
|
479
484
|
|
|
@@ -498,18 +503,18 @@ async function fetchIntrospection({ endpoint, headers }: FetchIntrospectionOptio
|
|
|
498
503
|
'Content-Type': 'application/json',
|
|
499
504
|
...headers,
|
|
500
505
|
},
|
|
506
|
+
timeout: 5000,
|
|
501
507
|
}
|
|
502
508
|
)
|
|
503
|
-
.catch(
|
|
504
|
-
const errorMessage = `The GraphQL introspection request failed (${endpoint})`
|
|
509
|
+
.catch(e => {
|
|
505
510
|
if (fs.existsSync(introspectionCacheFilePath)) {
|
|
506
511
|
const cachedSchema = JSON.parse(fs.readFileSync(introspectionCacheFilePath, { encoding: 'utf8' }))
|
|
507
512
|
loadedFromCache = true
|
|
508
|
-
console.warn(`Successfully restored from local cache.`)
|
|
513
|
+
console.warn(`Successfully restored (${endpoint}) from local cache.`)
|
|
509
514
|
return { data: cachedSchema }
|
|
510
515
|
} else {
|
|
511
516
|
console.error(e)
|
|
512
|
-
return Promise.reject(
|
|
517
|
+
return Promise.reject(`The GraphQL introspection request failed (${endpoint})`)
|
|
513
518
|
}
|
|
514
519
|
})
|
|
515
520
|
|
|
@@ -526,10 +531,11 @@ async function fetchIntrospection({ endpoint, headers }: FetchIntrospectionOptio
|
|
|
526
531
|
return types
|
|
527
532
|
}
|
|
528
533
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
534
|
+
function generateClient(
|
|
535
|
+
introspectionTypes: ReadonlyArray<IntrospectionType>,
|
|
536
|
+
{ output, ...restOptions }: IClientOptions
|
|
537
|
+
): TypescriptClientOutput {
|
|
538
|
+
const { js, mjs, typings } = generateClientCode(introspectionTypes, restOptions)
|
|
533
539
|
|
|
534
540
|
if (output && typeof output === 'string') {
|
|
535
541
|
const outputDir = path.dirname(output)
|
|
@@ -540,12 +546,16 @@ function generateClient(introspectionTypes: ReadonlyArray<IntrospectionType>, {o
|
|
|
540
546
|
|
|
541
547
|
fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.d.ts'), typings, { encoding: 'utf8' })
|
|
542
548
|
fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.js'), js, { encoding: 'utf8' })
|
|
549
|
+
fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.mjs'), mjs, { encoding: 'utf8' })
|
|
543
550
|
}
|
|
544
551
|
|
|
545
|
-
return { js, typings }
|
|
552
|
+
return { js, mjs, typings }
|
|
546
553
|
}
|
|
547
554
|
|
|
548
|
-
export async function generateTypescriptClient({
|
|
555
|
+
export async function generateTypescriptClient({
|
|
556
|
+
introspectionEndpoint,
|
|
557
|
+
...options
|
|
558
|
+
}: IClientOptions): Promise<TypescriptClientOutput> {
|
|
549
559
|
console.log(`Generating TypeScript client (name: ${options.clientName ?? 'n/a'})`)
|
|
550
560
|
|
|
551
561
|
axiosRetry(axios, { retries: 5, retryDelay: retryCount => 1000 * 2 ** retryCount })
|
|
@@ -556,14 +566,17 @@ export async function generateTypescriptClient({ introspectionEndpoint, ...optio
|
|
|
556
566
|
})
|
|
557
567
|
|
|
558
568
|
return generateClient(introspectionTypes, options)
|
|
559
|
-
|
|
560
569
|
}
|
|
561
570
|
|
|
562
|
-
export function generateTypescriptClientFromSDL(SDL: string, options: IClientOptions):
|
|
563
|
-
console.log(`Generating TypeScript client from SDL (name: ${options.clientName ?? 'n/a'})`)
|
|
571
|
+
export function generateTypescriptClientFromSDL(SDL: string, options: IClientOptions): TypescriptClientOutput {
|
|
572
|
+
console.log(`Generating TypeScript client from SDL (name: ${options.clientName ?? 'n/a'})`)
|
|
564
573
|
|
|
565
|
-
const graphqlSchemaObj = buildSchema(SDL)
|
|
566
|
-
const
|
|
574
|
+
const graphqlSchemaObj = buildSchema(SDL)
|
|
575
|
+
const introspectionResult = graphqlSync({
|
|
576
|
+
schema: graphqlSchemaObj,
|
|
577
|
+
source: getIntrospectionQuery(),
|
|
578
|
+
})
|
|
579
|
+
const introspectionTypes = (introspectionResult.data as any)?.__schema.types as IntrospectionType[]
|
|
567
580
|
|
|
568
581
|
return generateClient(introspectionTypes, options)
|
|
569
582
|
}
|
|
@@ -2,10 +2,10 @@ import { graphqlRequest } from './graphqlRequest'
|
|
|
2
2
|
|
|
3
3
|
describe('GraphQLRequest', () => {
|
|
4
4
|
it('Should request have proper structure', async () => {
|
|
5
|
-
let request: any
|
|
5
|
+
let request: any
|
|
6
6
|
|
|
7
7
|
const mockedAxios = {
|
|
8
|
-
post: function() {
|
|
8
|
+
post: function () {
|
|
9
9
|
request = arguments
|
|
10
10
|
return {
|
|
11
11
|
status: 200,
|
|
@@ -19,7 +19,7 @@ describe('GraphQLRequest', () => {
|
|
|
19
19
|
axios: mockedAxios,
|
|
20
20
|
queryName: 'sampleQueryName',
|
|
21
21
|
query: 'sampleQuery',
|
|
22
|
-
variables: {foo:'bar',bar:'foo'},
|
|
22
|
+
variables: { foo: 'bar', bar: 'foo' },
|
|
23
23
|
client: {
|
|
24
24
|
url: 'https://whatever.com',
|
|
25
25
|
headers: {},
|
|
@@ -51,7 +51,6 @@ describe('GraphQLRequest', () => {
|
|
|
51
51
|
} as any
|
|
52
52
|
|
|
53
53
|
const result = await graphqlRequest({
|
|
54
|
-
|
|
55
54
|
failureMode: 'loud',
|
|
56
55
|
axios: mockedAxios,
|
|
57
56
|
queryName: 'whatever',
|
|
@@ -31,14 +31,17 @@ export function jsonToGraphQLQuery({
|
|
|
31
31
|
parentType: kind === 'query' ? typesTree.Query : typesTree.Mutation,
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
const variableItems = Object.values(variablesData).reduce(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const variableItems = Object.values(variablesData).reduce(
|
|
35
|
+
(variablesObj, variables) => {
|
|
36
|
+
variables.forEach((variable, index) => {
|
|
37
|
+
const name = variable.update(variables.length > 1 ? index : undefined)
|
|
38
|
+
variablesObj[name] = { type: variable.type, value: variable.value }
|
|
39
|
+
})
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
return variablesObj
|
|
42
|
+
},
|
|
43
|
+
{} as Record<string, Variable>
|
|
44
|
+
)
|
|
42
45
|
|
|
43
46
|
const variablesQuery = Object.keys(variableItems).length
|
|
44
47
|
? `(${entries(variableItems)
|
package/src/types.ts
CHANGED
|
@@ -24,30 +24,31 @@ type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends read
|
|
|
24
24
|
type Primitive = Date | string | number | boolean | null | undefined
|
|
25
25
|
|
|
26
26
|
// Projection is the resulting type of Selection (type generated out of a query) applied to Base (generated graphql type)
|
|
27
|
-
export type Projection<Selection, Base, E = never> =
|
|
28
|
-
|
|
29
|
-
? ArrayElement<Base>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
export type Projection<Selection, Base, E = never> =
|
|
28
|
+
Base extends Array<any>
|
|
29
|
+
? ArrayElement<Base> extends Primitive | E
|
|
30
|
+
? ArrayElement<Base>[]
|
|
31
|
+
: Projection<Defined<Selection>, ArrayElement<Base>, E>[]
|
|
32
|
+
: Base extends Primitive | E
|
|
33
|
+
? // Is primitive and extends undefined
|
|
34
|
+
Selection extends undefined
|
|
35
|
+
? Base | undefined
|
|
36
|
+
: Base
|
|
37
|
+
: {
|
|
38
|
+
[k in keyof Selection & keyof Base]: Selection[k] extends boolean
|
|
39
|
+
? Base[k]
|
|
40
|
+
: Base[k] extends Array<infer A>
|
|
41
|
+
? Projection<Defined<Selection[k]>, A, E>[]
|
|
42
|
+
: Projection<Defined<Selection[k]>, Base[k], E>
|
|
43
|
+
}
|
|
43
44
|
|
|
44
45
|
export type Unpacked<T> = T extends (infer U)[]
|
|
45
46
|
? U
|
|
46
47
|
: T extends (...args: any[]) => infer U
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
? U
|
|
49
|
+
: T extends Promise<infer U>
|
|
50
|
+
? U
|
|
51
|
+
: T
|
|
51
52
|
|
|
52
53
|
export type Replacement<M extends [any, any], T> = M extends any ? ([T] extends [M[0]] ? M[1] : never) : never
|
|
53
54
|
|
|
@@ -59,8 +60,8 @@ export type DeepReplace<T, Ignore, M extends [any, any]> = {
|
|
|
59
60
|
: T[P]
|
|
60
61
|
: Replacement<M, T[P]>
|
|
61
62
|
: T[P] extends object
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
? DeepReplace<T[P], Ignore, M>
|
|
64
|
+
: T[P]
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export type RawEndpoint<I, O, E> = <S extends I>(
|
|
@@ -98,6 +99,12 @@ export type LogInfo = {
|
|
|
98
99
|
duration: number
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
export type TypescriptClientOutput = {
|
|
103
|
+
js: string
|
|
104
|
+
mjs: string
|
|
105
|
+
typings: string
|
|
106
|
+
}
|
|
107
|
+
|
|
101
108
|
export class GraphQLClientError extends Error {
|
|
102
109
|
responseData: ResponseData
|
|
103
110
|
|