@avantstay/graphql-ts-client 12.0.2 → 12.2.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.
@@ -1,7 +1,8 @@
1
- import { C as ClientConfig, I as IResponseListener, E as Endpoint } from './types-23d2d6aa.js';
1
+ import { C as ClientConfig, I as IRequestListener, c as IResponseListener, E as Endpoint } from './types-c6f94d0b.js';
2
2
 
3
3
  declare const getApiEndpointCreator: (apiConfig: {
4
4
  getClient: () => ClientConfig;
5
+ requestListeners: IRequestListener[];
5
6
  responseListeners: IResponseListener[];
6
7
  typesTree: any;
7
8
  maxAge: number;
package/dist/endpoint.js CHANGED
@@ -724,13 +724,11 @@ var getApiEndpointCreator = function(apiConfig) {
724
724
  return function(kind, queryName) {
725
725
  var rawEndpoint = function() {
726
726
  var _ref = _asyncToGenerator(function(failureMode, jsonQuery) {
727
- var clientConfig, _jsonQuery___alias, alias, _jsonQuery___url, url, _jsonQuery___retry, shouldRetry, _jsonQuery___headers, requestHeaders, _jsonToGraphQLQuery, query, variables, start, logOptions, responseListenerData, _ref, data, errors, warnings, headers, status, response, error;
727
+ var _jsonQuery___alias, alias, _jsonQuery___retry, shouldRetry, _jsonQuery___headers, requestHeaders, _jsonToGraphQLQuery, query, variables, start, logOptions, listenerData, clientConfig, _jsonQuery___url, url, _ref, data, errors, warnings, headers, status, response, error;
728
728
  return __generator(this, function(_state) {
729
729
  switch(_state.label){
730
730
  case 0:
731
- clientConfig = apiConfig.getClient();
732
731
  alias = (_jsonQuery___alias = jsonQuery === null || jsonQuery === void 0 ? void 0 : jsonQuery.__alias) !== null && _jsonQuery___alias !== void 0 ? _jsonQuery___alias : queryName;
733
- url = (_jsonQuery___url = jsonQuery === null || jsonQuery === void 0 ? void 0 : jsonQuery.__url) !== null && _jsonQuery___url !== void 0 ? _jsonQuery___url : clientConfig.url;
734
732
  shouldRetry = (_jsonQuery___retry = jsonQuery === null || jsonQuery === void 0 ? void 0 : jsonQuery.__retry) !== null && _jsonQuery___retry !== void 0 ? _jsonQuery___retry : true;
735
733
  requestHeaders = (_jsonQuery___headers = jsonQuery === null || jsonQuery === void 0 ? void 0 : jsonQuery.__headers) !== null && _jsonQuery___headers !== void 0 ? _jsonQuery___headers : {};
736
734
  _jsonToGraphQLQuery = jsonToGraphQLQuery({
@@ -748,18 +746,29 @@ var getApiEndpointCreator = function(apiConfig) {
748
746
  query: query,
749
747
  variables: variables
750
748
  };
751
- responseListenerData = {
749
+ listenerData = {
752
750
  queryName: alias,
753
751
  query: apiConfig.formatGraphQL(query),
754
- variables: variables
752
+ variables: variables,
753
+ headers: _objectSpread({}, requestHeaders)
755
754
  };
756
- _state.label = 1;
755
+ return [
756
+ 4,
757
+ Promise.all(apiConfig.requestListeners.map(function(listener) {
758
+ return listener(listenerData);
759
+ }))
760
+ ];
757
761
  case 1:
762
+ _state.sent();
763
+ clientConfig = apiConfig.getClient();
764
+ url = (_jsonQuery___url = jsonQuery === null || jsonQuery === void 0 ? void 0 : jsonQuery.__url) !== null && _jsonQuery___url !== void 0 ? _jsonQuery___url : clientConfig.url;
765
+ _state.label = 2;
766
+ case 2:
758
767
  _state.trys.push([
759
- 1,
760
- 3,
768
+ 2,
769
+ 4,
761
770
  ,
762
- 4
771
+ 5
763
772
  ]);
764
773
  return [
765
774
  4,
@@ -770,13 +779,13 @@ var getApiEndpointCreator = function(apiConfig) {
770
779
  client: _objectSpreadProps(_objectSpread({}, clientConfig), {
771
780
  url: url
772
781
  }),
773
- requestHeaders: requestHeaders,
782
+ requestHeaders: listenerData.headers,
774
783
  query: query,
775
784
  variables: variables,
776
785
  errorsParser: apiConfig.errorsParser
777
786
  })
778
787
  ];
779
- case 2:
788
+ case 3:
780
789
  _ref = _state.sent(), data = _ref.data, errors = _ref.errors, warnings = _ref.warnings, headers = _ref.headers, status = _ref.status;
781
790
  response = {
782
791
  data: data,
@@ -791,7 +800,7 @@ var getApiEndpointCreator = function(apiConfig) {
791
800
  duration: +new Date() - start
792
801
  }));
793
802
  }
794
- executeListeners(apiConfig.responseListeners, _objectSpreadProps(_objectSpread({}, responseListenerData), {
803
+ executeListeners(apiConfig.responseListeners, _objectSpreadProps(_objectSpread({}, listenerData), {
795
804
  response: response
796
805
  }));
797
806
  return [
@@ -804,7 +813,7 @@ var getApiEndpointCreator = function(apiConfig) {
804
813
  status: status
805
814
  }
806
815
  ];
807
- case 3:
816
+ case 4:
808
817
  error = _state.sent();
809
818
  if (apiConfig.verbose && globalThis.document) {
810
819
  logRequest(_objectSpreadProps(_objectSpread({}, logOptions), {
@@ -812,11 +821,11 @@ var getApiEndpointCreator = function(apiConfig) {
812
821
  duration: +new Date() - start
813
822
  }));
814
823
  }
815
- executeListeners(apiConfig.responseListeners, _objectSpreadProps(_objectSpread({}, responseListenerData), {
824
+ executeListeners(apiConfig.responseListeners, _objectSpreadProps(_objectSpread({}, listenerData), {
816
825
  response: error.response
817
826
  }));
818
827
  throw error;
819
- case 4:
828
+ case 5:
820
829
  return [
821
830
  2
822
831
  ];
package/dist/endpoint.mjs CHANGED
@@ -197,9 +197,7 @@ function logRequest(logInfo) {
197
197
  var executeListeners = (listeners, data) => setTimeout(() => listeners.forEach((runResponseListener) => runResponseListener(data)));
198
198
  var getApiEndpointCreator = (apiConfig) => (kind, queryName) => {
199
199
  const rawEndpoint = async (failureMode, jsonQuery) => {
200
- const clientConfig = apiConfig.getClient();
201
200
  const alias = jsonQuery?.__alias ?? queryName;
202
- const url = jsonQuery?.__url ?? clientConfig.url;
203
201
  const shouldRetry = jsonQuery?.__retry ?? true;
204
202
  const requestHeaders = jsonQuery?.__headers ?? {};
205
203
  const { query, variables } = jsonToGraphQLQuery({ kind, queryName, jsonQuery, typesTree: apiConfig.typesTree });
@@ -212,18 +210,22 @@ var getApiEndpointCreator = (apiConfig) => (kind, queryName) => {
212
210
  query,
213
211
  variables
214
212
  };
215
- const responseListenerData = {
213
+ const listenerData = {
216
214
  queryName: alias,
217
215
  query: apiConfig.formatGraphQL(query),
218
- variables
216
+ variables,
217
+ headers: { ...requestHeaders }
219
218
  };
219
+ await Promise.all(apiConfig.requestListeners.map((listener) => listener(listenerData)));
220
+ const clientConfig = apiConfig.getClient();
221
+ const url = jsonQuery?.__url ?? clientConfig.url;
220
222
  try {
221
223
  const { data, errors, warnings, headers, status } = await graphqlRequest({
222
224
  shouldRetry,
223
225
  failureMode,
224
226
  queryName: alias,
225
227
  client: { ...clientConfig, url },
226
- requestHeaders,
228
+ requestHeaders: listenerData.headers,
227
229
  query,
228
230
  variables,
229
231
  errorsParser: apiConfig.errorsParser
@@ -237,7 +239,7 @@ var getApiEndpointCreator = (apiConfig) => (kind, queryName) => {
237
239
  });
238
240
  }
239
241
  executeListeners(apiConfig.responseListeners, {
240
- ...responseListenerData,
242
+ ...listenerData,
241
243
  response
242
244
  });
243
245
  return { data: data?.[alias], errors, warnings, headers, status };
@@ -250,7 +252,7 @@ var getApiEndpointCreator = (apiConfig) => (kind, queryName) => {
250
252
  });
251
253
  }
252
254
  executeListeners(apiConfig.responseListeners, {
253
- ...responseListenerData,
255
+ ...listenerData,
254
256
  response: error.response
255
257
  });
256
258
  throw error;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { PathLike } from 'fs';
2
- import { T as TypescriptClientOutput } from './types-23d2d6aa.js';
3
- export { C as ClientConfig, c as DeepReplace, D as Defined, E as Endpoint, G as GraphQLClientError, I as IResponseListener, J as JsonOutput, L as LogInfo, M as Maybe, P as Projection, d as RawEndpoint, b as Replacement, R as ResponseData, a as ResponseListenerInfo, T as TypescriptClientOutput, U as Unpacked } from './types-23d2d6aa.js';
2
+ import { T as TypescriptClientOutput } from './types-c6f94d0b.js';
3
+ export { C as ClientConfig, e as DeepReplace, D as Defined, E as Endpoint, G as GraphQLClientError, I as IRequestListener, c as IResponseListener, J as JsonOutput, L as LogInfo, M as Maybe, P as Projection, f as RawEndpoint, d as Replacement, a as RequestListenerInfo, R as ResponseData, b as ResponseListenerInfo, T as TypescriptClientOutput, U as Unpacked } from './types-c6f94d0b.js';
4
4
 
5
5
  type IClientOptions = {
6
6
  output?: PathLike;
package/dist/index.js CHANGED
@@ -487,7 +487,7 @@ var prettier = __toESM(require("prettier"));
487
487
  // package.json
488
488
  var package_default = {
489
489
  name: "@avantstay/graphql-ts-client",
490
- version: "12.0.2",
490
+ version: "12.2.0",
491
491
  description: "GraphQL Typescript Client Generator",
492
492
  homepage: "https://github.com/avantstay/graphql-ts-client",
493
493
  bugs: {
@@ -810,12 +810,12 @@ function generateClientCode(types, options) {
810
810
  selection: false,
811
811
  outputType: "js"
812
812
  });
813
- }).join("\n"), "\n\n // Schema Resolution Tree\n ").concat(getTypesTreeCode(forInputExtraction), "\n\n let verbose = ").concat(Boolean(options.verbose), "\n let headers = {}\n let url = '").concat(options.endpoint, "'\n let retryConfig = {\n max: 0,\n before: undefined,\n waitBeforeRetry: 0\n }\n let responseListeners = []\n let errorsParser = ").concat(options.errorsParser, "\n // noinspection JSUnusedLocalSymbols\n let apiEndpoint = getApiEndpointCreator({\n getClient: () => ({ url, headers, retryConfig }),\n responseListeners,\n maxAge: 30000,\n verbose,\n typesTree,\n formatGraphQL,\n errorsParser\n })\n\n export const ").concat(clientName, " = {\n addResponseListener: (listener) => responseListeners.push(\n listener),\n setHeader: (key, value) => {\n headers[key] = value\n },\n setHeaders: (newHeaders) => {\n headers = newHeaders\n },\n setRetryConfig: (options) => {\n if (!Number.isInteger(options.max) || options.max < 0) {\n throw new Error('retryOptions.max should be a non-negative integer')\n }\n \n retryConfig = { \n max: options.max,\n waitBeforeRetry: options.waitBeforeRetry,\n before: options.before \n }\n },\n setUrl: (_url) => url = _url,\n queries: {\n ").concat(queries.map(function(query) {
813
+ }).join("\n"), "\n\n // Schema Resolution Tree\n ").concat(getTypesTreeCode(forInputExtraction), "\n\n let verbose = ").concat(Boolean(options.verbose), "\n let headers = {}\n let url = '").concat(options.endpoint, "'\n let retryConfig = {\n max: 0,\n before: undefined,\n waitBeforeRetry: 0\n }\n let requestListeners = []\n let responseListeners = []\n let errorsParser = ").concat(options.errorsParser, "\n // noinspection JSUnusedLocalSymbols\n let apiEndpoint = getApiEndpointCreator({\n getClient: () => ({ url, headers, retryConfig }),\n requestListeners,\n responseListeners,\n maxAge: 30000,\n verbose,\n typesTree,\n formatGraphQL,\n errorsParser\n })\n\n export const ").concat(clientName, " = {\n addRequestListener: (listener) => {\n requestListeners.push(listener)\n return () => {\n const index = requestListeners.indexOf(listener)\n if (index > -1) requestListeners.splice(index, 1)\n }\n },\n removeRequestListener: (listener) => {\n const index = requestListeners.indexOf(listener)\n if (index > -1) requestListeners.splice(index, 1)\n },\n addResponseListener: (listener) => {\n responseListeners.push(listener)\n return () => {\n const index = responseListeners.indexOf(listener)\n if (index > -1) responseListeners.splice(index, 1)\n }\n },\n removeResponseListener: (listener) => {\n const index = responseListeners.indexOf(listener)\n if (index > -1) responseListeners.splice(index, 1)\n },\n setHeader: (key, value) => {\n headers[key] = value\n },\n setHeaders: (newHeaders) => {\n headers = newHeaders\n },\n setRetryConfig: (options) => {\n if (!Number.isInteger(options.max) || options.max < 0) {\n throw new Error('retryOptions.max should be a non-negative integer')\n }\n \n retryConfig = { \n max: options.max,\n waitBeforeRetry: options.waitBeforeRetry,\n before: options.before \n }\n },\n setUrl: (_url) => url = _url,\n queries: {\n ").concat(queries.map(function(query) {
814
814
  return gqlEndpointToCode("query", query, "js");
815
815
  }).join(",\n"), "\n },\n mutations: {\n ").concat(mutations.map(function(mutation) {
816
816
  return gqlEndpointToCode("mutation", mutation, "js");
817
817
  }).join(",\n"), "\n }\n }\n\n export default ").concat(clientName);
818
- var typingsCode = "\n // noinspection TypeScriptUnresolvedVariable, ES6UnusedImports, JSUnusedLocalSymbols, TypeScriptCheckImport\n import { DeepRequired } from 'ts-essentials'\n import { Maybe, IResponseListener, Endpoint } from '".concat(graphqlTsClientPath, "'\n\n // Scalars\n export type IDate = string | Date\n ").concat(scalars.map(function(it) {
818
+ var typingsCode = "\n // noinspection TypeScriptUnresolvedVariable, ES6UnusedImports, JSUnusedLocalSymbols, TypeScriptCheckImport\n import { DeepRequired } from 'ts-essentials'\n import { Maybe, IRequestListener, IResponseListener, Endpoint } from '".concat(graphqlTsClientPath, "'\n\n // Scalars\n export type IDate = string | Date\n ").concat(scalars.map(function(it) {
819
819
  return gqlSchemaToCode(it, {
820
820
  selection: false,
821
821
  outputType: "ts"
@@ -842,7 +842,7 @@ function generateClientCode(types, options) {
842
842
  selection: true,
843
843
  outputType: "ts"
844
844
  });
845
- }).join("\n"), "\n \n export declare const ").concat(clientName, ": {\n addResponseListener: (listener: IResponseListener) => void\n setHeader: (key: string, value: string) => void\n setHeaders: (newHeaders: { [k: string]: string }) => void,\n setUrl: (url: string) => void,\n setRetryConfig: (options: { max: number, waitBeforeRetry?: number, before?: IResponseListener }) => void\n queries: {\n ").concat(queries.map(function(q) {
845
+ }).join("\n"), "\n \n export declare const ").concat(clientName, ": {\n addRequestListener: (listener: IRequestListener) => () => void\n removeRequestListener: (listener: IRequestListener) => void\n addResponseListener: (listener: IResponseListener) => () => void\n removeResponseListener: (listener: IResponseListener) => void\n setHeader: (key: string, value: string) => void\n setHeaders: (newHeaders: { [k: string]: string }) => void,\n setUrl: (url: string) => void,\n setRetryConfig: (options: { max: number, waitBeforeRetry?: number, before?: IResponseListener }) => void\n queries: {\n ").concat(queries.map(function(q) {
846
846
  return gqlEndpointToCode("query", q, "ts");
847
847
  }).join(",\n"), "\n },\n mutations: {\n ").concat(mutations.map(function(q) {
848
848
  return gqlEndpointToCode("mutation", q, "ts");
package/dist/index.mjs CHANGED
@@ -24,7 +24,7 @@ import * as prettier from "prettier";
24
24
  // package.json
25
25
  var package_default = {
26
26
  name: "@avantstay/graphql-ts-client",
27
- version: "12.0.2",
27
+ version: "12.2.0",
28
28
  description: "GraphQL Typescript Client Generator",
29
29
  homepage: "https://github.com/avantstay/graphql-ts-client",
30
30
  bugs: {
@@ -355,11 +355,13 @@ function generateClientCode(types, options) {
355
355
  before: undefined,
356
356
  waitBeforeRetry: 0
357
357
  }
358
+ let requestListeners = []
358
359
  let responseListeners = []
359
360
  let errorsParser = ${options.errorsParser}
360
361
  // noinspection JSUnusedLocalSymbols
361
362
  let apiEndpoint = getApiEndpointCreator({
362
363
  getClient: () => ({ url, headers, retryConfig }),
364
+ requestListeners,
363
365
  responseListeners,
364
366
  maxAge: 30000,
365
367
  verbose,
@@ -369,8 +371,28 @@ function generateClientCode(types, options) {
369
371
  })
370
372
 
371
373
  export const ${clientName} = {
372
- addResponseListener: (listener) => responseListeners.push(
373
- listener),
374
+ addRequestListener: (listener) => {
375
+ requestListeners.push(listener)
376
+ return () => {
377
+ const index = requestListeners.indexOf(listener)
378
+ if (index > -1) requestListeners.splice(index, 1)
379
+ }
380
+ },
381
+ removeRequestListener: (listener) => {
382
+ const index = requestListeners.indexOf(listener)
383
+ if (index > -1) requestListeners.splice(index, 1)
384
+ },
385
+ addResponseListener: (listener) => {
386
+ responseListeners.push(listener)
387
+ return () => {
388
+ const index = responseListeners.indexOf(listener)
389
+ if (index > -1) responseListeners.splice(index, 1)
390
+ }
391
+ },
392
+ removeResponseListener: (listener) => {
393
+ const index = responseListeners.indexOf(listener)
394
+ if (index > -1) responseListeners.splice(index, 1)
395
+ },
374
396
  setHeader: (key, value) => {
375
397
  headers[key] = value
376
398
  },
@@ -401,7 +423,7 @@ function generateClientCode(types, options) {
401
423
  const typingsCode = `
402
424
  // noinspection TypeScriptUnresolvedVariable, ES6UnusedImports, JSUnusedLocalSymbols, TypeScriptCheckImport
403
425
  import { DeepRequired } from 'ts-essentials'
404
- import { Maybe, IResponseListener, Endpoint } from '${graphqlTsClientPath}'
426
+ import { Maybe, IRequestListener, IResponseListener, Endpoint } from '${graphqlTsClientPath}'
405
427
 
406
428
  // Scalars
407
429
  export type IDate = string | Date
@@ -431,7 +453,10 @@ function generateClientCode(types, options) {
431
453
  ${objectTypes.filter((it) => it.name !== "Query").map((it) => gqlSchemaToCode(it, { selection: true, outputType: "ts" })).join("\n")}
432
454
 
433
455
  export declare const ${clientName}: {
434
- addResponseListener: (listener: IResponseListener) => void
456
+ addRequestListener: (listener: IRequestListener) => () => void
457
+ removeRequestListener: (listener: IRequestListener) => void
458
+ addResponseListener: (listener: IResponseListener) => () => void
459
+ removeResponseListener: (listener: IResponseListener) => void
435
460
  setHeader: (key: string, value: string) => void
436
461
  setHeaders: (newHeaders: { [k: string]: string }) => void,
437
462
  setUrl: (url: string) => void,
@@ -9,6 +9,12 @@ type ResponseData = {
9
9
  message: string;
10
10
  }[];
11
11
  };
12
+ type RequestListenerInfo = {
13
+ queryName: string;
14
+ query: string;
15
+ variables: any;
16
+ };
17
+ type IRequestListener = (info: RequestListenerInfo) => void | Promise<void>;
12
18
  type ResponseListenerInfo = {
13
19
  queryName: string;
14
20
  query: string;
@@ -17,7 +23,8 @@ type ResponseListenerInfo = {
17
23
  };
18
24
  type IResponseListener = (info: ResponseListenerInfo) => void | Promise<void>;
19
25
  type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
20
- type Projection<Selection, Base, E = never> = Base extends Array<any> ? ArrayElement<Base> extends Date | string | number | boolean | null | undefined | E ? ArrayElement<Base>[] : Projection<Defined<Selection>, ArrayElement<Base>, E>[] : Base extends Date | string | number | boolean | null | E ? Selection extends undefined ? Base | undefined : Base : {
26
+ type Primitive = Date | string | number | boolean | null | undefined;
27
+ type Projection<Selection, Base, E = never> = Base extends Array<any> ? ArrayElement<Base> extends Primitive | E ? ArrayElement<Base>[] : Projection<Defined<Selection>, ArrayElement<Base>, E>[] : Base extends Primitive | E ? Selection extends undefined ? Base | undefined : Base : {
21
28
  [k in keyof Selection & keyof Base]: Selection[k] extends boolean ? Base[k] : Base[k] extends Array<infer A> ? Projection<Defined<Selection[k]>, A, E>[] : Projection<Defined<Selection[k]>, Base[k], E>;
22
29
  };
23
30
  type Unpacked<T> = T extends (infer U)[] ? U : T extends (...args: any[]) => infer U ? U : T extends Promise<infer U> ? U : T;
@@ -59,6 +66,11 @@ type LogInfo = {
59
66
  error?: Error;
60
67
  duration: number;
61
68
  };
69
+ type TypescriptClientOutput = {
70
+ js: string;
71
+ mjs: string;
72
+ typings: string;
73
+ };
62
74
  declare class GraphQLClientError extends Error {
63
75
  responseData: ResponseData;
64
76
  constructor(responseData: ResponseData);
@@ -66,4 +78,4 @@ declare class GraphQLClientError extends Error {
66
78
  get response(): ResponseData;
67
79
  }
68
80
 
69
- export { ClientConfig as C, Defined as D, Endpoint as E, GraphQLClientError as G, IResponseListener as I, JsonOutput as J, LogInfo as L, Maybe as M, Projection as P, ResponseData as R, Unpacked as U, ResponseListenerInfo as a, Replacement as b, DeepReplace as c, RawEndpoint as d };
81
+ export { ClientConfig as C, Defined as D, Endpoint as E, GraphQLClientError as G, IRequestListener as I, JsonOutput as J, LogInfo as L, Maybe as M, Projection as P, ResponseData as R, TypescriptClientOutput as T, Unpacked as U, RequestListenerInfo as a, ResponseListenerInfo as b, IResponseListener as c, Replacement as d, DeepReplace as e, RawEndpoint as f };
@@ -0,0 +1,85 @@
1
+ type Maybe<T> = null | undefined | T;
2
+ type Defined<T> = Exclude<T, undefined>;
3
+ type ResponseData = {
4
+ data: any;
5
+ warnings: any;
6
+ headers: any;
7
+ status?: number;
8
+ errors: {
9
+ message: string;
10
+ }[];
11
+ };
12
+ type RequestListenerInfo = {
13
+ url: string;
14
+ queryName: string;
15
+ query: string;
16
+ variables: any;
17
+ headers: {
18
+ [key: string]: string;
19
+ };
20
+ };
21
+ type IRequestListener = (info: RequestListenerInfo) => void | Promise<void>;
22
+ type ResponseListenerInfo = {
23
+ queryName: string;
24
+ query: string;
25
+ variables: any;
26
+ response: ResponseData;
27
+ };
28
+ type IResponseListener = (info: ResponseListenerInfo) => void | Promise<void>;
29
+ type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
30
+ type Primitive = Date | string | number | boolean | null | undefined;
31
+ type Projection<Selection, Base, E = never> = Base extends Array<any> ? ArrayElement<Base> extends Primitive | E ? ArrayElement<Base>[] : Projection<Defined<Selection>, ArrayElement<Base>, E>[] : Base extends Primitive | E ? Selection extends undefined ? Base | undefined : Base : {
32
+ [k in keyof Selection & keyof Base]: Selection[k] extends boolean ? Base[k] : Base[k] extends Array<infer A> ? Projection<Defined<Selection[k]>, A, E>[] : Projection<Defined<Selection[k]>, Base[k], E>;
33
+ };
34
+ type Unpacked<T> = T extends (infer U)[] ? U : T extends (...args: any[]) => infer U ? U : T extends Promise<infer U> ? U : T;
35
+ type Replacement<M extends [any, any], T> = M extends any ? ([T] extends [M[0]] ? M[1] : never) : never;
36
+ type DeepReplace<T, Ignore, M extends [any, any]> = {
37
+ [P in keyof T]: T[P] extends M[0] ? T[P] extends Ignore ? T[P] extends object ? DeepReplace<T[P], Ignore, M> : T[P] : Replacement<M, T[P]> : T[P] extends object ? DeepReplace<T[P], Ignore, M> : T[P];
38
+ };
39
+ type RawEndpoint<I, O, E> = <S extends I>(jsonQuery?: S) => Promise<{
40
+ data: Projection<S, O, E>;
41
+ errors: any[];
42
+ warnings: any[];
43
+ headers: any;
44
+ status: any;
45
+ }>;
46
+ type JsonOutput<O, ToBeIgnored> = DeepReplace<O, ToBeIgnored, [string | Date, string]>;
47
+ type Endpoint<I, O, E> = (<S extends I>(jsonQuery?: S) => Promise<Projection<S, JsonOutput<O, E>, E>>) & {
48
+ memo: <S extends I>(jsonQuery?: S) => Promise<Projection<S, JsonOutput<O, E>, E>>;
49
+ memoRaw: RawEndpoint<I, JsonOutput<O, E>, E>;
50
+ raw: RawEndpoint<I, JsonOutput<O, E>, E>;
51
+ };
52
+ type ClientConfig = {
53
+ url: string;
54
+ headers: {
55
+ [key: string]: string;
56
+ };
57
+ retryConfig: {
58
+ max: number;
59
+ waitBeforeRetry?: number;
60
+ before: IResponseListener;
61
+ };
62
+ };
63
+ type LogInfo = {
64
+ query: string;
65
+ variables: any;
66
+ formatGraphQL: any;
67
+ kind: string;
68
+ queryName: string;
69
+ response?: any;
70
+ error?: Error;
71
+ duration: number;
72
+ };
73
+ type TypescriptClientOutput = {
74
+ js: string;
75
+ mjs: string;
76
+ typings: string;
77
+ };
78
+ declare class GraphQLClientError extends Error {
79
+ responseData: ResponseData;
80
+ constructor(responseData: ResponseData);
81
+ get message(): string;
82
+ get response(): ResponseData;
83
+ }
84
+
85
+ export { ClientConfig as C, Defined as D, Endpoint as E, GraphQLClientError as G, IRequestListener as I, JsonOutput as J, LogInfo as L, Maybe as M, Projection as P, ResponseData as R, TypescriptClientOutput as T, Unpacked as U, RequestListenerInfo as a, ResponseListenerInfo as b, IResponseListener as c, Replacement as d, DeepReplace as e, RawEndpoint as f };
@@ -0,0 +1,84 @@
1
+ type Maybe<T> = null | undefined | T;
2
+ type Defined<T> = Exclude<T, undefined>;
3
+ type ResponseData = {
4
+ data: any;
5
+ warnings: any;
6
+ headers: any;
7
+ status?: number;
8
+ errors: {
9
+ message: string;
10
+ }[];
11
+ };
12
+ type RequestListenerInfo = {
13
+ queryName: string;
14
+ query: string;
15
+ variables: any;
16
+ headers: {
17
+ [key: string]: string;
18
+ };
19
+ };
20
+ type IRequestListener = (info: RequestListenerInfo) => void | Promise<void>;
21
+ type ResponseListenerInfo = {
22
+ queryName: string;
23
+ query: string;
24
+ variables: any;
25
+ response: ResponseData;
26
+ };
27
+ type IResponseListener = (info: ResponseListenerInfo) => void | Promise<void>;
28
+ type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
29
+ type Primitive = Date | string | number | boolean | null | undefined;
30
+ type Projection<Selection, Base, E = never> = Base extends Array<any> ? ArrayElement<Base> extends Primitive | E ? ArrayElement<Base>[] : Projection<Defined<Selection>, ArrayElement<Base>, E>[] : Base extends Primitive | E ? Selection extends undefined ? Base | undefined : Base : {
31
+ [k in keyof Selection & keyof Base]: Selection[k] extends boolean ? Base[k] : Base[k] extends Array<infer A> ? Projection<Defined<Selection[k]>, A, E>[] : Projection<Defined<Selection[k]>, Base[k], E>;
32
+ };
33
+ type Unpacked<T> = T extends (infer U)[] ? U : T extends (...args: any[]) => infer U ? U : T extends Promise<infer U> ? U : T;
34
+ type Replacement<M extends [any, any], T> = M extends any ? ([T] extends [M[0]] ? M[1] : never) : never;
35
+ type DeepReplace<T, Ignore, M extends [any, any]> = {
36
+ [P in keyof T]: T[P] extends M[0] ? T[P] extends Ignore ? T[P] extends object ? DeepReplace<T[P], Ignore, M> : T[P] : Replacement<M, T[P]> : T[P] extends object ? DeepReplace<T[P], Ignore, M> : T[P];
37
+ };
38
+ type RawEndpoint<I, O, E> = <S extends I>(jsonQuery?: S) => Promise<{
39
+ data: Projection<S, O, E>;
40
+ errors: any[];
41
+ warnings: any[];
42
+ headers: any;
43
+ status: any;
44
+ }>;
45
+ type JsonOutput<O, ToBeIgnored> = DeepReplace<O, ToBeIgnored, [string | Date, string]>;
46
+ type Endpoint<I, O, E> = (<S extends I>(jsonQuery?: S) => Promise<Projection<S, JsonOutput<O, E>, E>>) & {
47
+ memo: <S extends I>(jsonQuery?: S) => Promise<Projection<S, JsonOutput<O, E>, E>>;
48
+ memoRaw: RawEndpoint<I, JsonOutput<O, E>, E>;
49
+ raw: RawEndpoint<I, JsonOutput<O, E>, E>;
50
+ };
51
+ type ClientConfig = {
52
+ url: string;
53
+ headers: {
54
+ [key: string]: string;
55
+ };
56
+ retryConfig: {
57
+ max: number;
58
+ waitBeforeRetry?: number;
59
+ before: IResponseListener;
60
+ };
61
+ };
62
+ type LogInfo = {
63
+ query: string;
64
+ variables: any;
65
+ formatGraphQL: any;
66
+ kind: string;
67
+ queryName: string;
68
+ response?: any;
69
+ error?: Error;
70
+ duration: number;
71
+ };
72
+ type TypescriptClientOutput = {
73
+ js: string;
74
+ mjs: string;
75
+ typings: string;
76
+ };
77
+ declare class GraphQLClientError extends Error {
78
+ responseData: ResponseData;
79
+ constructor(responseData: ResponseData);
80
+ get message(): string;
81
+ get response(): ResponseData;
82
+ }
83
+
84
+ export { ClientConfig as C, Defined as D, Endpoint as E, GraphQLClientError as G, IRequestListener as I, JsonOutput as J, LogInfo as L, Maybe as M, Projection as P, ResponseData as R, TypescriptClientOutput as T, Unpacked as U, RequestListenerInfo as a, ResponseListenerInfo as b, IResponseListener as c, Replacement as d, DeepReplace as e, RawEndpoint as f };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avantstay/graphql-ts-client",
3
- "version": "12.0.2",
3
+ "version": "12.2.0",
4
4
  "description": "GraphQL Typescript Client Generator",
5
5
  "homepage": "https://github.com/avantstay/graphql-ts-client",
6
6
  "bugs": {
@@ -91,4 +91,4 @@
91
91
  "limit": "10 KB"
92
92
  }
93
93
  ]
94
- }
94
+ }
@@ -29,7 +29,7 @@ __export(exports, {
29
29
  client: () => client,
30
30
  default: () => stdin_default
31
31
  });
32
- var import_endpoint = __toModule(require("./endpoint"));
32
+ var import_endpoint = __toModule(require("@avantstay/graphql-ts-client/dist/endpoint"));
33
33
  const formatGraphQL = (query) => query;
34
34
  const typesTree = {};
35
35
  let verbose = false;
@@ -40,10 +40,12 @@ let retryConfig = {
40
40
  before: void 0,
41
41
  waitBeforeRetry: 0
42
42
  };
43
+ let requestListeners = [];
43
44
  let responseListeners = [];
44
45
  let errorsParser = void 0;
45
46
  let apiEndpoint = (0, import_endpoint.getApiEndpointCreator)({
46
47
  getClient: () => ({ url, headers, retryConfig }),
48
+ requestListeners,
47
49
  responseListeners,
48
50
  maxAge: 3e4,
49
51
  verbose,
@@ -52,7 +54,32 @@ let apiEndpoint = (0, import_endpoint.getApiEndpointCreator)({
52
54
  errorsParser
53
55
  });
54
56
  const client = {
55
- addResponseListener: (listener) => responseListeners.push(listener),
57
+ addRequestListener: (listener) => {
58
+ requestListeners.push(listener);
59
+ return () => {
60
+ const index = requestListeners.indexOf(listener);
61
+ if (index > -1)
62
+ requestListeners.splice(index, 1);
63
+ };
64
+ },
65
+ removeRequestListener: (listener) => {
66
+ const index = requestListeners.indexOf(listener);
67
+ if (index > -1)
68
+ requestListeners.splice(index, 1);
69
+ },
70
+ addResponseListener: (listener) => {
71
+ responseListeners.push(listener);
72
+ return () => {
73
+ const index = responseListeners.indexOf(listener);
74
+ if (index > -1)
75
+ responseListeners.splice(index, 1);
76
+ };
77
+ },
78
+ removeResponseListener: (listener) => {
79
+ const index = responseListeners.indexOf(listener);
80
+ if (index > -1)
81
+ responseListeners.splice(index, 1);
82
+ },
56
83
  setHeader: (key, value) => {
57
84
  headers[key] = value;
58
85
  },
@@ -77,7 +104,7 @@ const client = {
77
104
  };
78
105
  var stdin_default = client;
79
106
  ",
80
- "mjs": "import { getApiEndpointCreator } from "./endpoint";
107
+ "mjs": "import { getApiEndpointCreator } from "@avantstay/graphql-ts-client/dist/endpoint";
81
108
  const formatGraphQL = (query) => query;
82
109
  const typesTree = {};
83
110
  let verbose = false;
@@ -88,10 +115,12 @@ let retryConfig = {
88
115
  before: void 0,
89
116
  waitBeforeRetry: 0
90
117
  };
118
+ let requestListeners = [];
91
119
  let responseListeners = [];
92
120
  let errorsParser = void 0;
93
121
  let apiEndpoint = getApiEndpointCreator({
94
122
  getClient: () => ({ url, headers, retryConfig }),
123
+ requestListeners,
95
124
  responseListeners,
96
125
  maxAge: 3e4,
97
126
  verbose,
@@ -100,7 +129,32 @@ let apiEndpoint = getApiEndpointCreator({
100
129
  errorsParser
101
130
  });
102
131
  const client = {
103
- addResponseListener: (listener) => responseListeners.push(listener),
132
+ addRequestListener: (listener) => {
133
+ requestListeners.push(listener);
134
+ return () => {
135
+ const index = requestListeners.indexOf(listener);
136
+ if (index > -1)
137
+ requestListeners.splice(index, 1);
138
+ };
139
+ },
140
+ removeRequestListener: (listener) => {
141
+ const index = requestListeners.indexOf(listener);
142
+ if (index > -1)
143
+ requestListeners.splice(index, 1);
144
+ },
145
+ addResponseListener: (listener) => {
146
+ responseListeners.push(listener);
147
+ return () => {
148
+ const index = responseListeners.indexOf(listener);
149
+ if (index > -1)
150
+ responseListeners.splice(index, 1);
151
+ };
152
+ },
153
+ removeResponseListener: (listener) => {
154
+ const index = responseListeners.indexOf(listener);
155
+ if (index > -1)
156
+ responseListeners.splice(index, 1);
157
+ },
104
158
  setHeader: (key, value) => {
105
159
  headers[key] = value;
106
160
  },
@@ -131,7 +185,12 @@ export {
131
185
  ",
132
186
  "typings": "// noinspection TypeScriptUnresolvedVariable, ES6UnusedImports, JSUnusedLocalSymbols, TypeScriptCheckImport
133
187
  import { DeepRequired } from "ts-essentials"
134
- import { Maybe, IResponseListener, Endpoint } from "."
188
+ import {
189
+ Maybe,
190
+ IRequestListener,
191
+ IResponseListener,
192
+ Endpoint,
193
+ } from "@avantstay/graphql-ts-client/dist"
135
194
 
136
195
  // Scalars
137
196
  export type IDate = string | Date
@@ -156,7 +215,10 @@ export interface Query {
156
215
  // Selection Types
157
216
 
158
217
  export declare const client: {
159
- addResponseListener: (listener: IResponseListener) => void
218
+ addRequestListener: (listener: IRequestListener) => () => void
219
+ removeRequestListener: (listener: IRequestListener) => void
220
+ addResponseListener: (listener: IResponseListener) => () => void
221
+ removeResponseListener: (listener: IResponseListener) => void
160
222
  setHeader: (key: string, value: string) => void
161
223
  setHeaders: (newHeaders: { [k: string]: string }) => void
162
224
  setUrl: (url: string) => void
@@ -1,6 +1,11 @@
1
1
  // noinspection TypeScriptUnresolvedVariable, ES6UnusedImports, JSUnusedLocalSymbols, TypeScriptCheckImport
2
2
  import { DeepRequired } from "ts-essentials"
3
- import { Maybe, IResponseListener, Endpoint } from "."
3
+ import {
4
+ Maybe,
5
+ IRequestListener,
6
+ IResponseListener,
7
+ Endpoint,
8
+ } from "@avantstay/graphql-ts-client/dist"
4
9
 
5
10
  // Scalars
6
11
  export type IDate = string | Date
@@ -92,7 +97,10 @@ export interface BookSearchParamsSomeRequiredSelection {
92
97
  }
93
98
 
94
99
  export declare const myApiClient: {
95
- addResponseListener: (listener: IResponseListener) => void
100
+ addRequestListener: (listener: IRequestListener) => () => void
101
+ removeRequestListener: (listener: IRequestListener) => void
102
+ addResponseListener: (listener: IResponseListener) => () => void
103
+ removeResponseListener: (listener: IResponseListener) => void
96
104
  setHeader: (key: string, value: string) => void
97
105
  setHeaders: (newHeaders: { [k: string]: string }) => void
98
106
  setUrl: (url: string) => void
@@ -26,7 +26,7 @@ __export(exports, {
26
26
  default: () => stdin_default,
27
27
  myApiClient: () => myApiClient
28
28
  });
29
- var import_endpoint = __toModule(require("./endpoint"));
29
+ var import_endpoint = __toModule(require("@avantstay/graphql-ts-client/dist/endpoint"));
30
30
  var import_standalone = __toModule(require("prettier/standalone"));
31
31
  var import_parser_graphql = __toModule(require("prettier/parser-graphql"));
32
32
  const formatGraphQL = (query) => (0, import_standalone.format)(query, { parser: "graphql", plugins: [import_parser_graphql.default] });
@@ -69,10 +69,12 @@ let retryConfig = {
69
69
  before: void 0,
70
70
  waitBeforeRetry: 0
71
71
  };
72
+ let requestListeners = [];
72
73
  let responseListeners = [];
73
74
  let errorsParser = void 0;
74
75
  let apiEndpoint = (0, import_endpoint.getApiEndpointCreator)({
75
76
  getClient: () => ({ url, headers, retryConfig }),
77
+ requestListeners,
76
78
  responseListeners,
77
79
  maxAge: 3e4,
78
80
  verbose,
@@ -81,7 +83,32 @@ let apiEndpoint = (0, import_endpoint.getApiEndpointCreator)({
81
83
  errorsParser
82
84
  });
83
85
  const myApiClient = {
84
- addResponseListener: (listener) => responseListeners.push(listener),
86
+ addRequestListener: (listener) => {
87
+ requestListeners.push(listener);
88
+ return () => {
89
+ const index = requestListeners.indexOf(listener);
90
+ if (index > -1)
91
+ requestListeners.splice(index, 1);
92
+ };
93
+ },
94
+ removeRequestListener: (listener) => {
95
+ const index = requestListeners.indexOf(listener);
96
+ if (index > -1)
97
+ requestListeners.splice(index, 1);
98
+ },
99
+ addResponseListener: (listener) => {
100
+ responseListeners.push(listener);
101
+ return () => {
102
+ const index = responseListeners.indexOf(listener);
103
+ if (index > -1)
104
+ responseListeners.splice(index, 1);
105
+ };
106
+ },
107
+ removeResponseListener: (listener) => {
108
+ const index = responseListeners.indexOf(listener);
109
+ if (index > -1)
110
+ responseListeners.splice(index, 1);
111
+ },
85
112
  setHeader: (key, value) => {
86
113
  headers[key] = value;
87
114
  },
@@ -1,4 +1,4 @@
1
- import { getApiEndpointCreator } from "./endpoint";
1
+ import { getApiEndpointCreator } from "@avantstay/graphql-ts-client/dist/endpoint";
2
2
  import { format as formatCode } from "prettier/standalone";
3
3
  import parserGraphql from "prettier/parser-graphql";
4
4
  const formatGraphQL = (query) => formatCode(query, { parser: "graphql", plugins: [parserGraphql] });
@@ -41,10 +41,12 @@ let retryConfig = {
41
41
  before: void 0,
42
42
  waitBeforeRetry: 0
43
43
  };
44
+ let requestListeners = [];
44
45
  let responseListeners = [];
45
46
  let errorsParser = void 0;
46
47
  let apiEndpoint = getApiEndpointCreator({
47
48
  getClient: () => ({ url, headers, retryConfig }),
49
+ requestListeners,
48
50
  responseListeners,
49
51
  maxAge: 3e4,
50
52
  verbose,
@@ -53,7 +55,32 @@ let apiEndpoint = getApiEndpointCreator({
53
55
  errorsParser
54
56
  });
55
57
  const myApiClient = {
56
- addResponseListener: (listener) => responseListeners.push(listener),
58
+ addRequestListener: (listener) => {
59
+ requestListeners.push(listener);
60
+ return () => {
61
+ const index = requestListeners.indexOf(listener);
62
+ if (index > -1)
63
+ requestListeners.splice(index, 1);
64
+ };
65
+ },
66
+ removeRequestListener: (listener) => {
67
+ const index = requestListeners.indexOf(listener);
68
+ if (index > -1)
69
+ requestListeners.splice(index, 1);
70
+ },
71
+ addResponseListener: (listener) => {
72
+ responseListeners.push(listener);
73
+ return () => {
74
+ const index = responseListeners.indexOf(listener);
75
+ if (index > -1)
76
+ responseListeners.splice(index, 1);
77
+ };
78
+ },
79
+ removeResponseListener: (listener) => {
80
+ const index = responseListeners.indexOf(listener);
81
+ if (index > -1)
82
+ responseListeners.splice(index, 1);
83
+ },
57
84
  setHeader: (key, value) => {
58
85
  headers[key] = value;
59
86
  },
package/src/endpoint.ts CHANGED
@@ -2,7 +2,15 @@ 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 {
6
+ ClientConfig,
7
+ Endpoint,
8
+ GraphQLClientError,
9
+ IRequestListener,
10
+ IResponseListener,
11
+ Projection,
12
+ ResponseListenerInfo,
13
+ } from './types'
6
14
 
7
15
  const executeListeners = (listeners: IResponseListener[], data: ResponseListenerInfo) =>
8
16
  setTimeout(() => listeners.forEach(runResponseListener => runResponseListener(data)))
@@ -10,6 +18,7 @@ const executeListeners = (listeners: IResponseListener[], data: ResponseListener
10
18
  export const getApiEndpointCreator =
11
19
  (apiConfig: {
12
20
  getClient: () => ClientConfig
21
+ requestListeners: IRequestListener[]
13
22
  responseListeners: IResponseListener[]
14
23
  typesTree: any
15
24
  maxAge: number
@@ -28,10 +37,7 @@ export const getApiEndpointCreator =
28
37
  headers: any
29
38
  status: any
30
39
  }> => {
31
- const clientConfig = apiConfig.getClient()
32
-
33
40
  const alias = (jsonQuery as any)?.__alias ?? queryName
34
- const url = (jsonQuery as any)?.__url ?? clientConfig.url
35
41
  const shouldRetry = (jsonQuery as any)?.__retry ?? true
36
42
  const requestHeaders = (jsonQuery as any)?.__headers ?? {}
37
43
  const { query, variables } = jsonToGraphQLQuery({ kind, queryName, jsonQuery, typesTree: apiConfig.typesTree })
@@ -46,19 +52,25 @@ export const getApiEndpointCreator =
46
52
  variables,
47
53
  }
48
54
 
49
- const responseListenerData = {
55
+ const listenerData = {
50
56
  queryName: alias,
51
57
  query: apiConfig.formatGraphQL(query),
52
58
  variables,
59
+ headers: { ...requestHeaders },
53
60
  }
54
61
 
62
+ await Promise.all(apiConfig.requestListeners.map(listener => listener(listenerData)))
63
+
64
+ const clientConfig = apiConfig.getClient()
65
+ const url = (jsonQuery as any)?.__url ?? clientConfig.url
66
+
55
67
  try {
56
68
  const { data, errors, warnings, headers, status } = await graphqlRequest({
57
69
  shouldRetry,
58
70
  failureMode,
59
71
  queryName: alias,
60
72
  client: { ...clientConfig, url },
61
- requestHeaders,
73
+ requestHeaders: listenerData.headers,
62
74
  query,
63
75
  variables,
64
76
  errorsParser: apiConfig.errorsParser,
@@ -75,7 +87,7 @@ export const getApiEndpointCreator =
75
87
  }
76
88
 
77
89
  executeListeners(apiConfig.responseListeners, {
78
- ...responseListenerData,
90
+ ...listenerData,
79
91
  response,
80
92
  })
81
93
 
@@ -90,7 +102,7 @@ export const getApiEndpointCreator =
90
102
  }
91
103
 
92
104
  executeListeners(apiConfig.responseListeners, {
93
- ...responseListenerData,
105
+ ...listenerData,
94
106
  response: (error as GraphQLClientError).response,
95
107
  })
96
108
 
@@ -2,7 +2,7 @@ import { ApolloServer } from 'apollo-server'
2
2
  import * as path from 'path'
3
3
  import { generateTypescriptClient, generateTypescriptClientFromSDL } from './generateTypescriptClient'
4
4
  import { startServer } from './testServer'
5
- import { GraphQLClientError, ResponseListenerInfo } from './types'
5
+ import { RequestListenerInfo, ResponseListenerInfo } from './types'
6
6
 
7
7
  let testServer: { server: ApolloServer; url: string }
8
8
  let client: any
@@ -77,7 +77,7 @@ describe('Generated Client', () => {
77
77
  (err: any) => err
78
78
  )
79
79
 
80
- expect(result).toBeInstanceOf(GraphQLClientError)
80
+ expect(result.constructor.name).toContain('GraphQLClientError')
81
81
  expect(result.message).toBe('Failed lorem ipsum dolor')
82
82
  })
83
83
 
@@ -109,6 +109,53 @@ describe('Generated Client', () => {
109
109
  expect(failed).toBe(true)
110
110
  })
111
111
 
112
+ it('request listener is called before the request', async () => {
113
+ let requestData: RequestListenerInfo | undefined
114
+ client.addRequestListener((data: any) => (requestData = data))
115
+
116
+ await client.queries.booksWithoutParams({ title: true })
117
+
118
+ expect(requestData?.queryName).toBe('booksWithoutParams')
119
+ expect(requestData?.query).toBeDefined()
120
+ expect(requestData?.variables).toBeDefined()
121
+ })
122
+
123
+ it('header set in request listener is sent with the request', async () => {
124
+ const axios = require('axios')
125
+ const axiosSpy = jest.spyOn(axios, 'post')
126
+
127
+ client.addRequestListener(async () => {
128
+ const token = await Promise.resolve('my-fresh-token')
129
+ client.setHeader('Authorization', `Bearer ${token}`)
130
+ })
131
+
132
+ await client.queries.booksWithoutParams({ title: true })
133
+
134
+ expect(axiosSpy).toHaveBeenCalledWith(
135
+ expect.any(String),
136
+ expect.any(Object),
137
+ expect.objectContaining({
138
+ headers: expect.objectContaining({
139
+ Authorization: 'Bearer my-fresh-token',
140
+ }),
141
+ })
142
+ )
143
+
144
+ axiosSpy.mockRestore()
145
+ client.setHeader('Authorization', undefined)
146
+ })
147
+
148
+ it('request listener is called even for failing operations', async () => {
149
+ let requestData: RequestListenerInfo | undefined
150
+ client.addRequestListener((data: any) => (requestData = data))
151
+
152
+ try {
153
+ await client.queries.failingQuery({ __args: { id: 'hello' } })
154
+ } catch {}
155
+
156
+ expect(requestData?.queryName).toBe('failingQuery')
157
+ })
158
+
112
159
  it('failing operations throw errors', async () => {
113
160
  let responseData: ResponseListenerInfo | undefined
114
161
  client.addResponseListener((data: any) => (responseData = data))
@@ -125,6 +172,56 @@ describe('Generated Client', () => {
125
172
  expect(responseData?.response.errors.length).toBeGreaterThan(0)
126
173
  })
127
174
 
175
+ it('removeRequestListener removes a previously added request listener', async () => {
176
+ let callCount = 0
177
+ const listener = () => { callCount++ }
178
+
179
+ client.addRequestListener(listener)
180
+ await client.queries.booksWithoutParams({ title: true })
181
+ expect(callCount).toBe(1)
182
+
183
+ client.removeRequestListener(listener)
184
+ await client.queries.booksWithoutParams({ title: true })
185
+ expect(callCount).toBe(1)
186
+ })
187
+
188
+ it('removeResponseListener removes a previously added response listener', async () => {
189
+ let callCount = 0
190
+ const listener = () => { callCount++ }
191
+
192
+ client.addResponseListener(listener)
193
+ await client.queries.booksWithoutParams({ title: true })
194
+ expect(callCount).toBe(1)
195
+
196
+ client.removeResponseListener(listener)
197
+ await client.queries.booksWithoutParams({ title: true })
198
+ expect(callCount).toBe(1)
199
+ })
200
+
201
+ it('addRequestListener returns an unsubscribe function', async () => {
202
+ let callCount = 0
203
+ const unsubscribe = client.addRequestListener(() => { callCount++ })
204
+
205
+ await client.queries.booksWithoutParams({ title: true })
206
+ expect(callCount).toBe(1)
207
+
208
+ unsubscribe()
209
+ await client.queries.booksWithoutParams({ title: true })
210
+ expect(callCount).toBe(1)
211
+ })
212
+
213
+ it('addResponseListener returns an unsubscribe function', async () => {
214
+ let callCount = 0
215
+ const unsubscribe = client.addResponseListener(() => { callCount++ })
216
+
217
+ await client.queries.booksWithoutParams({ title: true })
218
+ expect(callCount).toBe(1)
219
+
220
+ unsubscribe()
221
+ await client.queries.booksWithoutParams({ title: true })
222
+ expect(callCount).toBe(1)
223
+ })
224
+
128
225
  it('should generate proper code from SDL', () => {
129
226
  const sdlString = `
130
227
  type Query {
@@ -376,11 +376,13 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
376
376
  before: undefined,
377
377
  waitBeforeRetry: 0
378
378
  }
379
+ let requestListeners = []
379
380
  let responseListeners = []
380
381
  let errorsParser = ${options.errorsParser}
381
382
  // noinspection JSUnusedLocalSymbols
382
383
  let apiEndpoint = getApiEndpointCreator({
383
384
  getClient: () => ({ url, headers, retryConfig }),
385
+ requestListeners,
384
386
  responseListeners,
385
387
  maxAge: 30000,
386
388
  verbose,
@@ -390,8 +392,28 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
390
392
  })
391
393
 
392
394
  export const ${clientName} = {
393
- addResponseListener: (listener) => responseListeners.push(
394
- listener),
395
+ addRequestListener: (listener) => {
396
+ requestListeners.push(listener)
397
+ return () => {
398
+ const index = requestListeners.indexOf(listener)
399
+ if (index > -1) requestListeners.splice(index, 1)
400
+ }
401
+ },
402
+ removeRequestListener: (listener) => {
403
+ const index = requestListeners.indexOf(listener)
404
+ if (index > -1) requestListeners.splice(index, 1)
405
+ },
406
+ addResponseListener: (listener) => {
407
+ responseListeners.push(listener)
408
+ return () => {
409
+ const index = responseListeners.indexOf(listener)
410
+ if (index > -1) responseListeners.splice(index, 1)
411
+ }
412
+ },
413
+ removeResponseListener: (listener) => {
414
+ const index = responseListeners.indexOf(listener)
415
+ if (index > -1) responseListeners.splice(index, 1)
416
+ },
395
417
  setHeader: (key, value) => {
396
418
  headers[key] = value
397
419
  },
@@ -424,7 +446,7 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
424
446
  const typingsCode = `
425
447
  // noinspection TypeScriptUnresolvedVariable, ES6UnusedImports, JSUnusedLocalSymbols, TypeScriptCheckImport
426
448
  import { DeepRequired } from 'ts-essentials'
427
- import { Maybe, IResponseListener, Endpoint } from '${graphqlTsClientPath}'
449
+ import { Maybe, IRequestListener, IResponseListener, Endpoint } from '${graphqlTsClientPath}'
428
450
 
429
451
  // Scalars
430
452
  export type IDate = string | Date
@@ -461,7 +483,10 @@ function generateClientCode(types: ReadonlyArray<IntrospectionType>, options: Om
461
483
  .join('\n')}
462
484
 
463
485
  export declare const ${clientName}: {
464
- addResponseListener: (listener: IResponseListener) => void
486
+ addRequestListener: (listener: IRequestListener) => () => void
487
+ removeRequestListener: (listener: IRequestListener) => void
488
+ addResponseListener: (listener: IResponseListener) => () => void
489
+ removeResponseListener: (listener: IResponseListener) => void
465
490
  setHeader: (key: string, value: string) => void
466
491
  setHeaders: (newHeaders: { [k: string]: string }) => void,
467
492
  setUrl: (url: string) => void,
package/src/types.ts CHANGED
@@ -11,6 +11,14 @@ export type ResponseData = {
11
11
  }[]
12
12
  }
13
13
 
14
+ export type RequestListenerInfo = {
15
+ queryName: string
16
+ query: string
17
+ variables: any
18
+ headers: { [key: string]: string }
19
+ }
20
+ export type IRequestListener = (info: RequestListenerInfo) => void | Promise<void>
21
+
14
22
  export type ResponseListenerInfo = {
15
23
  queryName: string
16
24
  query: string
@@ -1,27 +0,0 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined")
5
- return require.apply(this, arguments);
6
- throw new Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
-
9
- // src/types.ts
10
- var GraphQLClientError = class extends Error {
11
- constructor(responseData) {
12
- super();
13
- this.responseData = responseData;
14
- Object.setPrototypeOf(this, GraphQLClientError.prototype);
15
- }
16
- get message() {
17
- return this.response.errors.map((it) => it.message).join(";\n");
18
- }
19
- get response() {
20
- return this.responseData;
21
- }
22
- };
23
-
24
- export {
25
- __require,
26
- GraphQLClientError
27
- };