@avantstay/graphql-ts-client 11.0.0 → 12.0.1
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-2VJ7JZPA.mjs +18 -0
- package/dist/chunk-I4QOADOI.mjs +27 -0
- package/dist/endpoint.d.ts +1 -1
- package/dist/endpoint.js +6 -9
- package/dist/endpoint.mjs +271 -0
- package/dist/index.d.ts +4 -7
- package/dist/index.js +73 -59
- package/dist/index.mjs +528 -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 +34 -23
- package/src/graphqlRequest.test.ts +3 -4
- package/src/jsonToGraphQLQuery.ts +11 -12
- 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,16 +15,14 @@ import {
|
|
|
15
15
|
IntrospectionObjectType,
|
|
16
16
|
IntrospectionOutputTypeRef,
|
|
17
17
|
IntrospectionType,
|
|
18
|
-
Source,
|
|
19
18
|
} from 'graphql'
|
|
20
|
-
import kebabCase from 'lodash
|
|
21
|
-
import orderBy from 'lodash/orderBy'
|
|
22
|
-
import set from 'lodash/set'
|
|
19
|
+
import { kebabCase, orderBy, set } from 'lodash'
|
|
23
20
|
import md5 from 'md5'
|
|
24
21
|
import os from 'os'
|
|
25
22
|
import path from 'path'
|
|
26
23
|
import * as prettier from 'prettier'
|
|
27
24
|
import pkg from '../package.json'
|
|
25
|
+
import { TypescriptClientOutput } from './types'
|
|
28
26
|
|
|
29
27
|
const tempDir = fs.realpathSync(os.tmpdir())
|
|
30
28
|
|
|
@@ -314,7 +312,7 @@ type IClientOptions = {
|
|
|
314
312
|
errorsParser?: (errors: any[]) => any
|
|
315
313
|
}
|
|
316
314
|
|
|
317
|
-
type FetchIntrospectionOptions = Omit<
|
|
315
|
+
type FetchIntrospectionOptions = Omit<IClientOptions, 'output' | 'introspectionEndpoint'>
|
|
318
316
|
|
|
319
317
|
function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Omit<IClientOptions, 'output'>) {
|
|
320
318
|
const typesHash = md5(`${JSON.stringify(options)}__${JSON.stringify(types)}`)
|
|
@@ -322,7 +320,11 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
|
|
|
322
320
|
const clientCacheFilePath = path.resolve(tempDir, clientCacheFileName)
|
|
323
321
|
|
|
324
322
|
if (!options.skipCache && fs.existsSync(clientCacheFilePath)) {
|
|
325
|
-
|
|
323
|
+
const output: Partial<TypescriptClientOutput> = JSON.parse(fs.readFileSync(clientCacheFilePath, { encoding: 'utf8' }))
|
|
324
|
+
|
|
325
|
+
if (output.js && output.mjs && output.typings) {
|
|
326
|
+
return output as TypescriptClientOutput
|
|
327
|
+
}
|
|
326
328
|
}
|
|
327
329
|
|
|
328
330
|
const queries = (<IntrospectionObjectType>types.find(it => it.name === 'Query'))?.fields || []
|
|
@@ -472,8 +474,9 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
|
|
|
472
474
|
|
|
473
475
|
export default ${clientName}`
|
|
474
476
|
|
|
475
|
-
const output = {
|
|
477
|
+
const output: TypescriptClientOutput = {
|
|
476
478
|
js: esbuild.transformSync(jsCode, { format: 'cjs', loader: 'js' }).code,
|
|
479
|
+
mjs: esbuild.transformSync(jsCode, { format: 'esm', loader: 'js' }).code,
|
|
477
480
|
typings: prettier.format(typingsCode, { semi: false, parser: 'typescript' }),
|
|
478
481
|
}
|
|
479
482
|
|
|
@@ -498,18 +501,18 @@ async function fetchIntrospection({ endpoint, headers }: FetchIntrospectionOptio
|
|
|
498
501
|
'Content-Type': 'application/json',
|
|
499
502
|
...headers,
|
|
500
503
|
},
|
|
504
|
+
timeout: 5000,
|
|
501
505
|
}
|
|
502
506
|
)
|
|
503
|
-
.catch(
|
|
504
|
-
const errorMessage = `The GraphQL introspection request failed (${endpoint})`
|
|
507
|
+
.catch(e => {
|
|
505
508
|
if (fs.existsSync(introspectionCacheFilePath)) {
|
|
506
509
|
const cachedSchema = JSON.parse(fs.readFileSync(introspectionCacheFilePath, { encoding: 'utf8' }))
|
|
507
510
|
loadedFromCache = true
|
|
508
|
-
console.warn(`Successfully restored from local cache.`)
|
|
511
|
+
console.warn(`Successfully restored (${endpoint}) from local cache.`)
|
|
509
512
|
return { data: cachedSchema }
|
|
510
513
|
} else {
|
|
511
514
|
console.error(e)
|
|
512
|
-
return Promise.reject(
|
|
515
|
+
return Promise.reject(`The GraphQL introspection request failed (${endpoint})`)
|
|
513
516
|
}
|
|
514
517
|
})
|
|
515
518
|
|
|
@@ -526,10 +529,11 @@ async function fetchIntrospection({ endpoint, headers }: FetchIntrospectionOptio
|
|
|
526
529
|
return types
|
|
527
530
|
}
|
|
528
531
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
532
|
+
function generateClient(
|
|
533
|
+
introspectionTypes: ReadonlyArray<IntrospectionType>,
|
|
534
|
+
{ output, ...restOptions }: IClientOptions
|
|
535
|
+
): TypescriptClientOutput {
|
|
536
|
+
const { js, mjs, typings } = generateClientCode(introspectionTypes, restOptions)
|
|
533
537
|
|
|
534
538
|
if (output && typeof output === 'string') {
|
|
535
539
|
const outputDir = path.dirname(output)
|
|
@@ -540,12 +544,16 @@ function generateClient(introspectionTypes: ReadonlyArray<IntrospectionType>, {o
|
|
|
540
544
|
|
|
541
545
|
fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.d.ts'), typings, { encoding: 'utf8' })
|
|
542
546
|
fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.js'), js, { encoding: 'utf8' })
|
|
547
|
+
fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.mjs'), mjs, { encoding: 'utf8' })
|
|
543
548
|
}
|
|
544
549
|
|
|
545
|
-
return { js, typings }
|
|
550
|
+
return { js, mjs, typings }
|
|
546
551
|
}
|
|
547
552
|
|
|
548
|
-
export async function generateTypescriptClient({
|
|
553
|
+
export async function generateTypescriptClient({
|
|
554
|
+
introspectionEndpoint,
|
|
555
|
+
...options
|
|
556
|
+
}: IClientOptions): Promise<TypescriptClientOutput> {
|
|
549
557
|
console.log(`Generating TypeScript client (name: ${options.clientName ?? 'n/a'})`)
|
|
550
558
|
|
|
551
559
|
axiosRetry(axios, { retries: 5, retryDelay: retryCount => 1000 * 2 ** retryCount })
|
|
@@ -556,14 +564,17 @@ export async function generateTypescriptClient({ introspectionEndpoint, ...optio
|
|
|
556
564
|
})
|
|
557
565
|
|
|
558
566
|
return generateClient(introspectionTypes, options)
|
|
559
|
-
|
|
560
567
|
}
|
|
561
568
|
|
|
562
|
-
export function generateTypescriptClientFromSDL(SDL: string, options: IClientOptions):
|
|
563
|
-
console.log(`Generating TypeScript client from SDL (name: ${options.clientName ?? 'n/a'})`)
|
|
569
|
+
export function generateTypescriptClientFromSDL(SDL: string, options: IClientOptions): TypescriptClientOutput {
|
|
570
|
+
console.log(`Generating TypeScript client from SDL (name: ${options.clientName ?? 'n/a'})`)
|
|
564
571
|
|
|
565
|
-
const graphqlSchemaObj = buildSchema(SDL)
|
|
566
|
-
const
|
|
572
|
+
const graphqlSchemaObj = buildSchema(SDL)
|
|
573
|
+
const introspectionResult = graphqlSync({
|
|
574
|
+
schema: graphqlSchemaObj,
|
|
575
|
+
source: getIntrospectionQuery(),
|
|
576
|
+
})
|
|
577
|
+
const introspectionTypes = (introspectionResult.data as any)?.__schema.types as IntrospectionType[]
|
|
567
578
|
|
|
568
579
|
return generateClient(introspectionTypes, options)
|
|
569
580
|
}
|
|
@@ -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',
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import omit from 'lodash
|
|
1
|
+
import { cloneDeep, fromPairs as fromEntries, omit, toPairs as entries } from 'lodash'
|
|
2
2
|
|
|
3
3
|
const VAR_PREFIX = '@@VAR@@'
|
|
4
4
|
const VAR_PREFIX_LENGTH = VAR_PREFIX.length
|
|
5
5
|
|
|
6
|
-
const fromEntries: (arr: [string, any][]) => { [key: string]: any } = require('lodash/fromPairs')
|
|
7
|
-
const entries: (obj: { [key: string]: any }) => [string, any][] = require('lodash/toPairs')
|
|
8
|
-
const cloneDeep = require('lodash/cloneDeep')
|
|
9
|
-
|
|
10
6
|
type ExtractedVariables = Record<string, (Variable & { name: string; update: (index?: number) => string })[]>
|
|
11
7
|
type Variable = { type: any; value: any }
|
|
12
8
|
|
|
@@ -31,14 +27,17 @@ export function jsonToGraphQLQuery({
|
|
|
31
27
|
parentType: kind === 'query' ? typesTree.Query : typesTree.Mutation,
|
|
32
28
|
})
|
|
33
29
|
|
|
34
|
-
const variableItems = Object.values(variablesData).reduce(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
const variableItems = Object.values(variablesData).reduce(
|
|
31
|
+
(variablesObj, variables) => {
|
|
32
|
+
variables.forEach((variable, index) => {
|
|
33
|
+
const name = variable.update(variables.length > 1 ? index : undefined)
|
|
34
|
+
variablesObj[name] = { type: variable.type, value: variable.value }
|
|
35
|
+
})
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
return variablesObj
|
|
38
|
+
},
|
|
39
|
+
{} as Record<string, Variable>
|
|
40
|
+
)
|
|
42
41
|
|
|
43
42
|
const variablesQuery = Object.keys(variableItems).length
|
|
44
43
|
? `(${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
|
|