@avantstay/graphql-ts-client 11.0.0-beta.1 → 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.
@@ -29,7 +29,7 @@ __export(exports, {
29
29
  client: () => client,
30
30
  default: () => stdin_default
31
31
  });
32
- var import_endpoint = __toModule(require("../dist/endpoint"));
32
+ var import_endpoint = __toModule(require("./endpoint"));
33
33
  const formatGraphQL = (query) => query;
34
34
  const typesTree = {};
35
35
  let verbose = false;
@@ -76,10 +76,62 @@ 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"
82
- import { Maybe, IResponseListener, Endpoint } from "../dist"
134
+ import { Maybe, IResponseListener, Endpoint } from "."
83
135
 
84
136
  // Scalars
85
137
  export type IDate = string | Date
@@ -119,6 +171,7 @@ export declare const client: {
119
171
  __headers?: { [key: string]: string }
120
172
  __retry?: boolean
121
173
  __alias?: string
174
+ __url?: string
122
175
  },
123
176
  string,
124
177
  AllEnums
@@ -30,15 +30,6 @@ export interface FailingQueryArgs {
30
30
 
31
31
  // Input/Output Types
32
32
 
33
- /**
34
- * @deprecated Avoid directly using this interface. Instead, create a type alias based on the query/mutation return type.
35
- */
36
-
37
- export interface Image {
38
- url?: string
39
- alt?: string
40
- }
41
-
42
33
  /**
43
34
  * @deprecated Avoid directly using this interface. Instead, create a type alias based on the query/mutation return type.
44
35
  */
@@ -48,7 +39,6 @@ export interface Book {
48
39
  author?: string
49
40
  type?: BookType
50
41
  dateCreated?: ISODate
51
- images?: Image[]
52
42
  }
53
43
 
54
44
  /**
@@ -83,17 +73,11 @@ export interface Query {
83
73
 
84
74
  // Selection Types
85
75
 
86
- export interface ImageSelection {
87
- url?: boolean
88
- alt?: boolean
89
- }
90
-
91
76
  export interface BookSelection {
92
77
  title?: boolean
93
78
  author?: boolean
94
79
  type?: boolean
95
80
  dateCreated?: boolean
96
- images?: ImageSelection
97
81
  }
98
82
 
99
83
  export interface BookSearchParamsAllOptionalSelection {
@@ -123,6 +107,7 @@ 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
@@ -132,6 +117,7 @@ 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[],
@@ -142,6 +128,7 @@ 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[],
@@ -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,
@@ -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,14 +58,7 @@ const typesTree = {
60
58
  }
61
59
  };
62
60
  },
63
- get booksWithoutParams() {
64
- return {
65
- __fields: typesTree.Book
66
- };
67
- }
68
- },
69
- Book: {
70
- images: {}
61
+ booksWithoutParams: {}
71
62
  }
72
63
  };
73
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: {
@@ -32,7 +28,10 @@ export const getApiEndpointCreator =
32
28
  headers: any
33
29
  status: any
34
30
  }> => {
31
+ const clientConfig = apiConfig.getClient()
32
+
35
33
  const alias = (jsonQuery as any)?.__alias ?? queryName
34
+ const url = (jsonQuery as any)?.__url ?? clientConfig.url
36
35
  const shouldRetry = (jsonQuery as any)?.__retry ?? true
37
36
  const requestHeaders = (jsonQuery as any)?.__headers ?? {}
38
37
  const { query, variables } = jsonToGraphQLQuery({ kind, queryName, jsonQuery, typesTree: apiConfig.typesTree })
@@ -58,11 +57,11 @@ export const getApiEndpointCreator =
58
57
  shouldRetry,
59
58
  failureMode,
60
59
  queryName: alias,
61
- client: apiConfig.getClient(),
60
+ client: { ...clientConfig, url },
62
61
  requestHeaders,
63
62
  query,
64
63
  variables,
65
- errorsParser: apiConfig.errorsParser
64
+ errorsParser: apiConfig.errorsParser,
66
65
  })
67
66
 
68
67
  const response = { data, warnings, headers, status, errors }
@@ -75,7 +74,7 @@ export const getApiEndpointCreator =
75
74
  })
76
75
  }
77
76
 
78
- executeListeners(apiConfig.responseListeners,{
77
+ executeListeners(apiConfig.responseListeners, {
79
78
  ...responseListenerData,
80
79
  response,
81
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,9 +15,8 @@ import {
15
15
  IntrospectionObjectType,
16
16
  IntrospectionOutputTypeRef,
17
17
  IntrospectionType,
18
- Source,
19
18
  } from 'graphql'
20
- import { kebabCase } from 'lodash'
19
+ import kebabCase from 'lodash/kebabCase'
21
20
  import orderBy from 'lodash/orderBy'
22
21
  import set from 'lodash/set'
23
22
  import md5 from 'md5'
@@ -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
 
@@ -108,7 +108,7 @@ function gqlFieldToTypescript(
108
108
  })
109
109
  )
110
110
 
111
- fieldTypeDefinition = `{ __headers?: {[key: string]: string}; __retry?: boolean; __alias?: string; __args${
111
+ fieldTypeDefinition = `{ __headers?: {[key: string]: string}; __retry?: boolean; __alias?: string; __url?: string; __args${
112
112
  fieldsOnArgs.every(arg => arg.isOptional) ? '?' : ''
113
113
  }: { ${fieldsOnArgs.map(arg => arg.code).join(', ')} }}${fieldTypeDefinition ? ` & ${fieldTypeDefinition}` : ''}`
114
114
  }
@@ -148,6 +148,7 @@ function gqlEndpointToCode(kind: 'mutation' | 'query', endpoint: IntrospectionFi
148
148
  __headers?: {[key: string]: string};
149
149
  __retry?: boolean;
150
150
  __alias?: string;
151
+ __url?: string;
151
152
  ${argsType ? `__args${argsType.optional ? '?' : ''}: ${argsType.alias}` : ''}
152
153
  }${selectionType ? ` & ${selectionType}` : ''}`
153
154
 
@@ -313,7 +314,7 @@ type IClientOptions = {
313
314
  errorsParser?: (errors: any[]) => any
314
315
  }
315
316
 
316
- type FetchIntrospectionOptions = Omit< IClientOptions, 'output' | 'introspectionEndpoint'>
317
+ type FetchIntrospectionOptions = Omit<IClientOptions, 'output' | 'introspectionEndpoint'>
317
318
 
318
319
  function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Omit<IClientOptions, 'output'>) {
319
320
  const typesHash = md5(`${JSON.stringify(options)}__${JSON.stringify(types)}`)
@@ -321,7 +322,11 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
321
322
  const clientCacheFilePath = path.resolve(tempDir, clientCacheFileName)
322
323
 
323
324
  if (!options.skipCache && fs.existsSync(clientCacheFilePath)) {
324
- return JSON.parse(fs.readFileSync(clientCacheFilePath, { encoding: 'utf8' }))
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
+ }
325
330
  }
326
331
 
327
332
  const queries = (<IntrospectionObjectType>types.find(it => it.name === 'Query'))?.fields || []
@@ -471,8 +476,9 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
471
476
 
472
477
  export default ${clientName}`
473
478
 
474
- const output = {
479
+ const output: TypescriptClientOutput = {
475
480
  js: esbuild.transformSync(jsCode, { format: 'cjs', loader: 'js' }).code,
481
+ mjs: esbuild.transformSync(jsCode, { format: 'esm', loader: 'js' }).code,
476
482
  typings: prettier.format(typingsCode, { semi: false, parser: 'typescript' }),
477
483
  }
478
484
 
@@ -497,17 +503,18 @@ async function fetchIntrospection({ endpoint, headers }: FetchIntrospectionOptio
497
503
  'Content-Type': 'application/json',
498
504
  ...headers,
499
505
  },
506
+ timeout: 5000,
500
507
  }
501
508
  )
502
- .catch(() => {
503
- const errorMessage = `The GraphQL introspection request failed (${endpoint})`
509
+ .catch(e => {
504
510
  if (fs.existsSync(introspectionCacheFilePath)) {
505
511
  const cachedSchema = JSON.parse(fs.readFileSync(introspectionCacheFilePath, { encoding: 'utf8' }))
506
512
  loadedFromCache = true
507
- console.warn(`Successfully restored from local cache.`)
513
+ console.warn(`Successfully restored (${endpoint}) from local cache.`)
508
514
  return { data: cachedSchema }
509
515
  } else {
510
- return Promise.reject(errorMessage)
516
+ console.error(e)
517
+ return Promise.reject(`The GraphQL introspection request failed (${endpoint})`)
511
518
  }
512
519
  })
513
520
 
@@ -524,10 +531,11 @@ async function fetchIntrospection({ endpoint, headers }: FetchIntrospectionOptio
524
531
  return types
525
532
  }
526
533
 
527
- type Client = { typings: string; js: string }
528
-
529
- function generateClient(introspectionTypes: ReadonlyArray<IntrospectionType>, {output,...restOptions}: IClientOptions): Client {
530
- const { js, typings } = generateClientCode(introspectionTypes, restOptions)
534
+ function generateClient(
535
+ introspectionTypes: ReadonlyArray<IntrospectionType>,
536
+ { output, ...restOptions }: IClientOptions
537
+ ): TypescriptClientOutput {
538
+ const { js, mjs, typings } = generateClientCode(introspectionTypes, restOptions)
531
539
 
532
540
  if (output && typeof output === 'string') {
533
541
  const outputDir = path.dirname(output)
@@ -538,12 +546,16 @@ function generateClient(introspectionTypes: ReadonlyArray<IntrospectionType>, {o
538
546
 
539
547
  fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.d.ts'), typings, { encoding: 'utf8' })
540
548
  fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.js'), js, { encoding: 'utf8' })
549
+ fs.writeFileSync(output.replace(/(\.(ts|js))?$/, '.mjs'), mjs, { encoding: 'utf8' })
541
550
  }
542
551
 
543
- return { js, typings }
552
+ return { js, mjs, typings }
544
553
  }
545
554
 
546
- export async function generateTypescriptClient({ introspectionEndpoint, ...options }: IClientOptions): Promise<Client> {
555
+ export async function generateTypescriptClient({
556
+ introspectionEndpoint,
557
+ ...options
558
+ }: IClientOptions): Promise<TypescriptClientOutput> {
547
559
  console.log(`Generating TypeScript client (name: ${options.clientName ?? 'n/a'})`)
548
560
 
549
561
  axiosRetry(axios, { retries: 5, retryDelay: retryCount => 1000 * 2 ** retryCount })
@@ -554,14 +566,17 @@ export async function generateTypescriptClient({ introspectionEndpoint, ...optio
554
566
  })
555
567
 
556
568
  return generateClient(introspectionTypes, options)
557
-
558
569
  }
559
570
 
560
- export function generateTypescriptClientFromSDL(SDL: string, options: IClientOptions): Client {
561
- 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'})`)
562
573
 
563
- const graphqlSchemaObj = buildSchema(SDL);
564
- const introspectionTypes = graphqlSync(graphqlSchemaObj, new Source(getIntrospectionQuery())).data?.__schema.types as IntrospectionType[]
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[]
565
580
 
566
581
  return generateClient(introspectionTypes, options)
567
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',
@@ -23,7 +23,7 @@ export function jsonToGraphQLQuery({
23
23
  }) {
24
24
  const variablesData = {} as ExtractedVariables
25
25
  const alias = jsonQuery.__alias
26
- const newJsonQuery = cloneDeep(omit(jsonQuery, ['__alias', '__headers']))
26
+ const newJsonQuery = cloneDeep(omit(jsonQuery, ['__alias', '__headers', '__url']))
27
27
 
28
28
  extractVariables({
29
29
  jsonQuery: { [queryName]: newJsonQuery },
@@ -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((variablesObj, variables) => {
35
- variables.forEach((variable, index) => {
36
- const name = variable.update(variables.length > 1 ? index : undefined)
37
- variablesObj[name] = { type: variable.type, value: variable.value }
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
- return variablesObj
41
- }, {} as Record<string, Variable>)
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> = Base extends Array<any>
28
- ? ArrayElement<Base> extends Primitive | E
29
- ? ArrayElement<Base>[]
30
- : Projection<Defined<Selection>, ArrayElement<Base>, E>[]
31
- : Base extends Primitive | E
32
- ? // Is primitive and extends undefined
33
- Selection extends undefined
34
- ? Base | undefined
35
- : Base
36
- : {
37
- [k in keyof Selection & keyof Base]: Selection[k] extends boolean
38
- ? Base[k]
39
- : Base[k] extends Array<infer A>
40
- ? Projection<Defined<Selection[k]>, A, E>[]
41
- : Projection<Defined<Selection[k]>, Base[k], E>
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
- ? U
48
- : T extends Promise<infer U>
49
- ? U
50
- : T
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
- ? DeepReplace<T[P], Ignore, M>
63
- : T[P]
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