swaggie 1.9.0-dev.1 → 2.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.
Files changed (46) hide show
  1. package/README.md +117 -31
  2. package/dist/browser.js +24 -5
  3. package/dist/cli.js +54 -7
  4. package/dist/gen/genMocks.js +453 -0
  5. package/dist/gen/genOperations.js +112 -20
  6. package/dist/gen/genTypes.js +6 -5
  7. package/dist/gen/header.js +0 -1
  8. package/dist/gen/index.js +14 -1
  9. package/dist/generated/bundledTemplates.js +17 -19
  10. package/dist/index.js +17 -1
  11. package/dist/swagger/operations.js +16 -7
  12. package/dist/swagger/typesExtractor.js +12 -11
  13. package/dist/types.d.ts +55 -5
  14. package/dist/utils/documentLoader.js +1 -3
  15. package/dist/utils/fileUtils.js +22 -0
  16. package/dist/utils/refResolver.js +9 -21
  17. package/dist/utils/templateEngine.js +18 -0
  18. package/dist/utils/templateManager.js +123 -14
  19. package/dist/utils/templateValidator.js +127 -0
  20. package/dist/utils/utils.js +19 -13
  21. package/package.json +5 -4
  22. package/templates/axios/operation.ejs +25 -21
  23. package/templates/fetch/operation.ejs +4 -0
  24. package/templates/ng1/operation.ejs +11 -3
  25. package/templates/ng2/baseClient.ejs +9 -49
  26. package/templates/ng2/client.ejs +3 -7
  27. package/templates/ng2/operation.ejs +34 -17
  28. package/templates/swr/baseClient.ejs +7 -0
  29. package/templates/swr/client.ejs +63 -0
  30. package/templates/swr/swrMutationOperation.ejs +32 -0
  31. package/templates/swr/swrOperation.ejs +18 -0
  32. package/templates/tsq/baseClient.ejs +1 -0
  33. package/templates/tsq/client.ejs +67 -0
  34. package/templates/tsq/mutationOperation.ejs +31 -0
  35. package/templates/tsq/queryOperation.ejs +19 -0
  36. package/templates/xior/operation.ejs +25 -21
  37. package/templates/swr-axios/barrel.ejs +0 -58
  38. package/templates/swr-axios/baseClient.ejs +0 -20
  39. package/templates/swr-axios/client.ejs +0 -21
  40. package/templates/swr-axios/operation.ejs +0 -40
  41. package/templates/swr-axios/swrOperation.ejs +0 -50
  42. package/templates/tsq-xior/barrel.ejs +0 -0
  43. package/templates/tsq-xior/baseClient.ejs +0 -14
  44. package/templates/tsq-xior/client.ejs +0 -22
  45. package/templates/tsq-xior/operation.ejs +0 -40
  46. package/templates/tsq-xior/queryOperation.ejs +0 -30
@@ -204,8 +204,9 @@ function generateItemsType(schema, options) {
204
204
  function renderExtendedEnumType(name, def) {
205
205
  const isString = def.type === 'string';
206
206
  let res = `export enum ${name} {\n`;
207
- const enumNames = _nullishCoalesce(def['x-enumNames'], () => ( def['x-enum-varnames']));
208
- const enumValues = def.enum.map((el) => (isString ? `"${el}"` : el));
207
+ const defAny = def ;
208
+ const enumNames = _nullishCoalesce(_nullishCoalesce(defAny['x-enumNames'], () => ( defAny['x-enum-varnames'])), () => ( []));
209
+ const enumValues = (_nullishCoalesce(def.enum, () => ( []))).map((el) => (isString ? `"${el}"` : el));
209
210
 
210
211
  for (let index = 0; index < enumNames.length; index++) {
211
212
  res += ` ${_utils.escapePropName.call(void 0, enumNames[index])} = ${enumValues[index]},\n`;
@@ -221,7 +222,7 @@ function renderEnumType(name, def, options) {
221
222
  return renderStringEnumDeclaration(name, def, options);
222
223
  }
223
224
 
224
- const values = def.enum.map((v) => (typeof v === 'number' ? v : `"${v}"`)).join(' | ');
225
+ const values = (_nullishCoalesce(def.enum, () => ( []))).map((v) => (typeof v === 'number' ? v : `"${v}"`)).join(' | ');
225
226
  return `export type ${name} = ${values};\n`;
226
227
  }
227
228
 
@@ -272,7 +273,7 @@ function toPascalCase(value) {
272
273
  */
273
274
  function renderOpenApi31Enum(name, def) {
274
275
  let res = `export enum ${name} {\n`;
275
- for (const v of def.oneOf) {
276
+ for (const v of _nullishCoalesce(def.oneOf, () => ( []))) {
276
277
  if ('const' in v) {
277
278
  res += ` ${_utils.escapePropName.call(void 0, v.title)} = ${
278
279
  typeof v.const === 'string' ? `"${v.const}"` : v.const
@@ -383,7 +384,7 @@ function getMergedCompositeObjects(schema) {
383
384
  }
384
385
 
385
386
  function isObject(item) {
386
- return item && typeof item === 'object' && !Array.isArray(item);
387
+ return !!item && typeof item === 'object' && !Array.isArray(item);
387
388
  }
388
389
  function deepMerge(target, ...sources) {
389
390
  if (!sources.length) return target;
@@ -3,7 +3,6 @@
3
3
  * regardless of generation mode.
4
4
  */
5
5
  const FILE_HEADER =
6
- '/* istanbul ignore file -- auto-generated code */\n' +
7
6
  '/* tslint:disable */\n' +
8
7
  '/* eslint-disable */\n' +
9
8
  '//----------------------\n' +
package/dist/gen/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _createNamedExportFrom(obj, localName, importedName) { Object.defineProperty(exports, localName, {enumerable: true, configurable: true, get: () => obj[importedName]}); }
2
2
 
3
3
  var _genOperations = require('./genOperations'); var _genOperations2 = _interopRequireDefault(_genOperations);
4
+ var _genMocks = require('./genMocks'); var _genMocks2 = _interopRequireDefault(_genMocks);
4
5
  var _genTypes = require('./genTypes'); var _genTypes2 = _interopRequireDefault(_genTypes);
5
6
  var _header = require('./header'); _createNamedExportFrom(_header, 'FILE_HEADER', 'FILE_HEADER');
6
7
  var _utils = require('../utils');
@@ -23,7 +24,19 @@ var _utils = require('../utils');
23
24
 
24
25
  if (options.out) {
25
26
  const destFile = _utils.prepareOutputFilename.call(void 0, options.out);
26
- await _utils.saveFile.call(void 0, destFile, fileContents);
27
+ if (destFile) {
28
+ await _utils.saveFile.call(void 0, destFile, fileContents);
29
+ }
30
+ }
31
+
32
+ if (options.mocks && options.testingFramework && options.out) {
33
+ const resolvedMocksPath = _utils.prepareOutputFilename.call(void 0, options.mocks);
34
+ const resolvedOutPath = _utils.prepareOutputFilename.call(void 0, options.out);
35
+ if (resolvedMocksPath && resolvedOutPath) {
36
+ const relativeApiImport = _utils.deriveRelativeImport.call(void 0, resolvedMocksPath, resolvedOutPath);
37
+ const mockContents = _genMocks2.default.call(void 0, spec, options, relativeApiImport);
38
+ await _utils.saveFile.call(void 0, resolvedMocksPath, mockContents);
39
+ }
27
40
  }
28
41
 
29
42
  return fileContents;
@@ -6,45 +6,43 @@
6
6
  "barrel.ejs": "\n/**\n * Serializes a params object into a query string that is compatible with different REST APIs.\n * Implementation from: https://github.com/suhaotian/xior/blob/main/src/utils.ts\n * Kudos to @suhaotian for the original implementation\n */\nfunction encodeParams<T = any>(\n params: T,\n parentKey: string | null = null,\n options?: {\n allowDots?: boolean;\n serializeDate?: (value: Date) => string;\n arrayFormat?: 'indices' | 'repeat' | 'brackets';\n }\n): string {\n if (params === undefined || params === null) return '';\n const encodedParams: string[] = [];\n const paramsIsArray = Array.isArray(params);\n const { arrayFormat, allowDots, serializeDate } = options || {};\n\n const getKey = (key: string) => {\n if (allowDots && !paramsIsArray) return `.${key}`;\n if (paramsIsArray) {\n if (arrayFormat === 'brackets') {\n return '[]';\n }\n if (arrayFormat === 'repeat') {\n return '';\n }\n }\n return `[${key}]`;\n };\n\n for (const key in params) {\n if (Object.prototype.hasOwnProperty.call(params, key)) {\n let value = (params as any)[key];\n if (value !== undefined) {\n const encodedKey = parentKey ? `${parentKey}${getKey(key)}` : (key as string);\n\n // biome-ignore lint/suspicious/noGlobalIsNan: <explanation>\n if (!isNaN(value) && value instanceof Date) {\n value = serializeDate ? serializeDate(value) : value.toISOString();\n }\n if (typeof value === 'object') {\n // If the value is an object or array, recursively encode its contents\n const result = encodeParams(value, encodedKey, options);\n if (result !== '') encodedParams.push(result);\n } else {\n // Otherwise, encode the key-value pair\n encodedParams.push(`${encodeURIComponent(encodedKey)}=${encodeURIComponent(value)}`);\n }\n }\n }\n }\n\n return encodedParams.join('&');\n}\n\n",
7
7
  "baseClient.ejs": "import Axios, { type AxiosPromise, type AxiosRequestConfig } from \"axios\";\n\nexport const axios = Axios.create({\n baseURL: '<%= it.baseUrl || '' %>',\n paramsSerializer: (params: any) =>\n encodeParams(params, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n});\n\n",
8
8
  "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n",
9
- "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: AxiosRequestConfig\n ): AxiosPromise<<%~ it.returnType %>> {\n const url = `<%= it.url %>`;\n\n return axios.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'urlencoded') { %>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n data: <%= it.body.name %>,\n<% } %>\n<% } %>\n<% if(it.query && it.query.length > 0) { %>\n params: {\n <% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>\n },\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n <% it.headers.forEach((parameter) => { %>\n <% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n <% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% } %>\n <% }); %>\n},\n<% } %>\n ...$config,\n });\n },\n"
9
+ "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: AxiosRequestConfig\n ): AxiosPromise<<%~ it.returnType %>> {\n const url = `<%= it.url %>`;\n\n return axios.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { -%>\n<% if(it.body.contentType === 'urlencoded') { -%>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { -%>\n data: <%= it.body.name %>,\n<% } -%>\n<% } -%>\n<% if(it.query && it.query.length > 0) { -%>\n params: {\n<% it.query.forEach((parameter) => { -%>\n<% if (it.queryParamObject) { -%>\n '<%= parameter.originalName %>': <%= it.queryParamObject.name %>?.<%= parameter.name %>,\n<% } else { -%>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n<% } -%>\n<% }); -%>\n },\n<% } -%>\n<% if(it.headers && it.headers.length > 0) { -%>\n headers: {\n<% it.headers.forEach((parameter) => { -%>\n<% if (parameter.value) { -%>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n<% } else { -%>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n<% } -%>\n<% }); -%>\n },\n<% } -%>\n ...$config,\n });\n },\n"
10
10
  },
11
11
  "fetch": {
12
12
  "barrel.ejs": "\n/**\n * Serializes a params object into a query string that is compatible with different REST APIs.\n * Implementation from: https://github.com/suhaotian/xior/blob/main/src/utils.ts\n * Kudos to @suhaotian for the original implementation\n */\nfunction encodeParams<T = any>(\n params: T,\n parentKey: string | null = null,\n options?: {\n allowDots?: boolean;\n serializeDate?: (value: Date) => string;\n arrayFormat?: 'indices' | 'repeat' | 'brackets';\n }\n): string {\n if (params === undefined || params === null) return '';\n const encodedParams: string[] = [];\n const paramsIsArray = Array.isArray(params);\n const { arrayFormat, allowDots, serializeDate } = options || {};\n\n const getKey = (key: string) => {\n if (allowDots && !paramsIsArray) return `.${key}`;\n if (paramsIsArray) {\n if (arrayFormat === 'brackets') {\n return '[]';\n }\n if (arrayFormat === 'repeat') {\n return '';\n }\n }\n return `[${key}]`;\n };\n\n for (const key in params) {\n if (Object.prototype.hasOwnProperty.call(params, key)) {\n let value = (params as any)[key];\n if (value !== undefined) {\n const encodedKey = parentKey ? `${parentKey}${getKey(key)}` : (key as string);\n\n // biome-ignore lint/suspicious/noGlobalIsNan: <explanation>\n if (!isNaN(value) && value instanceof Date) {\n value = serializeDate ? serializeDate(value) : value.toISOString();\n }\n if (typeof value === 'object') {\n // If the value is an object or array, recursively encode its contents\n const result = encodeParams(value, encodedKey, options);\n if (result !== '') encodedParams.push(result);\n } else {\n // Otherwise, encode the key-value pair\n encodedParams.push(`${encodeURIComponent(encodedKey)}=${encodeURIComponent(value)}`);\n }\n }\n }\n }\n\n return encodedParams.join('&');\n}\n\n",
13
13
  "baseClient.ejs": "export const defaults = {\n baseUrl: '<%= it.baseUrl || '' %>',\n paramsSerializer: (params: any) =>\n encodeParams(params, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n};\n\n",
14
14
  "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n",
15
- "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: RequestInit\n ): Promise<<%~ it.returnType %>> {\n const url = `${defaults.baseUrl}<%= it.url %>?<%\n if(it.query && it.query.length > 0) { %>${defaults.paramsSerializer({<%\n it.query.forEach((parameter) => { %>\n'<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>})}<% } %>`;\n\n<% if(it.headers && it.headers.length > 0) { %>\n const { headers: $configHeaders, ...$configRest } = $config ?? {};\n const headers = new Headers({\n<% it.headers.forEach((parameter) => { %>\n<% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n<% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %> ?? '',\n<% } %>\n<% }); %>\n });\n if ($configHeaders) {\n new Headers($configHeaders).forEach((value, key) => headers.set(key, value));\n }\n\n return fetch(url, {\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'json') { %>\n body: JSON.stringify(<%= it.body.name %>),\n<% } else if(it.body.contentType === 'urlencoded') { %>\n body: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n body: <%= it.body.name %>,\n<% } %>\n<% } %>\n headers,\n ...$configRest,\n })\n<% } else { %>\n return fetch(url, {\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'json') { %>\n body: JSON.stringify(<%= it.body.name %>),\n<% } else if(it.body.contentType === 'urlencoded') { %>\n body: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n body: <%= it.body.name %>,\n<% } %>\n<% } %>\n ...$config,\n })\n<% } %>\n<% if(it.responseContentType === 'binary') { %>\n .then((response) => response.blob() as Promise<<%~ it.returnType %>>);\n<% } else if(it.responseContentType === 'text') { %>\n .then((response) => response.text() as Promise<<%~ it.returnType %>>);\n<% } else { %>\n .then((response) => response.json() as Promise<<%~ it.returnType %>>);\n<% } %>\n },\n"
15
+ "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: RequestInit\n ): Promise<<%~ it.returnType %>> {\n const url = `${defaults.baseUrl}<%= it.url %>?<%\n if(it.query && it.query.length > 0) { %>${defaults.paramsSerializer({<%\n it.query.forEach((parameter) => { %>\n<% if (it.queryParamObject) { %>\n'<%= parameter.originalName %>': <%= it.queryParamObject.name %>?.<%= parameter.name %>,\n<% } else { %>\n'<%= parameter.originalName %>': <%= parameter.name %>,\n<% } %>\n <% }); %>})}<% } %>`;\n\n<% if(it.headers && it.headers.length > 0) { %>\n const { headers: $configHeaders, ...$configRest } = $config ?? {};\n const headers = new Headers({\n<% it.headers.forEach((parameter) => { %>\n<% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n<% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %> ?? '',\n<% } %>\n<% }); %>\n });\n if ($configHeaders) {\n new Headers($configHeaders).forEach((value, key) => headers.set(key, value));\n }\n\n return fetch(url, {\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'json') { %>\n body: JSON.stringify(<%= it.body.name %>),\n<% } else if(it.body.contentType === 'urlencoded') { %>\n body: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n body: <%= it.body.name %>,\n<% } %>\n<% } %>\n headers,\n ...$configRest,\n })\n<% } else { %>\n return fetch(url, {\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'json') { %>\n body: JSON.stringify(<%= it.body.name %>),\n<% } else if(it.body.contentType === 'urlencoded') { %>\n body: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n body: <%= it.body.name %>,\n<% } %>\n<% } %>\n ...$config,\n })\n<% } %>\n<% if(it.responseContentType === 'binary') { %>\n .then((response) => response.blob() as Promise<<%~ it.returnType %>>);\n<% } else if(it.responseContentType === 'text') { %>\n .then((response) => response.text() as Promise<<%~ it.returnType %>>);\n<% } else { %>\n .then((response) => response.json() as Promise<<%~ it.returnType %>>);\n<% } %>\n },\n"
16
16
  },
17
17
  "ng1": {
18
18
  "barrel.ejs": "export class ApiServices {\n public static bootstrap(moduleName: string, baseUrl: string) {\n angular\n .module(moduleName)\n .constant('Api<%= it.servicePrefix -%>BaseUrl', baseUrl)\n<% it.clients.forEach((client) => { %>\n .service('<%= client.fileName %>Service', <%= client.fileName %>Service)\n<% }); %>;\n }\n}\n\nfunction serializeQueryParam(obj: any, property: string): string {\n if (obj === null || obj === undefined || obj === '') {\n return '';\n } else if (obj instanceof Date) {\n return property + '=' + encodeURIComponent(obj.toJSON());\n } else if (Array.isArray(obj)) {\n return Object.values(obj)\n .map(value => `${property}[]=${value}`)\n .join('&');\n } else if (typeof obj !== 'object') {\n return property + '=' + encodeURIComponent(obj);\n } else if (typeof obj === 'object') {\n return Object.keys(obj)\n .filter(key => !!serializeQueryParam(obj[key], property + '.' + key))\n .reduce(\n (a: any, b) =>\n a.push(serializeQueryParam(obj[b], property + '.' + b)) && a,\n []\n )\n .join('&');\n } else {\n return '';\n }\n}\n",
19
19
  "baseClient.ejs": "import type { IHttpService, IRequestShortcutConfig, IPromise } from 'angular';\n\nabstract class BaseService {\n constructor(protected readonly $http: IHttpService, public baseUrl: string) { }\n\n protected $get<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<T> {\n return this.$http.get(this.baseUrl + url, config).then((response: any) => {\n return this.processSingle<T>(response);\n });\n }\n\n protected $getAll<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<T[]> {\n return this.$http.get(this.baseUrl + url, config).then((response: any) => {\n return this.processMany<T>(response);\n });\n }\n\n protected $delete<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<any> {\n return this.$http\n .delete(this.baseUrl + url, config)\n .then((response: any) => {\n return this.processSingle<T>(response);\n });\n }\n\n protected $head<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<any> {\n return this.$http.head(this.baseUrl + url, config).then((response: any) => {\n return this.processSingle<T>(response);\n });\n }\n\n protected $jsonp<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<any> {\n return this.$http\n .jsonp(this.baseUrl + url, config)\n .then((response: any) => {\n return this.processSingle<T>(response);\n });\n }\n\n protected $post<T>(\n url: string,\n data: any,\n config?: IRequestShortcutConfig\n ): IPromise<any> {\n return this.$http\n .post(this.baseUrl + url, data, config)\n .then((response: any) => {\n return this.processSingle<T>(response);\n });\n }\n\n protected $put<T>(\n url: string,\n data: any,\n config?: IRequestShortcutConfig\n ): IPromise<any> {\n return this.$http\n .put(this.baseUrl + url, data, config)\n .then((response: any) => {\n return this.processSingle<T>(response);\n });\n }\n\n protected $patch<T>(\n url: string,\n data: any,\n config?: IRequestShortcutConfig\n ): IPromise<any> {\n return this.$http\n .patch(this.baseUrl + url, data, config)\n .then((response: any) => {\n return this.processSingle<T>(response);\n });\n }\n\n protected processSingle<T>(response: any): T {\n var data = response.data;\n var status = response.status;\n\n if (status >= 200 && status <= 299) {\n return data;\n } else {\n throw 'error_no_callback_for_the_received_http_status';\n }\n }\n\n protected processMany<T>(response: any): T[] {\n var data = response.data;\n var status = response.status;\n\n if (status >= 200 && status <= 299) {\n return data;\n } else {\n throw 'error_no_callback_for_the_received_http_status';\n }\n }\n}\n\n",
20
20
  "client.ejs": "export class <%= it.clientName -%>Service extends BaseService {\n /* @ngInject */\n constructor($http: IHttpService, Api<%= it.servicePrefix -%>BaseUrl: string) {\n super($http, Api<%= it.servicePrefix -%>BaseUrl);\n }\n\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n}\n\n",
21
- "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n config?: IRequestShortcutConfig\n ): IPromise<<%~ it.returnType %>> {\n let url = `<%= it.url %>?`;\n<% if(it.query && it.query.length > 0) { %>\n <% it.query.forEach((parameter) => { %>\n if (<%= parameter.name %> !== undefined) {\n <% if(!!parameter.original && parameter.original.type === 'array') { %>\n <%= parameter.name %>.forEach(item => { url += serializeQueryParam(item, '<%= parameter.originalName %>') + \"&\"; });\n <% } else {%>\n url += serializeQueryParam(<%= parameter.name %>, '<%= parameter.originalName %>') + \"&\";\n <% } %>\n }\n <% }); %>\n<% } %>\n\n return this.$<%= it.method.toLowerCase() %>(\n url,\n<% if(['POST', 'PUT', 'PATCH'].includes(it.method)) { %>\n <% if(it.body) { %>\n <%= it.body.contentType === 'urlencoded' ? 'new URLSearchParams(' + it.body.name + ' as any)' : it.body.name %>,\n <% } else { %>\n null,\n <% } %>\n<% } %>\n config\n );\n }\n"
21
+ "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n config?: IRequestShortcutConfig\n ): IPromise<<%~ it.returnType %>> {\n let url = `<%= it.url %>?`;\n<% if(it.query && it.query.length > 0) { %>\n <% it.query.forEach((parameter) => { %>\n if (<%= it.queryParamObject ? `${it.queryParamObject.name}?.${parameter.name}` : parameter.name %> !== undefined) {\n <% if(!!parameter.original && parameter.original.type === 'array') { %>\n <%= it.queryParamObject ? `${it.queryParamObject.name}.${parameter.name}` : parameter.name %>.forEach(item => { url += serializeQueryParam(item, '<%= parameter.originalName %>') + \"&\"; });\n <% } else {%>\n url += serializeQueryParam(<%= it.queryParamObject ? `${it.queryParamObject.name}.${parameter.name}` : parameter.name %>, '<%= parameter.originalName %>') + \"&\";\n <% } %>\n }\n <% }); %>\n<% } %>\n\n<% if(it.headers && it.headers.length > 0) { it.headers.forEach((parameter) => { %>\n<% if (parameter.value) { %>\n config = { ...config, headers: { ...config?.headers, '<%= parameter.originalName %>': '<%= parameter.value %>' } };\n<% } else { %>\n if (<%= parameter.name %>) {\n config = { ...config, headers: { ...config?.headers, '<%= parameter.originalName %>': <%= parameter.name %> } };\n }\n<% } %><% }); } %>\n return this.$<%= it.method.toLowerCase() %>(\n url,\n<% if(['POST', 'PUT', 'PATCH'].includes(it.method)) { %>\n <% if(it.body) { %>\n <%= it.body.contentType === 'urlencoded' ? 'new URLSearchParams(' + it.body.name + ' as any)' : it.body.name %>,\n <% } else { %>\n null,\n <% } %>\n<% } %>\n config\n );\n }\n"
22
22
  },
23
23
  "ng2": {
24
24
  "barrel.ejs": "\n/**\n * Serializes a params object into a query string that is compatible with different REST APIs.\n * Implementation from: https://github.com/suhaotian/xior/blob/main/src/utils.ts\n * Kudos to @suhaotian for the original implementation\n */\nfunction encodeParams<T = any>(\n params: T,\n parentKey: string | null = null,\n options?: {\n allowDots?: boolean;\n serializeDate?: (value: Date) => string;\n arrayFormat?: 'indices' | 'repeat' | 'brackets';\n }\n): string {\n if (params === undefined || params === null) return '';\n const encodedParams: string[] = [];\n const paramsIsArray = Array.isArray(params);\n const { arrayFormat, allowDots, serializeDate } = options || {};\n\n const getKey = (key: string) => {\n if (allowDots && !paramsIsArray) return `.${key}`;\n if (paramsIsArray) {\n if (arrayFormat === 'brackets') {\n return '[]';\n }\n if (arrayFormat === 'repeat') {\n return '';\n }\n }\n return `[${key}]`;\n };\n\n for (const key in params) {\n if (Object.prototype.hasOwnProperty.call(params, key)) {\n let value = (params as any)[key];\n if (value !== undefined) {\n const encodedKey = parentKey ? `${parentKey}${getKey(key)}` : (key as string);\n\n // biome-ignore lint/suspicious/noGlobalIsNan: <explanation>\n if (!isNaN(value) && value instanceof Date) {\n value = serializeDate ? serializeDate(value) : value.toISOString();\n }\n if (typeof value === 'object') {\n // If the value is an object or array, recursively encode its contents\n const result = encodeParams(value, encodedKey, options);\n if (result !== '') encodedParams.push(result);\n } else {\n // Otherwise, encode the key-value pair\n encodedParams.push(`${encodeURIComponent(encodedKey)}=${encodeURIComponent(value)}`);\n }\n }\n }\n }\n\n return encodedParams.join('&');\n}\n\n",
25
- "baseClient.ejs": "import type { Observable } from \"rxjs\";\nimport { Injectable, Inject, Optional, InjectionToken } from \"@angular/core\";\nimport { HttpClient } from \"@angular/common/http\";\n\nexport const <%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL = new InjectionToken<string>(\"<%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL\");\n\nabstract class BaseService {\n private httpClient: HttpClient;\n private baseUrl: string;\n\n constructor(\n @Inject(HttpClient) httpClient: HttpClient,\n @Optional() @Inject(<%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL) baseUrl?: string\n ) {\n this.httpClient = httpClient;\n this.baseUrl = baseUrl ? baseUrl : '';\n }\n\n protected $get<T>(url: string, options?: any): Observable<T> {\n return this.httpClient\n .get<T>(this.baseUrl + url, options)\n .pipe((response: any) => response);\n }\n\n protected $getAll<T>(url: string, options?: any): Observable<T[]> {\n return this.httpClient\n .get<T[]>(this.baseUrl + url, options)\n .pipe((response: any) => response);\n }\n\n protected $delete<T>(url: string, options?: any): Observable<T> {\n return this.httpClient\n .delete(this.baseUrl + url, options)\n .pipe((response: any) => response);\n }\n\n protected $post(url: string, data: any, options?: any): Observable<any> {\n return this.httpClient\n .post(this.baseUrl + url, data, options)\n .pipe((response: any) => response);\n }\n\n protected $patch<T>(url: string, data: any, options?: any): Observable<T> {\n return this.httpClient\n .patch(this.baseUrl + url, data, options)\n .pipe((response: any) => response);\n }\n\n protected $put(url: string, data: any, options?: any): Observable<any> {\n return this.httpClient\n .put(this.baseUrl + url, data, options)\n .pipe((response: any) => response);\n }\n}\n\nfunction paramsSerializer(params: any) {\n return encodeParams(params, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n });\n}\n\n",
26
- "client.ejs": "@Injectable({\n providedIn: 'root'\n})\nexport class <%= it.clientName -%>Service extends BaseService {\n constructor(\n @Inject(HttpClient) httpClient: HttpClient,\n @Optional() @Inject(<%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL) baseUrl?: string\n ) {\n super(httpClient, baseUrl);\n }\n\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n}\n\n",
27
- "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(\n <% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\nconfig?: any\n ): Observable<<%~ it.returnType %>> {\n const url = `<%= it.url %>?<%\n if(it.query && it.query.length > 0) { %>${paramsSerializer({<%\n it.query.forEach((parameter) => { %>\n'<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>})}<% } %>`;\n\n return this.$<%= it.method.toLowerCase() %>(\n url,\n<% if(['POST', 'PUT', 'PATCH'].includes(it.method)) { %>\n<% if(it.body) { %>\n <%= it.body.contentType === 'urlencoded' ? 'new URLSearchParams(' + it.body.name + ' as any)' : it.body.name %>,\n<% } else { %>\n null,\n<% } %>\n<% } %>\n config\n );\n }\n"
25
+ "baseClient.ejs": "import type { Observable } from \"rxjs\";\nimport { Injectable, InjectionToken, inject } from \"@angular/core\";\nimport { HttpClient, type HttpContext, type HttpHeaders, type HttpParams } from \"@angular/common/http\";\n\nexport const <%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL = new InjectionToken<string>(\"<%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL\");\n\nexport interface HttpConfig {\n headers?: HttpHeaders | Record<string, string | string[]>;\n context?: HttpContext;\n params?: HttpParams | Record<string, string | number | boolean | (string | number | boolean)[]>;\n reportProgress?: boolean;\n withCredentials?: boolean;\n transferCache?: boolean | { includeHeaders?: string[] };\n}\n\nfunction paramsSerializer(params: any) {\n return encodeParams(params, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n });\n}\n\n",
26
+ "client.ejs": "@Injectable({\n providedIn: 'root'\n})\nexport class <%= it.clientName.charAt(0).toUpperCase() + it.clientName.slice(1) -%>Service {\n private http = inject(HttpClient);\n private baseUrl = inject(<%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL, { optional: true }) ?? '';\n\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n}\n\n",
27
+ "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(\n <% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\nconfig?: HttpConfig\n ): Observable<<%~ it.returnType %>> {\n<% if(it.query && it.query.length > 0) { -%>\n const params = paramsSerializer({\n<% it.query.forEach((parameter) => { -%>\n<% if (it.queryParamObject) { -%>\n '<%= parameter.originalName %>': <%= it.queryParamObject.name %>?.<%= parameter.name %>,\n<% } else { -%>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n<% } -%>\n<% }); -%>\n });\n const url = `<%= it.url %>${params ? '?' + params : ''}`;\n<% } else { -%>\n const url = `<%= it.url %>`;\n<% } -%>\n<% if(it.headers && it.headers.length > 0) { it.headers.forEach((parameter) => { -%>\n<% if (parameter.value) { -%>\n config = { ...config, headers: { ...config?.headers, '<%= parameter.originalName %>': '<%= parameter.value %>' } };\n<% } else { -%>\n if (<%= parameter.name %>) {\n config = { ...config, headers: { ...config?.headers, '<%= parameter.originalName %>': <%= parameter.name %> } };\n }\n<% } -%>\n<% }); } -%>\n<% if(it.method === 'GET') { -%>\n return this.http.get<<%~ it.returnType %>>(this.baseUrl + url, config);\n<% } else if(it.method === 'DELETE') { -%>\n return this.http.delete<<%~ it.returnType %>>(this.baseUrl + url, config);\n<% } else if(['POST', 'PUT', 'PATCH'].includes(it.method)) {\n const bodyArg = it.body\n ? (it.body.contentType === 'urlencoded' ? 'new URLSearchParams(' + it.body.name + ' as any)' : it.body.name)\n : 'null';\n-%>\n return this.http.<%= it.method.toLowerCase() %><%~ '<' + it.returnType + '>' %>(this.baseUrl + url, <%~ bodyArg %>, config);\n<% } %>\n }\n"
28
28
  },
29
- "swr-axios": {
30
- "barrel.ejs": "\n/**\n * Serializes a params object into a query string that is compatible with different REST APIs.\n * Implementation from: https://github.com/suhaotian/xior/blob/main/src/utils.ts\n * Kudos to @suhaotian for the original implementation\n */\nfunction encodeParams<T = any>(\n params: T,\n parentKey: string | null = null,\n options?: {\n allowDots?: boolean;\n serializeDate?: (value: Date) => string;\n arrayFormat?: 'indices' | 'repeat' | 'brackets';\n }\n): string {\n if (params === undefined || params === null) return '';\n const encodedParams: string[] = [];\n const paramsIsArray = Array.isArray(params);\n const { arrayFormat, allowDots, serializeDate } = options || {};\n\n const getKey = (key: string) => {\n if (allowDots && !paramsIsArray) return `.${key}`;\n if (paramsIsArray) {\n if (arrayFormat === 'brackets') {\n return '[]';\n }\n if (arrayFormat === 'repeat') {\n return '';\n }\n }\n return `[${key}]`;\n };\n\n for (const key in params) {\n if (Object.prototype.hasOwnProperty.call(params, key)) {\n let value = (params as any)[key];\n if (value !== undefined) {\n const encodedKey = parentKey ? `${parentKey}${getKey(key)}` : (key as string);\n\n // biome-ignore lint/suspicious/noGlobalIsNan: <explanation>\n if (!isNaN(value) && value instanceof Date) {\n value = serializeDate ? serializeDate(value) : value.toISOString();\n }\n if (typeof value === 'object') {\n // If the value is an object or array, recursively encode its contents\n const result = encodeParams(value, encodedKey, options);\n if (result !== '') encodedParams.push(result);\n } else {\n // Otherwise, encode the key-value pair\n encodedParams.push(`${encodeURIComponent(encodedKey)}=${encodeURIComponent(value)}`);\n }\n }\n }\n }\n\n return encodedParams.join('&');\n}\n\n",
31
- "baseClient.ejs": "import Axios, { type AxiosPromise, type AxiosRequestConfig } from \"axios\";\nimport useSWR, { type SWRConfiguration, type Key } from 'swr';\n\nexport const axios = Axios.create({\n baseURL: '<%= it.baseUrl || '' -%>',\n paramsSerializer: (params: any) =>\n encodeParams(params, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n});\n\ninterface SwrConfig extends SWRConfiguration {\n /* Custom key for SWR. You don't have to worry about this as by default it's the URL. You can use standard SWR Key here if you need more flexibility. */\n key?: Key;\n\n /* Configuration for axios fetcher */\n axios?: AxiosRequestConfig;\n}\n\n",
32
- "client.ejs": "export const <%= it.camelCaseName -%>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n <% }); %>\n};\n\n<% var getOperations = it.operations.filter((o) => o.method === 'GET');\nif(getOperations.length > 0) { %>\n <% getOperations.forEach((operation) => {\n var opName = operation.name;\n if(opName.toLowerCase().startsWith(\"get\")) {\n opName = opName.substring(3);\n }\n opName[0] = opName[0].toUpperCase();\n var customName = \"use\" + it.clientName + opName;\n var swrOperation = Object.assign({ swrOpName: customName }, operation); %>\n<%~ include('swrOperation.ejs', swrOperation); %>\n\n <% }); %>\n<% } %>\n",
33
- "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n $config?: AxiosRequestConfig\n ): AxiosPromise<<%~ it.returnType %>> {\n const url = `<%= it.url %>`;\n\n return axios.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'urlencoded') { %>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n data: <%= it.body.name %>,\n<% } %>\n<% } %>\n<% if(it.query && it.query.length > 0) { %>\n params: {\n <% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>\n },\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n <% it.headers.forEach((parameter) => { %>\n <% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n <% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% } %>\n <% }); %>\n},\n<% } %>\n ...$config,\n });\n },\n",
34
- "swrOperation.ejs": "<%~ it.jsDocs %>\n\nexport function <%= it.swrOpName %>(<% it.parameters.forEach((parameter) => { %>\n <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n $config?: SwrConfig\n ) {\n const url = `<%= it.url %>`;\n const { axios: $axiosConf, key, ...config } = $config || {};\n\n const cacheUrl = `${url}?<%\n if(it.query && it.query.length > 0) { %>${encodeParams({<%\n it.query.forEach((parameter) => { %>\n'<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>})}<% } %>`;\n\nconst { data, error, mutate } = useSWR<<%~ it.returnType %>>(\n key ?? cacheUrl,\n () => axios.request({\n url: url,\n method: '<%= it.method %>',\n<% if(it.query && it.query.length > 0) { %>\n params: {\n <% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>\n},\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n <% it.headers.forEach((parameter) => { %>\n <% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n <% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% } %>\n <% }); %>\n },\n<% } %>\n ...$axiosConf})\n .then((resp) => resp.data),\n config);\n\n return {\n data,\n isLoading: !error && !data,\n error: error,\n mutate,\n };\n}\n"
29
+ "swr": {
30
+ "baseClient.ejs": "import useSWR, { type SWRConfiguration, type Key } from 'swr';\nimport useSWRMutation, { type SWRMutationConfiguration } from 'swr/mutation';\n\ninterface SwrConfig extends SWRConfiguration {\n /* Custom key for SWR. You don't have to worry about this as by default it's the URL. You can use standard SWR Key here if you need more flexibility. */\n key?: Key;\n}\n",
31
+ "client.ejs": "export const <%= it.camelCaseName -%>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n <% }); %>\n};\n\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n\nfunction toOpName(name) {\n var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;\n return n.charAt(0).toUpperCase() + n.slice(1);\n}\n\nfunction safeOperation(operation, clientName) {\n var safeParams = operation.parameters.map(function(p) {\n return p.name === clientName ? Object.assign({}, p, { name: '_' + p.name }) : p;\n });\n return Object.assign({}, operation, { parameters: safeParams });\n}\n%>\n\nexport const <%= it.camelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var swrOperation = Object.assign({\n swrOpName: 'use' + opName,\n clientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('swrOperation.ejs', swrOperation); %>\n\n<% }); %>\n },\n\n mutations: {\n<% mutationOperations.forEach((operation) => {\n var opName = operation.name.charAt(0).toUpperCase() + operation.name.slice(1);\n var swrMutationOperation = Object.assign({\n mutOpName: 'use' + opName,\n clientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('swrMutationOperation.ejs', swrMutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = safeOperation(operation, it.camelCaseName);\n%>\n <%= keyName %>: (<% safeOp.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => `<%= operation.url %><% if(safeOp.query && safeOp.query.length > 0) { %>?${encodeParams({<% safeOp.query.forEach((parameter) => { %>'<%= parameter.originalName %>': <%= safeOp.queryParamObject ? `${safeOp.queryParamObject.name}?.${parameter.name}` : parameter.name %>, <% }); %>})}<% } %>`,\n<% }); %>\n },\n};\n",
32
+ "swrMutationOperation.ejs": "<%\nvar hasParams = it.parameters.length > 0;\n\nvar variablesType;\nif (!hasParams) {\n variablesType = 'void';\n} else {\n var parts = it.parameters.map(function(p) {\n return p.name + (p.skippable ? '?' : '') + ': ' + p.type + (p.optional ? ' | null' : '');\n });\n variablesType = '{ ' + parts.join('; ') + ' }';\n}\n\nvar callArgs = it.parameters.map(function(p) { return 'arg.' + p.name; });\n\n// Stable SWR key: replace all ${...} path expressions with *\nvar swrKey = it.url.replace(/\\$\\{(?:[^{}]|\\{[^{}]*\\})*\\}/g, '*');\n\nvar docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\\n' : '';\n%>\n<%~ docs %>\n <%= it.mutOpName %>(\n $config?: SWRMutationConfiguration<<%~ it.returnType %>, Error, string, <%~ variablesType %>>,\n $httpConfig?: <%= it.httpConfigType %>\n ) {\n return useSWRMutation<<%~ it.returnType %>, Error, string, <%~ variablesType %>>(\n '<%= swrKey %>',\n (_key: string, { arg }<%= hasParams ? ': { arg: ' + variablesType + ' }' : '' %>) =>\n <%= it.clientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig).then((resp) => resp.data),\n $config\n );\n },\n",
33
+ "swrOperation.ejs": "<% var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\\n' : ''; %>\n<%~ docs %>\n <%= it.swrOpName %>(\n<% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,\n<% }); %> $config?: Omit<SwrConfig, 'key'> & { key?: Key },\n $httpConfig?: <%= it.httpConfigType %>\n ) {\n const { key, ...config } = $config || {};\n const cacheUrl = key ?? <%= it.clientName %>.queryKeys.<%= it.swrOpName.charAt(3).toLowerCase() + it.swrOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>);\n\n const { data, error, isLoading, mutate } = useSWR<<%~ it.returnType %>>(\n cacheUrl,\n () => <%= it.clientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig).then((resp) => resp.data),\n config\n );\n\n return { data, isLoading, error, mutate };\n },\n"
35
34
  },
36
- "tsq-xior": {
37
- "barrel.ejs": "",
38
- "baseClient.ejs": "import xior, { type XiorResponse, type XiorRequestConfig, encodeParams } from \"xior\";\nimport { QueryClient, type UseQueryOptions, useQuery } from '@tanstack/react-query';\n\nexport const queryClient = new QueryClient();\n\nexport const http = xior.create({\n baseURL: '<%= it.baseUrl || '' %>',\n paramsSerializer: (params: any) =>\n encodeParams(params, true, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n});\n\n",
39
- "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n\n<% var getOperations = it.operations.filter((o) => o.method === 'GET');\nif(getOperations.length > 0) { %>\n <% getOperations.forEach((operation) => {\n var opName = operation.name;\n if(opName.toLowerCase().startsWith(\"get\")) {\n opName = opName.substring(3);\n }\n opName[0] = opName[0].toUpperCase();\n var customName = \"use\" + it.clientName + opName;\n var queryOperation = Object.assign({ rqOpName: customName, opKey: it.clientName + opName, clientName: it.camelCaseName }, operation); %>\n<%~ include('queryOperation.ejs', queryOperation); %>\n\n <% }); %>\n<% } %>\n",
40
- "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: XiorRequestConfig\n ): Promise<XiorResponse<<%~ it.returnType %>>> {\n const url = `<%= it.url %>`;\n\n return http.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'urlencoded') { %>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n data: <%= it.body.name %>,\n<% } %>\n<% } %>\n<% if(it.query && it.query.length > 0) { %>\n params: {\n <% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>\n },\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n <% it.headers.forEach((parameter) => { %>\n <% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n <% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% } %>\n <% }); %>\n},\n<% } %>\n ...$config,\n });\n },\n",
41
- "queryOperation.ejs": "<%\nif (it.jsDocs) {\n const additionalParams = ` * @param $config (optional) Additional configuration for TanStack Query\n * @param $httpConfig (optional) Additional configuration for xior request (actually executes the request)`;\n\n // Replace the closing */ with newline + additional params + closing */\n const modifiedDocs = it.jsDocs.replace(/(\\s*)\\*\\/\\s*$/, `\\n${additionalParams}\\n */`);\n-%>\n<%~ modifiedDocs %>\n<% } else { -%>\n<%~ it.jsDocs %>\n<% } %>\n\nexport function <%= it.rqOpName %><TData = <%~ it.returnType %>, TError = Error>(<% it.parameters.forEach((parameter) => { %>\n <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: Omit<\n UseQueryOptions<<%~ it.returnType %>, TError, TData>,\n 'queryKey' | 'queryFn'\n>,\n $httpConfig?: XiorRequestConfig\n ) {\n return useQuery<<%~ it.returnType %>, TError, TData>({\n queryKey: ['<%= it.clientName %>', '<%= it.opKey %>', <% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>],\n queryFn: () => <%= it.clientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %>, <% }); %>$httpConfig).then(res => res.data),\n ...$config\n });\n}\n<%= it.rqOpName %>.queryKeys = ['<%= it.clientName %>', '<%= it.opKey %>'];\n"
35
+ "tsq": {
36
+ "baseClient.ejs": "import { type UseQueryOptions, type UseMutationOptions, useQuery, useMutation } from '@tanstack/react-query';\n",
37
+ "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n\n// Helper: strip leading \"get\" and capitalise\nfunction toOpName(name) {\n var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;\n return n.charAt(0).toUpperCase() + n.slice(1);\n}\n\n// Returns a deep copy of operation with any parameter whose name collides with\n// the group object name (clientName) renamed to _<name> to avoid TS shadowing.\nfunction safeOperation(operation, clientName) {\n var safeParams = operation.parameters.map(function(p) {\n return p.name === clientName ? Object.assign({}, p, { name: '_' + p.name }) : p;\n });\n return Object.assign({}, operation, { parameters: safeParams });\n}\n%>\n\nexport const <%= it.camelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var queryOperation = Object.assign({\n rqOpName: 'use' + opName,\n opKey: it.camelCaseName + opName,\n clientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('queryOperation.ejs', queryOperation); %>\n\n<% }); %>\n },\n\n mutations: {\n<% mutationOperations.forEach((operation) => {\n var opName = operation.name.charAt(0).toUpperCase() + operation.name.slice(1);\n var mutationOperation = Object.assign({\n mutOpName: 'use' + opName,\n clientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('mutationOperation.ejs', mutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = safeOperation(operation, it.camelCaseName);\n%>\n <%= keyName %>: (<% safeOp.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => ['<%= it.camelCaseName %>', '<%= it.camelCaseName + opName %>'<% safeOp.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,\n<% }); %>\n },\n};\n",
38
+ "mutationOperation.ejs": "<%\nvar hasParams = it.parameters.length > 0;\n\nvar variablesType;\nif (!hasParams) {\n variablesType = 'void';\n} else {\n var parts = it.parameters.map(function(p) {\n return p.name + (p.skippable ? '?' : '') + ': ' + p.type + (p.optional ? ' | null' : '');\n });\n variablesType = '{ ' + parts.join('; ') + ' }';\n}\n\nvar callArgs = it.parameters.map(function(p) { return 'vars.' + p.name; });\n\nvar baseAdditionalParams = ' * @param $config (optional) Additional configuration for TanStack Query\\n * @param $httpConfig (optional) Additional HTTP client configuration (passed to the underlying ' + it.httpConfigType + ')';\nvar rawDocs = it.jsDocs\n ? it.jsDocs.replace(/(\\s*)\\*\\/\\s*$/, '\\n' + baseAdditionalParams + '\\n */')\n : '';\nvar docs = rawDocs ? rawDocs.replace(/^/gm, ' ') + '\\n' : '';\n%>\n<%~ docs %>\n <%= it.mutOpName %><TData = <%~ it.returnType %>, TError = Error>(\n $config?: UseMutationOptions<<%~ it.returnType %>, TError, <%~ variablesType %>>,\n $httpConfig?: <%= it.httpConfigType %>\n ) {\n return useMutation<<%~ it.returnType %>, TError, <%~ variablesType %>>({\n mutationFn: (<%= hasParams ? 'vars' : '' %>) => <%= it.clientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig).then(res => res.data),\n ...$config\n });\n },\n",
39
+ "queryOperation.ejs": "<%\nvar baseAdditionalParams = ' * @param $config (optional) Additional configuration for TanStack Query\\n * @param $httpConfig (optional) Additional HTTP client configuration (passed to the underlying ' + it.httpConfigType + ')';\nvar rawDocs = it.jsDocs\n ? it.jsDocs.replace(/(\\s*)\\*\\/\\s*$/, '\\n' + baseAdditionalParams + '\\n */')\n : '';\nvar docs = rawDocs ? rawDocs.replace(/^/gm, ' ') + '\\n' : '';\n%>\n<%~ docs %>\n <%= it.rqOpName %><TData = <%~ it.returnType %>, TError = Error>(\n<% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,\n<% }); %> $config?: Omit<UseQueryOptions<<%~ it.returnType %>, TError, TData>, 'queryKey' | 'queryFn'>,\n $httpConfig?: <%= it.httpConfigType %>\n ) {\n return useQuery<<%~ it.returnType %>, TError, TData>({\n queryKey: <%= it.clientName %>.queryKeys.<%= it.rqOpName.charAt(3).toLowerCase() + it.rqOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>),\n queryFn: () => <%= it.clientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig).then(res => res.data),\n ...$config\n });\n },\n"
42
40
  },
43
41
  "xior": {
44
42
  "barrel.ejs": "",
45
43
  "baseClient.ejs": "import xior, { type XiorResponse, type XiorRequestConfig, encodeParams } from \"xior\";\n\nexport const http = xior.create({\n baseURL: '<%= it.baseUrl || '' %>',\n paramsSerializer: (params: any) =>\n encodeParams(params, true, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n});\n\n",
46
44
  "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n",
47
- "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: XiorRequestConfig\n ): Promise<XiorResponse<<%~ it.returnType %>>> {\n const url = `<%= it.url %>`;\n\n return http.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'urlencoded') { %>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n data: <%= it.body.name %>,\n<% } %>\n<% } %>\n<% if(it.query && it.query.length > 0) { %>\n params: {\n <% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>\n },\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n <% it.headers.forEach((parameter) => { %>\n <% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n <% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% } %>\n <% }); %>\n},\n<% } %>\n ...$config,\n });\n },\n"
45
+ "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: XiorRequestConfig\n ): Promise<XiorResponse<<%~ it.returnType %>>> {\n const url = `<%= it.url %>`;\n\n return http.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { -%>\n<% if(it.body.contentType === 'urlencoded') { -%>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { -%>\n data: <%= it.body.name %>,\n<% } -%>\n<% } -%>\n<% if(it.query && it.query.length > 0) { -%>\n params: {\n<% it.query.forEach((parameter) => { -%>\n<% if (it.queryParamObject) { -%>\n '<%= parameter.originalName %>': <%= it.queryParamObject.name %>?.<%= parameter.name %>,\n<% } else { -%>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n<% } -%>\n<% }); -%>\n },\n<% } -%>\n<% if(it.headers && it.headers.length > 0) { -%>\n headers: {\n<% it.headers.forEach((parameter) => { -%>\n<% if (parameter.value) { -%>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n<% } else { -%>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n<% } -%>\n<% }); -%>\n },\n<% } -%>\n ...$config,\n });\n },\n"
48
46
  }
49
47
  } ; exports.BUNDLED_TEMPLATES = BUNDLED_TEMPLATES;
50
48
 
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  var _gen = require('./gen'); var _gen2 = _interopRequireDefault(_gen);
5
5
 
6
6
  var _utils = require('./utils');
7
+ var _templateValidator = require('./utils/templateValidator');
7
8
  var _swagger = require('./swagger');
8
9
 
9
10
  /**
@@ -31,10 +32,19 @@ function verifyOptions(options) {
31
32
  if (!!options.config === !!options.src) {
32
33
  throw new Error('You need to provide either --config or --src parameters');
33
34
  }
35
+ if (!!options.mocks !== !!options.testingFramework) {
36
+ throw new Error('--mocks and --testingFramework must be used together');
37
+ }
38
+ if (options.mocks && !options.out) {
39
+ throw new Error(
40
+ '--mocks requires --out to be set, since the mock file needs to import the generated client'
41
+ );
42
+ }
34
43
  }
35
44
 
36
45
  function gen(spec, options) {
37
46
  if (options.generationMode === 'full') {
47
+ _templateValidator.validateTemplate.call(void 0, options.template);
38
48
  _utils.loadAllTemplateFiles.call(void 0, options.template);
39
49
  }
40
50
 
@@ -83,6 +93,7 @@ function readFile(filePath) {
83
93
  const {
84
94
  allowDots,
85
95
  arrayFormat,
96
+ queryParamsAsObject,
86
97
  mode,
87
98
  schemaStyle,
88
99
  enumStyle,
@@ -90,6 +101,8 @@ function readFile(filePath) {
90
101
  nullables,
91
102
  template,
92
103
  queryParamsSerialization = {},
104
+ mocks,
105
+ testingFramework,
93
106
  ...rest
94
107
  } = cliOpts;
95
108
  const mergedQueryParamsSerialization = {
@@ -99,11 +112,12 @@ function readFile(filePath) {
99
112
  ),
100
113
  ...(allowDots !== undefined ? { allowDots } : {}),
101
114
  ...(arrayFormat !== undefined ? { arrayFormat } : {}),
115
+ ...(queryParamsAsObject !== undefined ? { queryParamsAsObject } : {}),
102
116
  };
103
117
 
104
118
  return {
105
119
  ...rest,
106
- template: _nullishCoalesce(template, () => ( _swagger.APP_DEFAULTS.template)),
120
+ template: _templateValidator.normalizeTemplate.call(void 0, _nullishCoalesce(template, () => ( _swagger.APP_DEFAULTS.template))),
107
121
  servicePrefix: _nullishCoalesce(rest.servicePrefix, () => ( _swagger.APP_DEFAULTS.servicePrefix)),
108
122
  nullableStrategy: _nullishCoalesce(_nullishCoalesce(nullables, () => ( rest.nullableStrategy)), () => ( _swagger.APP_DEFAULTS.nullableStrategy)),
109
123
  generationMode: _nullishCoalesce(_nullishCoalesce(mode, () => ( rest.generationMode)), () => ( _swagger.APP_DEFAULTS.generationMode)),
@@ -113,6 +127,8 @@ function readFile(filePath) {
113
127
  _nullishCoalesce(_nullishCoalesce(enumStyle, () => ( rest.enumDeclarationStyle)), () => ( _swagger.APP_DEFAULTS.enumDeclarationStyle)),
114
128
  enumNamesStyle: normalizeEnumNamesStyle(enumNamesStyle),
115
129
  queryParamsSerialization: mergedQueryParamsSerialization,
130
+ ...(mocks !== undefined ? { mocks } : {}),
131
+ ...(testingFramework !== undefined ? { testingFramework } : {}),
116
132
  };
117
133
  } exports.prepareAppOptions = prepareAppOptions;
118
134
 
@@ -53,11 +53,15 @@ function inheritPathParams(op, inheritableParams) {
53
53
  return;
54
54
  }
55
55
 
56
+ if (!op.parameters) {
57
+ op.parameters = [];
58
+ }
59
+
56
60
  for (const pathParam of inheritableParams) {
57
61
  // If the operation doesn't have a parameter with the same name and in, then add it
58
62
  if (
59
63
  !op.parameters.some(
60
- (p) => p.name === pathParam.name && p.in === pathParam.in
64
+ (p) => !('$ref' in p) && (p ).name === pathParam.name && (p ).in === pathParam.in
61
65
  )
62
66
  ) {
63
67
  op.parameters.push(Object.assign({}, pathParam));
@@ -70,9 +74,10 @@ function getPathOperation(
70
74
  pathInfo,
71
75
  spec
72
76
  ) {
77
+ const pathOp = pathInfo[method];
73
78
  const op = Object.assign(
74
- { method, path: pathInfo.path, parameters: [], group: getOperationGroupName(pathInfo[method]) },
75
- pathInfo[method]
79
+ { method, path: pathInfo.path, parameters: [], group: getOperationGroupName(pathOp) },
80
+ pathOp
76
81
  );
77
82
 
78
83
  // if there's no explicit operationId given, create one based on the method and path
@@ -83,13 +88,13 @@ function getPathOperation(
83
88
  .replace(/[\/}\-]/g, '');
84
89
  }
85
90
 
86
- const pathLevelParams = _nullishCoalesce(spec.paths[pathInfo.path].parameters, () => ( []));
91
+ const pathLevelParams = _nullishCoalesce(_optionalChain([spec, 'access', _2 => _2.paths, 'optionalAccess', _3 => _3[pathInfo.path], 'optionalAccess', _4 => _4.parameters]), () => ( []));
87
92
 
88
93
  // Replace the path level parameters references with the actual parameters
89
- replaceReferencedParams(pathLevelParams, _nullishCoalesce(_optionalChain([spec, 'access', _2 => _2.components, 'optionalAccess', _3 => _3.parameters]), () => ( {})));
94
+ replaceReferencedParams(pathLevelParams, _nullishCoalesce(_optionalChain([spec, 'access', _5 => _5.components, 'optionalAccess', _6 => _6.parameters]), () => ( {})));
90
95
 
91
96
  // Replace the operation parameters references with the actual parameters
92
- replaceReferencedParams(op.parameters, _nullishCoalesce(_optionalChain([spec, 'access', _4 => _4.components, 'optionalAccess', _5 => _5.parameters]), () => ( {})));
97
+ replaceReferencedParams(op.parameters, _nullishCoalesce(_optionalChain([spec, 'access', _7 => _7.components, 'optionalAccess', _8 => _8.parameters]), () => ( {})));
93
98
 
94
99
  // At this stage path level parameters are already replaced with the actual parameters
95
100
  inheritPathParams(op, pathLevelParams );
@@ -103,7 +108,7 @@ function getPathOperation(
103
108
  * Removes invalid characters and ensures the name doesn't start with a number.
104
109
  */
105
110
  function getOperationGroupName(op) {
106
- let name = _optionalChain([op, 'access', _6 => _6.tags, 'optionalAccess', _7 => _7.length]) ? op.tags[0] : 'default';
111
+ let name = _optionalChain([op, 'access', _9 => _9.tags, 'optionalAccess', _10 => _10.length]) ? op.tags[0] : 'default';
107
112
  name = name.replace(/[^$_a-z0-9]+/gi, '');
108
113
  return name.replace(/^[0-9]+/m, '');
109
114
  }
@@ -117,6 +122,10 @@ function replaceReferencedParams(
117
122
  parameters,
118
123
  componentParams
119
124
  ) {
125
+ if (!parameters || !componentParams) {
126
+ return;
127
+ }
128
+
120
129
  for (let index = 0; index < parameters.length; index++) {
121
130
  const param = parameters[index];
122
131
 
@@ -25,7 +25,7 @@ var _utils = require('../utils/utils');
25
25
  return unknownType;
26
26
  }
27
27
 
28
- return getTypeFromSchemaResolved(param.schema, options, `${context}.schema`);
28
+ return getTypeFromSchemaResolved(param.schema , options, `${context}.schema`);
29
29
  } exports.getParameterType = getParameterType;
30
30
 
31
31
  /**
@@ -172,7 +172,7 @@ function getTypeFromSchemaInternal(
172
172
  return getTypeFromObject(schema, options, undefined, context);
173
173
  }
174
174
  if ('enum' in schema) {
175
- return `${schema.enum.map((v) => JSON.stringify(v)).join(' | ')}`;
175
+ return `${(_nullishCoalesce(schema.enum, () => ( []))).map((v) => JSON.stringify(v)).join(' | ')}`;
176
176
  }
177
177
  if (schema.type === 'integer' || schema.type === 'number') {
178
178
  return 'number';
@@ -368,25 +368,26 @@ function isRequiredOnlyCompositeBranch(schema) {
368
368
  * It is used only for `allOf` property, as it enforces extending types.
369
369
  */
370
370
  function getRefCompositeTypes(schema) {
371
- return schema.allOf
371
+ return (_nullishCoalesce(schema.allOf, () => ( [])))
372
372
  .filter((v) => '$ref' in v)
373
373
  .map((s) => getSafeIdentifier(s.$ref.split('/').pop()));
374
374
  } exports.getRefCompositeTypes = getRefCompositeTypes;
375
375
 
376
376
  /** Default values applied to every field of AppOptions that has a default. */
377
377
  const APP_DEFAULTS = {
378
- template: 'axios',
378
+ template: 'axios' ,
379
379
  servicePrefix: '',
380
- nullableStrategy: 'ignore',
381
- generationMode: 'full',
382
- schemaDeclarationStyle: 'interface',
383
- enumDeclarationStyle: 'union',
384
- enumNamesStyle: 'original',
380
+ nullableStrategy: 'ignore' ,
381
+ generationMode: 'full' ,
382
+ schemaDeclarationStyle: 'interface' ,
383
+ enumDeclarationStyle: 'union' ,
384
+ enumNamesStyle: 'original' ,
385
385
  queryParamsSerialization: {
386
386
  allowDots: true,
387
- arrayFormat: 'repeat',
387
+ arrayFormat: 'repeat' ,
388
+ queryParamsAsObject: false ,
388
389
  },
389
- }; exports.APP_DEFAULTS = APP_DEFAULTS;
390
+ } ; exports.APP_DEFAULTS = APP_DEFAULTS;
390
391
 
391
392
  /**
392
393
  * Fills in all AppOptions defaults for a partial ClientOptions object.
package/dist/types.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { OpenAPIV3 as OA3 } from 'openapi-types';
2
2
  interface QueryParamsSerializationOptions {
3
3
  allowDots?: boolean;
4
4
  arrayFormat?: ArrayFormat;
5
+ queryParamsAsObject?: boolean | number;
5
6
  }
6
7
  export interface ClientOptions {
7
8
  /**
@@ -11,8 +12,14 @@ export interface ClientOptions {
11
12
  src: string | object;
12
13
  /** Path to the file which will contain generated TypeScript code */
13
14
  out?: string;
14
- /** Template to be used for code generation */
15
- template: Template;
15
+ /**
16
+ * Template to be used for code generation.
17
+ * Can be a single L1 template name (e.g. "axios"), a single L2 template name
18
+ * (e.g. "swr" — defaults to "fetch" as the L1), or a 2-element tuple of
19
+ * [L2, L1] (e.g. ["swr", "axios"]).
20
+ * Custom filesystem paths are also accepted in any position.
21
+ */
22
+ template: TemplateInput;
16
23
  baseUrl?: string;
17
24
  preferAny?: boolean;
18
25
  /** Skip deprecated operations. When enabled, deprecated operations will be skipped from the generated code */
@@ -42,6 +49,26 @@ export interface ClientOptions {
42
49
  * - `'PascalCase'` — convert values to PascalCase (e.g. `OrgName = "org name"`)
43
50
  */
44
51
  enumNamesStyle?: EnumNamesStyle;
52
+ /**
53
+ * Prepends `'use client';` as the very first line of the generated file.
54
+ * Required for Next.js App Router when using SWR or TanStack Query hooks,
55
+ * which can only run in Client Components.
56
+ * Has no effect and should not be used with non-RSC environments.
57
+ */
58
+ useClient?: boolean;
59
+ /**
60
+ * Output path for the generated mock/stub file. Requires `testingFramework`
61
+ * and `out` to also be set. When provided, a companion mock file is generated
62
+ * alongside the main client, exporting typed spy stubs for every operation.
63
+ */
64
+ mocks?: string;
65
+ /**
66
+ * The test framework to use for generated mock stubs.
67
+ * - `'vitest'` — uses `vi.fn()` from `vitest`
68
+ * - `'jest'` — uses `jest.fn()` from `@jest/globals`
69
+ * Requires `mocks` and `out` to also be set.
70
+ */
71
+ testingFramework?: TestingFramework;
45
72
  /** Offers ability to adjust the OpenAPI spec before it is processed */
46
73
  modifiers?: {
47
74
  /** Global-level modifiers for parameter with a given name */
@@ -53,18 +80,37 @@ export interface ClientOptions {
53
80
  export interface CliOptions extends Omit<FullAppOptions, 'enumNamesStyle'> {
54
81
  allowDots?: boolean;
55
82
  arrayFormat?: ArrayFormat;
83
+ queryParamsAsObject?: boolean | number;
56
84
  mode?: GenerationMode;
57
85
  schemaStyle?: SchemaDeclarationStyle;
58
86
  enumStyle?: EnumDeclarationStyle;
59
87
  /** Accepts 'original', 'PascalCase', or 'pascal' (normalized to 'PascalCase') */
60
88
  enumNamesStyle?: string;
61
89
  nullables?: NullableStrategy;
90
+ mocks?: string;
91
+ testingFramework?: TestingFramework;
62
92
  }
63
93
  export interface FullAppOptions extends ClientOptions {
64
94
  /** Path to the configuration file that contains actual config to be used */
65
95
  config?: string;
66
96
  }
67
- export type Template = 'axios' | 'fetch' | 'ng1' | 'ng2' | 'swr-axios' | 'xior' | 'tsq-xior';
97
+ /** HTTP client templates (standalone, no reactive layer) */
98
+ export type L1Template = 'axios' | 'fetch' | 'xior' | 'ng1' | 'ng2';
99
+ /** Reactive query layer templates (must be composed with an L1 template) */
100
+ export type L2Template = 'swr' | 'tsq';
101
+ /** Any named built-in template */
102
+ export type Template = L1Template | L2Template;
103
+ /**
104
+ * What the user may supply as a `template` value:
105
+ * - a single template name or custom path string
106
+ * - a [L2, L1] pair (names or custom paths)
107
+ */
108
+ export type TemplateInput = Template | string | [string, string];
109
+ /**
110
+ * After normalization inside prepareAppOptions the template is always
111
+ * either a single L1 string or a resolved [L2, L1] pair.
112
+ */
113
+ export type ResolvedTemplate = L1Template | string | [string, string];
68
114
  export type HttpMethod = 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch';
69
115
  export type DateSupport = 'string' | 'Date';
70
116
  export type ArrayFormat = 'indices' | 'repeat' | 'brackets';
@@ -73,13 +119,14 @@ export type GenerationMode = 'full' | 'schemas';
73
119
  export type SchemaDeclarationStyle = 'interface' | 'type';
74
120
  export type EnumDeclarationStyle = 'union' | 'enum';
75
121
  export type EnumNamesStyle = 'original' | 'PascalCase';
122
+ export type TestingFramework = 'vitest' | 'jest';
76
123
  /**
77
124
  * Internal options type used throughout the app after `prepareAppOptions` has run.
78
125
  * All fields that have defaults are required here so the rest of the codebase never
79
126
  * needs to perform its own `?? fallback` logic.
80
127
  */
81
128
  export interface AppOptions extends ClientOptions {
82
- template: Template;
129
+ template: ResolvedTemplate;
83
130
  servicePrefix: string;
84
131
  nullableStrategy: NullableStrategy;
85
132
  generationMode: GenerationMode;
@@ -89,7 +136,10 @@ export interface AppOptions extends ClientOptions {
89
136
  queryParamsSerialization: {
90
137
  allowDots: boolean;
91
138
  arrayFormat: ArrayFormat;
139
+ queryParamsAsObject: boolean | number;
92
140
  };
141
+ mocks?: string;
142
+ testingFramework?: TestingFramework;
93
143
  }
94
144
  /**
95
145
  * Local type that represent Operation as understood by Swaggie
@@ -97,6 +147,6 @@ export interface AppOptions extends ClientOptions {
97
147
  export interface ApiOperation extends OA3.OperationObject {
98
148
  method: HttpMethod;
99
149
  path: string;
100
- group: string;
150
+ group: string | null;
101
151
  }
102
152
  export {};
@@ -35,9 +35,7 @@ async function loadFromUrl(url) {
35
35
 
36
36
  async function readLocalFile(filePath) {
37
37
  const contents = await new Promise((res, rej) =>
38
- _nodefs2.default.readFile(filePath, 'utf8', (err, loadedContents) =>
39
- err ? rej(err) : res(loadedContents)
40
- )
38
+ _nodefs2.default.readFile(filePath, 'utf8', (err, loadedContents) => (err ? rej(err) : res(loadedContents)))
41
39
  );
42
40
  const spec = parseFileContents(contents , filePath) ;
43
41