swaggie 2.2.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -396,6 +396,21 @@ Sometimes an API spec marks a parameter as required, but your client handles it
396
396
  - `"optional"` — the parameter becomes optional regardless of what the spec says
397
397
  - `"required"` — the parameter is always required (generally better to just fix the spec)
398
398
 
399
+ ### Excluding Operations
400
+
401
+ Use `exclude` to drop entire operations from the generated output by tag or `operationId` — without modifying the spec. Types used exclusively by excluded operations are pruned automatically. Both fields support `*` and `?` wildcards.
402
+
403
+ ```json
404
+ {
405
+ "exclude": {
406
+ "tags": ["admin", "internal"],
407
+ "operationIds": ["deleteAccount", "admin*"]
408
+ }
409
+ }
410
+ ```
411
+
412
+ See the [full documentation](https://yhnavein.github.io/swaggie/guide/advanced#excluding-operations) for details and examples.
413
+
399
414
  ### Code Quality
400
415
 
401
416
  Swaggie's output is functional but not always perfectly formatted, since it uses a templating engine internally. It is strongly recommended to run the output through a formatter to ensure consistent style across regenerations.
package/dist/cli.js CHANGED
@@ -45,6 +45,12 @@ const queryParamsAsObjectOption = new (0, _commander.Option)(
45
45
  '--queryParamsAsObject [threshold]',
46
46
  'Group query params into a single object; pass a number to group only when query params count is greater than threshold'
47
47
  ).argParser(parseQueryParamsAsObjectArg);
48
+ const responseShapeOption = new (0, _commander.Option)(
49
+ '--responseShape <shape>',
50
+ "Standardize operation return shape across templates: 'body' returns the body (T); " +
51
+ "'full' returns an APIResponse<T> wrapper with data, headers and statusCode. " +
52
+ 'When omitted, each template keeps its current default return shape.'
53
+ ).choices(['body', 'full']);
48
54
 
49
55
  const program = new (0, _commander.Command)();
50
56
  program
@@ -101,6 +107,7 @@ program
101
107
  .addOption(dateFormatOption)
102
108
  .addOption(nullableStrategyOption)
103
109
  .addOption(queryParamsAsObjectOption)
110
+ .addOption(responseShapeOption)
104
111
  .option(
105
112
  '--hooksOut <filePath>',
106
113
  'Output path for the generated hooks file (L2 templates only). ' +
@@ -287,14 +287,9 @@ function withMockSWR<Fn extends (...args: never[]) => unknown>(spy: ${ref}.Spied
287
287
  /** Augments a spy with a \`mockSWRMutation\` shorthand for useSWRMutation hooks. */
288
288
  function withMockSWRMutation<Fn extends (...args: never[]) => unknown>(spy: ${ref}.SpiedFunction<Fn>) {
289
289
  return Object.assign(spy, {
290
- mockSWRMutation({
291
- data,
292
- ...rest
293
- }: MockSWRMutationReturn) {
290
+ mockSWRMutation({ ...rest }: MockSWRMutationReturn) {
294
291
  spy.mockReturnValue({
295
292
  ...defaultSWRMutationReturn,
296
- trigger: ${ref}.fn(() => Promise.resolve(undefined)),
297
- data,
298
293
  ...rest,
299
294
  } as ReturnType<Fn>);
300
295
  },
@@ -23,6 +23,16 @@ var _header = require('./header');
23
23
 
24
24
  var _jsDocs = require('./jsDocs');
25
25
 
26
+ /**
27
+ * Name of the generated wrapper interface emitted when `responseShape: 'full'`.
28
+ * Exposed to templates as `it.apiResponseType` so the name lives in one place.
29
+ *
30
+ * NOTE: like the 3rd-party type imports (e.g. `AxiosRequestConfig`), this name is
31
+ * not currently guarded against collisions with a user-defined schema of the same
32
+ * name. Keep it stable and reasonably unique.
33
+ */
34
+ const API_RESPONSE_TYPE = 'APIResponse';
35
+
26
36
  /**
27
37
  * Function that will analyze paths in the spec and generate the code for all the operations.
28
38
  *
@@ -45,6 +55,8 @@ var _jsDocs = require('./jsDocs');
45
55
  servicePrefix,
46
56
  baseUrl: options.baseUrl,
47
57
  ...options.queryParamsSerialization,
58
+ responseShape: options.responseShape,
59
+ apiResponseType: API_RESPONSE_TYPE,
48
60
  // For the ky+setup variant, embed the relative import to the setup file
49
61
  ...(isKyWithSetup && relativeSetupImport ? { relativeSetupImport } : {}),
50
62
  };
@@ -81,7 +93,9 @@ var _jsDocs = require('./jsDocs');
81
93
  ...clientData,
82
94
  servicePrefix,
83
95
  httpConfigType: _templateValidator.getHttpConfigType.call(void 0, options.template),
84
- responseMapper: _templateValidator.getResponseMapper.call(void 0, options.template),
96
+ responseMapper: _templateValidator.getResponseMapper.call(void 0, options.template, options.responseShape),
97
+ responseShape: options.responseShape,
98
+ apiResponseType: API_RESPONSE_TYPE,
85
99
  // For the ky+setup variant, operations call getKyHttp() instead of the
86
100
  // module-level `http` singleton.
87
101
  httpAccessor: isKyWithSetup ? 'getKyHttp()' : 'http',
@@ -157,6 +171,8 @@ var _jsDocs = require('./jsDocs');
157
171
  servicePrefix,
158
172
  baseUrl: options.baseUrl,
159
173
  ...options.queryParamsSerialization,
174
+ responseShape: options.responseShape,
175
+ apiResponseType: API_RESPONSE_TYPE,
160
176
  };
161
177
 
162
178
  // L2 base client contains the reactive library imports (useSWR, useQuery, etc.)
@@ -191,7 +207,9 @@ var _jsDocs = require('./jsDocs');
191
207
  ...clientData,
192
208
  servicePrefix,
193
209
  httpConfigType: _templateValidator.getHttpConfigType.call(void 0, options.template),
194
- responseMapper: _templateValidator.getResponseMapper.call(void 0, options.template),
210
+ responseMapper: _templateValidator.getResponseMapper.call(void 0, options.template, options.responseShape),
211
+ responseShape: options.responseShape,
212
+ apiResponseType: API_RESPONSE_TYPE,
195
213
  // Template helper functions — defined once here, used in all L2 templates.
196
214
  toOpName,
197
215
  safeOperation,
@@ -255,6 +273,18 @@ function prepareClient(
255
273
  ops = ops.filter((op) => !op.deprecated);
256
274
  }
257
275
 
276
+ if (_optionalChain([options, 'access', _2 => _2.exclude, 'optionalAccess', _3 => _3.tags, 'optionalAccess', _4 => _4.length])) {
277
+ ops = ops.filter(
278
+ (op) => !_optionalChain([op, 'access', _5 => _5.tags, 'optionalAccess', _6 => _6.some, 'call', _7 => _7((t) => options.exclude.tags.some((p) => matchesPattern(t, p)))])
279
+ );
280
+ }
281
+
282
+ if (_optionalChain([options, 'access', _8 => _8.exclude, 'optionalAccess', _9 => _9.operationIds, 'optionalAccess', _10 => _10.length])) {
283
+ ops = ops.filter(
284
+ (op) => !options.exclude.operationIds.some((p) => matchesPattern(_nullishCoalesce(op.operationId, () => ( '')), p))
285
+ );
286
+ }
287
+
258
288
  return ops.map((op) => {
259
289
  const operationContext = `${op.method.toUpperCase()} ${op.path} (${op.operationId || 'unknown operationId'})`;
260
290
 
@@ -295,9 +325,9 @@ function prepareClient(
295
325
 
296
326
  const headers = getParams(op.parameters , options, ['header']);
297
327
  // Some libraries need explicit Content-Type for request bodies.
298
- if (_optionalChain([body, 'optionalAccess', _2 => _2.contentType]) === 'urlencoded') {
328
+ if (_optionalChain([body, 'optionalAccess', _11 => _11.contentType]) === 'urlencoded') {
299
329
  upsertFixedHeader(headers, 'Content-Type', 'application/x-www-form-urlencoded');
300
- } else if (_optionalChain([body, 'optionalAccess', _3 => _3.contentType]) === 'json' && _templateValidator.getL1Template.call(void 0, options.template) === 'fetch') {
330
+ } else if (_optionalChain([body, 'optionalAccess', _12 => _12.contentType]) === 'json' && _templateValidator.getL1Template.call(void 0, options.template) === 'fetch') {
301
331
  upsertFixedHeader(headers, 'Content-Type', 'application/json');
302
332
  }
303
333
 
@@ -485,10 +515,10 @@ function prepareUrl(path) {
485
515
  type: _swagger.getParameterType.call(void 0, p, options),
486
516
  optional: p.required === undefined || p.required === null ? true : !p.required,
487
517
  original: p,
488
- jsDoc: _optionalChain([p, 'access', _4 => _4.description, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]),
518
+ jsDoc: _optionalChain([p, 'access', _13 => _13.description, 'optionalAccess', _14 => _14.trim, 'call', _15 => _15()]),
489
519
  }));
490
520
 
491
- if (_optionalChain([options, 'access', _7 => _7.modifiers, 'optionalAccess', _8 => _8.parameters])) {
521
+ if (_optionalChain([options, 'access', _16 => _16.modifiers, 'optionalAccess', _17 => _17.parameters])) {
492
522
  for (const [name, modifier] of Object.entries(options.modifiers.parameters)) {
493
523
  const paramIndex = result.findIndex(
494
524
  (p) => p.original.in !== 'path' && (p.originalName === name || p.name === name)
@@ -539,7 +569,7 @@ function getRequestBody(
539
569
  let reqBody;
540
570
  if ('$ref' in rawReqBody) {
541
571
  const refName = rawReqBody.$ref.replace('#/components/requestBodies/', '');
542
- const resolved = _optionalChain([components, 'optionalAccess', _9 => _9.requestBodies, 'optionalAccess', _10 => _10[refName]]);
572
+ const resolved = _optionalChain([components, 'optionalAccess', _18 => _18.requestBodies, 'optionalAccess', _19 => _19[refName]]);
543
573
  if (!resolved || '$ref' in resolved) {
544
574
  console.error(`RequestBody $ref '${rawReqBody.$ref}' not found in components/requestBodies`);
545
575
  return null;
@@ -673,6 +703,23 @@ function getL1HttpTypeImport(template) {
673
703
  }
674
704
  }
675
705
 
706
+ /**
707
+ * Matches a value against a pattern that may contain `*` (any sequence of
708
+ * characters) or `?` (any single character). Falls back to exact equality for
709
+ * patterns that contain neither wildcard (fast path).
710
+ */
711
+ function matchesPattern(value, pattern) {
712
+ if (!pattern.includes('*') && !pattern.includes('?')) {
713
+ return value === pattern;
714
+ }
715
+ const regex = new RegExp(
716
+ '^' +
717
+ pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.') +
718
+ '$'
719
+ );
720
+ return regex.test(value);
721
+ } exports.matchesPattern = matchesPattern;
722
+
676
723
  function upsertFixedHeader(headers, headerName, value) {
677
724
  const headerIndex = headers.findIndex(
678
725
  (header) => header.originalName.toLowerCase() === headerName.toLowerCase()
@@ -4,58 +4,58 @@
4
4
  const BUNDLED_TEMPLATES = {
5
5
  "axios": {
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 */\nexport function 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
- "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",
7
+ "baseClient.ejs": "import Axios, { type AxiosPromise, type AxiosRequestConfig<% if (it.responseShape === 'full') { %>, type AxiosResponse<% } %> } 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<% if (it.responseShape === 'full') { %>\nexport interface <%= it.apiResponseType %><T> {\n data: T;\n headers: AxiosResponse<T>['headers'];\n status: number;\n}\n<% } %>\n\n",
8
8
  "baseClientSetup.ejs": "/**\n * Axios client setup — GENERATED ONCE, will NOT be overwritten on subsequent runs.\n * Re-generate intentionally with: swaggie ... --clientSetup <path> --forceSetup\n *\n * This file is a write-once scaffold for configuring the Axios instance exported\n * from the generated API client. It is NOT imported by api.ts — call\n * `setupApiClient()` once at your application startup.\n *\n * Usage in your app:\n * import { setupApiClient } from '<%= it.relativeSetupImport %>';\n * setupApiClient(); // call once before any API requests\n *\n * The setup function can accept any parameters your app needs (e.g. an auth ref):\n * export function setupApiClient(authRef: AuthRef): () => void { ... }\n */\nimport { axios } from '<%= it.relativeApiImport %>';\n\n/**\n * Configures the shared Axios instance with request/response interceptors.\n * Returns a cleanup function that ejects the interceptors — useful for\n * React effects or other lifecycle-managed contexts.\n */\nexport function setupApiClient(): () => void {\n const requestInterceptor = axios.interceptors.request.use(\n (config) => {\n // TODO: Add request interceptors, e.g. attach an Authorization header:\n // config.headers.Authorization = `Bearer ${getToken()}`;\n return config;\n },\n (error) => Promise.reject(error)\n );\n\n const responseInterceptor = axios.interceptors.response.use(\n (response) => response,\n (error) => {\n // TODO: Add response error handling, e.g. redirect on 401:\n // if (error.response?.status === 401) { loginWithRedirect(); }\n return Promise.reject(error);\n }\n );\n\n return () => {\n axios.interceptors.request.eject(requestInterceptor);\n axios.interceptors.response.eject(responseInterceptor);\n };\n}\n",
9
9
  "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n",
10
- "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
+ "operation.ejs": "<%\nvar returnTypeWrapped = 'AxiosPromise<' + it.returnType + '>';\nvar responseMapper = '';\nif (it.responseShape === 'full') {\n // AxiosResponse<T> already exposes { data, headers, status } and is therefore\n // structurally assignable to APIResponse<T> — no remap needed.\n returnTypeWrapped = 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>';\n} else if (it.responseShape === 'body') {\n returnTypeWrapped = 'Promise<' + it.returnType + '>';\n responseMapper = '.then((resp) => resp.data)';\n}\n%>\n<%~ 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 ): <%~ returnTypeWrapped %> {\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 })<%~ responseMapper %>;\n },\n"
11
11
  },
12
12
  "fetch": {
13
13
  "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 */\nexport function 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",
14
- "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
+ "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<% if (it.responseShape === 'full') { %>\nexport interface <%= it.apiResponseType %><T> {\n data: T;\n headers: Headers;\n status: number;\n}\n<% } %>\n\n",
15
15
  "baseClientSetup.ejs": "/**\n * Fetch client setup — GENERATED ONCE, will NOT be overwritten on subsequent runs.\n * Re-generate intentionally with: swaggie ... --clientSetup <path> --forceSetup\n *\n * This file is a write-once scaffold for configuring the fetch-based client\n * exported from the generated API client. It is NOT imported by api.ts — call\n * `setupApiClient()` once at your application startup.\n *\n * Usage in your app:\n * import { setupApiClient } from '<%= it.relativeSetupImport %>';\n * setupApiClient(); // call once before any API requests\n *\n * The setup function can accept any parameters your app needs (e.g. an auth ref):\n * export function setupApiClient(authRef: AuthRef): void { ... }\n */\nimport { defaults } from '<%= it.relativeApiImport %>';\n\n/**\n * Configures the shared fetch client defaults.\n * Override `defaults.fetch` to wrap the native fetch with custom behaviour\n * (auth headers, error handling, logging, etc.).\n */\nexport function setupApiClient(): void {\n const originalFetch = defaults.fetch ?? globalThis.fetch;\n\n defaults.fetch = async (input, init) => {\n // TODO: Add pre-request logic, e.g. attach an Authorization header:\n // const token = await getToken();\n // init = { ...init, headers: { ...init?.headers, Authorization: `Bearer ${token}` } };\n\n const response = await originalFetch(input, init);\n\n // TODO: Add post-response logic, e.g. handle 401 Unauthorized:\n // if (response.status === 401) { loginWithRedirect(); }\n\n return response;\n };\n}\n",
16
16
  "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n",
17
- "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"
17
+ "operation.ejs": "<%\nvar parseMethod = it.responseContentType === 'binary' ? 'blob' : (it.responseContentType === 'text' ? 'text' : 'json');\nvar returnTypeWrapped = it.responseShape === 'full'\n ? 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>'\n : 'Promise<' + it.returnType + '>';\n%>\n<%~ 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 ): <%~ returnTypeWrapped %> {\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.responseShape === 'full') { %>\n .then(async (response) => ({\n data: (await response.<%= parseMethod %>()) as <%~ it.returnType %>,\n headers: response.headers,\n status: response.status,\n }));\n<% } else { %>\n .then((response) => response.<%= parseMethod %>() as Promise<<%~ it.returnType %>>);\n<% } %>\n },\n"
18
18
  },
19
19
  "ky": {
20
20
  "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/** Serializes a params object into a query string. Options override the generated defaults (allowDots, arrayFormat). */\nexport function 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 return _encodeParams(params, parentKey, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n ...options,\n });\n}\n",
21
- "baseClient.ejs": "import ky, { type Options as KyOptions } from 'ky';\n\nexport const http = ky.create({\n prefix: '<%= it.baseUrl || '' %>',\n});\n",
21
+ "baseClient.ejs": "import ky, { type Options as KyOptions } from 'ky';\n\nexport const http = ky.create({\n prefix: '<%= it.baseUrl || '' %>',\n});\n<% if (it.responseShape === 'full') { %>\nexport interface <%= it.apiResponseType %><T> {\n data: T;\n headers: Headers;\n status: number;\n}\n<% } %>\n",
22
22
  "baseClientSetup.ejs": "/**\n * Ky client setup — GENERATED ONCE, will NOT be overwritten on subsequent runs.\n * Re-generate intentionally with: swaggie ... --clientSetup <path> --forceSetup\n *\n * This file configures the ky HTTP client used by the generated API client.\n * Because ky requires hooks to be provided at creation time, this file exports\n * a `createKyConfig()` function that is called by `initKyHttp()` in api.ts.\n *\n * Usage in your app:\n * import { initKyHttp } from '<%= it.relativeApiImport %>';\n * initKyHttp(); // call once at startup, e.g. in a React provider or main.ts\n *\n * If you need to pass runtime values into hooks (e.g. an auth token getter),\n * store them in module-level variables and expose a configuration function:\n *\n * let _getToken: () => Promise<string> = () => Promise.resolve('');\n *\n * export function configureKyClient(opts: { getToken: () => Promise<string> }) {\n * _getToken = opts.getToken;\n * }\n *\n * Then call configureKyClient(...) before initKyHttp() at startup.\n */\nimport type { Options as KyOptions } from 'ky';\n\nexport type KySetupConfig = Pick<KyOptions, 'hooks' | 'prefix' | 'retry' | 'timeout'>;\n\n/**\n * Returns the ky configuration used to create the HTTP client instance.\n * Called once by `initKyHttp()` in the generated api.ts.\n *\n * Add your hooks here — beforeRequest for auth/headers, afterResponse for\n * error handling and monitoring, beforeError for error enrichment.\n */\nexport function createKyConfig(): KySetupConfig {\n return {\n prefix: '<%= it.baseUrl || '' %>',\n hooks: {\n beforeRequest: [\n // TODO: Add request hooks, e.g. attach an Authorization header:\n // async ({ request }) => {\n // const token = await _getToken();\n // if (token) request.headers.set('Authorization', `Bearer ${token}`);\n // },\n ],\n afterResponse: [\n // TODO: Add response hooks, e.g. handle 401 Unauthorized:\n // async ({ response }) => {\n // if (response.status === 401) {\n // await loginWithRedirect({ returnTo: window.location.toString() });\n // }\n // },\n ],\n beforeError: [\n // TODO: Add error hooks, e.g. enrich errors with request context:\n // ({ error }) => error,\n ],\n },\n };\n}\n",
23
- "baseClientWithSetup.ejs": "import ky, { type KyInstance, type Options as KyOptions } from 'ky';\nimport { createKyConfig } from '<%= it.relativeSetupImport %>';\n\nlet _http: KyInstance | null = null;\n\n/**\n * Initialises the ky HTTP client using the configuration returned by\n * `createKyConfig()` from your setup file.\n *\n * Call this once at application startup — for example inside a React provider\n * or your app's entry point — before any API calls are made.\n *\n * Re-calling this function replaces the existing instance. ky instances are\n * immutable, so hook changes require re-initialisation.\n */\nexport function initKyHttp(): void {\n _http = ky.create(createKyConfig());\n}\n\n/**\n * Returns the initialised ky instance. Throws if called before `initKyHttp()`.\n *\n * This guard prevents accidental use of the client before it has been\n * configured with hooks (auth, error handling, etc.).\n */\nexport function getKyHttp(): KyInstance {\n if (!_http) {\n throw new Error(\n '[Swaggie] ky client is not initialised. ' +\n 'Call initKyHttp() at application startup before making any API requests.'\n );\n }\n return _http;\n}\n",
23
+ "baseClientWithSetup.ejs": "import ky, { type KyInstance, type Options as KyOptions } from 'ky';\nimport { createKyConfig } from '<%= it.relativeSetupImport %>';\n\nlet _http: KyInstance | null = null;\n\n/**\n * Initialises the ky HTTP client using the configuration returned by\n * `createKyConfig()` from your setup file.\n *\n * Call this once at application startup — for example inside a React provider\n * or your app's entry point — before any API calls are made.\n *\n * Re-calling this function replaces the existing instance. ky instances are\n * immutable, so hook changes require re-initialisation.\n */\nexport function initKyHttp(): void {\n _http = ky.create(createKyConfig());\n}\n\n/**\n * Returns the initialised ky instance. Throws if called before `initKyHttp()`.\n *\n * This guard prevents accidental use of the client before it has been\n * configured with hooks (auth, error handling, etc.).\n */\nexport function getKyHttp(): KyInstance {\n if (!_http) {\n throw new Error(\n '[Swaggie] ky client is not initialised. ' +\n 'Call initKyHttp() at application startup before making any API requests.'\n );\n }\n return _http;\n}\n<% if (it.responseShape === 'full') { %>\nexport interface <%= it.apiResponseType %><T> {\n data: T;\n headers: Headers;\n status: number;\n}\n<% } %>\n",
24
24
  "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', Object.assign({ httpAccessor: it.httpAccessor }, operation)); %>\n\n<% }); %>\n};\n",
25
- "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?: KyOptions\n ): Promise<<%~ it.returnType %>> {\n const url = `<%= it.url.replace(/^\\//, '') %>`;\n\n return <%= it.httpAccessor %>.<%= it.method.toLowerCase() %>(url, {\n<% if(it.body) { -%>\n<% if(it.body.contentType === 'json') { -%>\n json: <%= 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<% if(it.query && it.query.length > 0) { -%>\n searchParams: encodeParams({\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<% if(it.responseContentType === 'binary') { -%>\n }).blob() as Promise<<%~ it.returnType %>>;\n<% } else if(it.responseContentType === 'text') { -%>\n }).text() as Promise<<%~ it.returnType %>>;\n<% } else { -%>\n }).json<<%~ it.returnType %>>();\n<% } -%>\n },\n"
25
+ "operation.ejs": "<%\nvar parseMethod = it.responseContentType === 'binary' ? 'blob' : (it.responseContentType === 'text' ? 'text' : 'json');\nvar returnTypeWrapped = it.responseShape === 'full'\n ? 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>'\n : 'Promise<' + it.returnType + '>';\n%>\n<%~ 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?: KyOptions\n ): <%~ returnTypeWrapped %> {\n const url = `<%= it.url.replace(/^\\//, '') %>`;\n\n return <%= it.httpAccessor %>.<%= it.method.toLowerCase() %>(url, {\n<% if(it.body) { -%>\n<% if(it.body.contentType === 'json') { -%>\n json: <%= 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<% if(it.query && it.query.length > 0) { -%>\n searchParams: encodeParams({\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<% if (it.responseShape === 'full') { -%>\n }).then(async (response) => ({\n data: (await response.<%= parseMethod %>()) as <%~ it.returnType %>,\n headers: response.headers,\n status: response.status,\n }));\n<% } else if(it.responseContentType === 'binary') { -%>\n }).blob() as Promise<<%~ it.returnType %>>;\n<% } else if(it.responseContentType === 'text') { -%>\n }).text() as Promise<<%~ it.returnType %>>;\n<% } else { -%>\n }).json<<%~ it.returnType %>>();\n<% } -%>\n },\n"
26
26
  },
27
27
  "ng1": {
28
28
  "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",
29
- "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",
29
+ "baseClient.ejs": "import type { IHttpService, IRequestShortcutConfig, IPromise<% if (it.responseShape === 'full') { %>, IHttpHeadersGetter<% } %> } 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<% if (it.responseShape === 'full') { %>\n protected $getFull<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<<%= it.apiResponseType %><T>> {\n return this.$http.get(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));\n }\n\n protected $deleteFull<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<<%= it.apiResponseType %><T>> {\n return this.$http.delete(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));\n }\n\n protected $headFull<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<<%= it.apiResponseType %><T>> {\n return this.$http.head(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));\n }\n\n protected $jsonpFull<T>(\n url: string,\n config?: IRequestShortcutConfig\n ): IPromise<<%= it.apiResponseType %><T>> {\n return this.$http.jsonp(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));\n }\n\n protected $postFull<T>(\n url: string,\n data: any,\n config?: IRequestShortcutConfig\n ): IPromise<<%= it.apiResponseType %><T>> {\n return this.$http.post(this.baseUrl + url, data, config).then((response: any) => this.processFull<T>(response));\n }\n\n protected $putFull<T>(\n url: string,\n data: any,\n config?: IRequestShortcutConfig\n ): IPromise<<%= it.apiResponseType %><T>> {\n return this.$http.put(this.baseUrl + url, data, config).then((response: any) => this.processFull<T>(response));\n }\n\n protected $patchFull<T>(\n url: string,\n data: any,\n config?: IRequestShortcutConfig\n ): IPromise<<%= it.apiResponseType %><T>> {\n return this.$http.patch(this.baseUrl + url, data, config).then((response: any) => this.processFull<T>(response));\n }\n\n protected processFull<T>(response: any): <%= it.apiResponseType %><T> {\n return { data: response.data, headers: response.headers, status: response.status };\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<% if (it.responseShape === 'full') { %>\nexport interface <%= it.apiResponseType %><T> {\n data: T;\n headers: IHttpHeadersGetter;\n status: number;\n}\n<% } %>\n\n",
30
30
  "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",
31
- "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"
31
+ "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 ): <%~ it.responseShape === 'full' ? 'IPromise<' + it.apiResponseType + '<' + it.returnType + '>>' : '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() %><%= it.responseShape === 'full' ? 'Full' : '' %>(\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"
32
32
  },
33
33
  "ng2": {
34
34
  "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 */\nexport function 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",
35
- "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",
35
+ "baseClient.ejs": "import type { Observable } from \"rxjs\";\n<% if (it.responseShape === 'full') { %>import { map } from \"rxjs/operators\";\n<% } %>import { 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<% if (it.responseShape === 'full') { %>\nexport interface <%= it.apiResponseType %><T> {\n data: T;\n headers: HttpHeaders;\n status: number;\n}\n<% } %>\n\n",
36
36
  "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",
37
- "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"
37
+ "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 ): <%~ it.responseShape === 'full' ? 'Observable<' + it.apiResponseType + '<' + it.returnType + '>>' : '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<%\nvar fullMap = '.pipe(\\n map((response) => ({ data: response.body as ' + it.returnType + ', headers: response.headers, status: response.status }))\\n )';\nvar bodyArg = it.body\n ? (it.body.contentType === 'urlencoded' ? 'new URLSearchParams(' + it.body.name + ' as any)' : it.body.name)\n : 'null';\n-%>\n<% if (it.responseShape === 'full') { -%>\n<% if(it.method === 'GET') { -%>\n return this.http.get<<%~ it.returnType %>>(this.baseUrl + url, { ...config, observe: 'response' as const })<%~ fullMap %>;\n<% } else if(it.method === 'DELETE') { -%>\n return this.http.delete<<%~ it.returnType %>>(this.baseUrl + url, { ...config, observe: 'response' as const })<%~ fullMap %>;\n<% } else if(['POST', 'PUT', 'PATCH'].includes(it.method)) { -%>\n return this.http.<%= it.method.toLowerCase() %><%~ '<' + it.returnType + '>' %>(this.baseUrl + url, <%~ bodyArg %>, { ...config, observe: 'response' as const })<%~ fullMap %>;\n<% } -%>\n<% } else { -%>\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 return this.http.<%= it.method.toLowerCase() %><%~ '<' + it.returnType + '>' %>(this.baseUrl + url, <%~ bodyArg %>, config);\n<% } -%>\n<% } -%>\n }\n"
38
38
  },
39
39
  "swr": {
40
- "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",
41
- "client.ejs": "export const <%= it.camelCaseName -%>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n <% }); %>\n};\n\n<% if (!it.splitMode) { %>\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var swrOperation = Object.assign({\n swrOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n }, it.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.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n }, it.safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('swrMutationOperation.ejs', swrMutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.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<% } /* end !splitMode */ %>\n",
42
- "hooksClient.ejs": "<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\nvar httpClientPrefix = 'API.' + it.camelCaseName;\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var safe = it.safeOperation(operation, it.camelCaseName);\n var swrOperation = Object.assign({}, safe, {\n swrOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\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 safe = it.safeOperation(operation, it.camelCaseName);\n var swrMutationOperation = Object.assign({}, safe, {\n mutOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\n%>\n<%~ include('swrMutationOperation.ejs', swrMutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.safeOperation(operation, it.camelCaseName);\n var prefixedParams = safeOp.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n });\n%>\n <%= keyName %>: (<% prefixedParams.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => `<%= operation.url %><% if(safeOp.query && safeOp.query.length > 0) { %>?${API.encodeParams({<% safeOp.query.forEach((parameter) => { %>'<%= parameter.originalName %>': <%= safeOp.queryParamObject ? `${safeOp.queryParamObject.name}?.${parameter.name}` : parameter.name %>, <% }); %>})}<% } %>`,\n<% }); %>\n },\n};\n",
43
- "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' : '';\nvar httpClientName = it.httpClientName || it.clientName;\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 <%= httpClientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig)<%~ it.responseMapper %>,\n $config\n );\n },\n",
44
- "swrOperation.ejs": "<% var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\\n' : ''; %>\n<% var httpClientName = it.httpClientName || it.clientName; %>\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, isValidating, mutate } = useSWR<<%~ it.returnType %>>(\n cacheUrl,\n () => <%= httpClientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig)<%~ it.responseMapper %>,\n config\n );\n\n return { data, isLoading, isValidating, error, mutate };\n },\n"
40
+ "baseClient.ejs": "import useSWR, { type SWRConfiguration, type Key as SWRKey } 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?: SWRKey;\n}\n",
41
+ "client.ejs": "export const <%= it.camelCaseName -%>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n <% }); %>\n};\n\n<% if (!it.splitMode) { %>\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var swrOperation = Object.assign({\n swrOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.apiResponseType,\n }, it.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.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.apiResponseType,\n }, it.safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('swrMutationOperation.ejs', swrMutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.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<% } /* end !splitMode */ %>\n",
42
+ "hooksClient.ejs": "<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\nvar httpClientPrefix = 'API.' + it.camelCaseName;\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var safe = it.safeOperation(operation, it.camelCaseName);\n var swrOperation = Object.assign({}, safe, {\n swrOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.prefixApiType(it.apiResponseType),\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\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 safe = it.safeOperation(operation, it.camelCaseName);\n var swrMutationOperation = Object.assign({}, safe, {\n mutOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.prefixApiType(it.apiResponseType),\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\n%>\n<%~ include('swrMutationOperation.ejs', swrMutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.safeOperation(operation, it.camelCaseName);\n var prefixedParams = safeOp.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n });\n%>\n <%= keyName %>: (<% prefixedParams.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => `<%= operation.url %><% if(safeOp.query && safeOp.query.length > 0) { %>?${API.encodeParams({<% safeOp.query.forEach((parameter) => { %>'<%= parameter.originalName %>': <%= safeOp.queryParamObject ? `${safeOp.queryParamObject.name}?.${parameter.name}` : parameter.name %>, <% }); %>})}<% } %>`,\n<% }); %>\n },\n};\n",
43
+ "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' : '';\nvar httpClientName = it.httpClientName || it.clientName;\nvar dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType;\n%>\n<%~ docs %>\n <%= it.mutOpName %>(\n $config?: SWRMutationConfiguration<<%~ dataType %>, Error, string, <%~ variablesType %>>,\n $httpConfig?: <%= it.httpConfigType %>\n ) {\n return useSWRMutation<<%~ dataType %>, Error, string, <%~ variablesType %>>(\n '<%= swrKey %>',\n (_key: string, { arg }<%~ hasParams ? ': { arg: ' + variablesType + ' }' : '' %>) =>\n <%= httpClientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig)<%~ it.responseMapper %>,\n $config\n );\n },\n",
44
+ "swrOperation.ejs": "<% var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\\n' : ''; %>\n<% var httpClientName = it.httpClientName || it.clientName; %>\n<% var dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType; %>\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?: SWRKey },\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, isValidating, mutate } = useSWR<<%~ dataType %>>(\n cacheUrl,\n () => <%= httpClientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig)<%~ it.responseMapper %>,\n config\n );\n\n return { data, isLoading, isValidating, error, mutate };\n },\n"
45
45
  },
46
46
  "tsq": {
47
47
  "baseClient.ejs": "import { type UseQueryOptions, type UseMutationOptions, useQuery, useMutation } from '@tanstack/react-query';\n",
48
- "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n<% if (!it.splitMode) { %>\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var queryOperation = Object.assign({\n rqOpName: 'use' + opName,\n opKey: it.hooksCamelCaseName + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n }, it.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.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n }, it.safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('mutationOperation.ejs', mutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.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.hooksCamelCaseName %>', '<%= it.hooksCamelCaseName + opName %>'<% safeOp.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,\n<% }); %>\n },\n};\n<% } /* end !splitMode */ %>\n",
49
- "hooksClient.ejs": "<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\nvar httpClientPrefix = 'API.' + it.camelCaseName;\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var safe = it.safeOperation(operation, it.camelCaseName);\n var queryOperation = Object.assign({}, safe, {\n rqOpName: 'use' + opName,\n opKey: it.hooksCamelCaseName + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\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 safe = it.safeOperation(operation, it.camelCaseName);\n var mutationOperation = Object.assign({}, safe, {\n mutOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\n%>\n<%~ include('mutationOperation.ejs', mutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.safeOperation(operation, it.camelCaseName);\n var prefixedParams = safeOp.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n });\n%>\n <%= keyName %>: (<% prefixedParams.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => ['<%= it.hooksCamelCaseName %>', '<%= it.hooksCamelCaseName + opName %>'<% safeOp.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,\n<% }); %>\n },\n};\n",
50
- "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' : '';\nvar httpClientName = it.httpClientName || it.clientName;\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' : '' %>) => <%= httpClientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig)<%~ it.responseMapper %>,\n ...$config\n });\n },\n",
51
- "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' : '';\nvar httpClientName = it.httpClientName || it.clientName;\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: () => <%= httpClientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig)<%~ it.responseMapper %>,\n ...$config\n });\n },\n"
48
+ "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n<% if (!it.splitMode) { %>\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var queryOperation = Object.assign({\n rqOpName: 'use' + opName,\n opKey: it.hooksCamelCaseName + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.apiResponseType,\n }, it.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.hooksCamelCaseName,\n httpClientName: it.camelCaseName,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.apiResponseType,\n }, it.safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('mutationOperation.ejs', mutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.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.hooksCamelCaseName %>', '<%= it.hooksCamelCaseName + opName %>'<% safeOp.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,\n<% }); %>\n },\n};\n<% } /* end !splitMode */ %>\n",
49
+ "hooksClient.ejs": "<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\nvar httpClientPrefix = 'API.' + it.camelCaseName;\n%>\n\nexport const <%= it.hooksCamelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var safe = it.safeOperation(operation, it.camelCaseName);\n var queryOperation = Object.assign({}, safe, {\n rqOpName: 'use' + opName,\n opKey: it.hooksCamelCaseName + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.prefixApiType(it.apiResponseType),\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\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 safe = it.safeOperation(operation, it.camelCaseName);\n var mutationOperation = Object.assign({}, safe, {\n mutOpName: 'use' + opName,\n clientName: it.hooksCamelCaseName,\n httpClientName: httpClientPrefix,\n httpConfigType: it.httpConfigType,\n responseMapper: it.responseMapper,\n responseShape: it.responseShape,\n apiResponseType: it.prefixApiType(it.apiResponseType),\n returnType: it.prefixApiType(safe.returnType),\n parameters: safe.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n }),\n });\n%>\n<%~ include('mutationOperation.ejs', mutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = it.toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = it.safeOperation(operation, it.camelCaseName);\n var prefixedParams = safeOp.parameters.map(function(p) {\n return Object.assign({}, p, { type: it.prefixApiType(p.type) });\n });\n%>\n <%= keyName %>: (<% prefixedParams.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => ['<%= it.hooksCamelCaseName %>', '<%= it.hooksCamelCaseName + opName %>'<% safeOp.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,\n<% }); %>\n },\n};\n",
50
+ "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' : '';\nvar httpClientName = it.httpClientName || it.clientName;\nvar dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType;\n%>\n<%~ docs %>\n <%= it.mutOpName %><TData = <%~ dataType %>, TError = Error>(\n $config?: UseMutationOptions<<%~ dataType %>, TError, <%~ variablesType %>>,\n $httpConfig?: <%= it.httpConfigType %>\n ) {\n return useMutation<<%~ dataType %>, TError, <%~ variablesType %>>({\n mutationFn: (<%= hasParams ? 'vars' : '' %>) => <%= httpClientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig)<%~ it.responseMapper %>,\n ...$config\n });\n },\n",
51
+ "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' : '';\nvar httpClientName = it.httpClientName || it.clientName;\nvar dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType;\n%>\n<%~ docs %>\n <%= it.rqOpName %><TData = <%~ dataType %>, TError = Error>(\n<% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,\n<% }); %> $config?: Omit<UseQueryOptions<<%~ dataType %>, TError, TData>, 'queryKey' | 'queryFn'>,\n $httpConfig?: <%= it.httpConfigType %>\n ) {\n return useQuery<<%~ dataType %>, TError, TData>({\n queryKey: <%= it.clientName %>.queryKeys.<%= it.rqOpName.charAt(3).toLowerCase() + it.rqOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>),\n queryFn: () => <%= httpClientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig)<%~ it.responseMapper %>,\n ...$config\n });\n },\n"
52
52
  },
53
53
  "xior": {
54
54
  "barrel.ejs": "export { encodeParams } from 'xior';\n",
55
- "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",
55
+ "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<% if (it.responseShape === 'full') { %>\nexport interface <%= it.apiResponseType %><T> {\n data: T;\n headers: XiorResponse<T>['headers'];\n status: number;\n}\n<% } %>\n\n",
56
56
  "baseClientSetup.ejs": "/**\n * Xior client setup — GENERATED ONCE, will NOT be overwritten on subsequent runs.\n * Re-generate intentionally with: swaggie ... --clientSetup <path> --forceSetup\n *\n * This file is a write-once scaffold for configuring the xior instance exported\n * from the generated API client. It is NOT imported by api.ts — call\n * `setupApiClient()` once at your application startup.\n *\n * Usage in your app:\n * import { setupApiClient } from '<%= it.relativeSetupImport %>';\n * const cleanup = setupApiClient(); // call once before any API requests\n *\n * The setup function can accept any parameters your app needs (e.g. an auth ref):\n * export function setupApiClient(authRef: AuthRef): () => void { ... }\n */\nimport { http } from '<%= it.relativeApiImport %>';\n\n/**\n * Registers interceptors on the shared xior instance.\n * Returns a cleanup function that ejects the interceptors — useful for\n * React effects or other lifecycle-managed contexts.\n */\nexport function setupApiClient(): () => void {\n const requestInterceptor = http.interceptors.request.use(\n (config) => {\n // TODO: Add request interceptors, e.g. attach an Authorization header:\n // const token = await authRef.current.getAccessToken();\n // if (token) config.headers.Authorization = `Bearer ${token}`;\n return config;\n },\n (error) => Promise.reject(error)\n );\n\n const responseInterceptor = http.interceptors.response.use(\n (response) => response,\n async (error) => {\n // TODO: Add response error handling, e.g. redirect on 401:\n // if (error.response?.status === 401) {\n // await authRef.current.loginWithRedirect({ returnTo: window.location.toString() });\n // return;\n // }\n return Promise.reject(error);\n }\n );\n\n return () => {\n http.interceptors.request.eject(requestInterceptor);\n http.interceptors.response.eject(responseInterceptor);\n };\n}\n",
57
57
  "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n",
58
- "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"
58
+ "operation.ejs": "<%\nvar returnTypeWrapped = 'Promise<XiorResponse<' + it.returnType + '>>';\nvar responseMapper = '';\nif (it.responseShape === 'full') {\n // XiorResponse<T> already exposes { data, headers, status } and is therefore\n // structurally assignable to APIResponse<T> — no remap needed.\n returnTypeWrapped = 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>';\n} else if (it.responseShape === 'body') {\n returnTypeWrapped = 'Promise<' + it.returnType + '>';\n responseMapper = '.then((resp) => resp.data)';\n}\n%>\n<%~ 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 ): <%~ returnTypeWrapped %> {\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 })<%~ responseMapper %>;\n },\n"
59
59
  }
60
60
  } ; exports.BUNDLED_TEMPLATES = BUNDLED_TEMPLATES;
61
61
 
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ var _gen = require('./gen'); var _gen2 = _interopRequireDefault(_gen);
10
10
 
11
11
 
12
12
 
13
+
13
14
  var _utils = require('./utils');
14
15
  var _templateValidator = require('./utils/templateValidator');
15
16
  var _swagger = require('./swagger');
@@ -86,6 +87,7 @@ function verifyOptions(options) {
86
87
  );
87
88
  }
88
89
  }
90
+ verifyExcludeOptions(options.exclude);
89
91
  }
90
92
 
91
93
  /**
@@ -126,6 +128,43 @@ function verifyEntryOptions(opts) {
126
128
  );
127
129
  }
128
130
  }
131
+ verifyExcludeOptions(opts.exclude);
132
+ }
133
+
134
+ /**
135
+ * Returns true when a pattern looks like a regex (starts with `/` or contains
136
+ * regex-specific metacharacters that are not our supported wildcards).
137
+ * Our supported wildcards are `*` and `?` only.
138
+ */
139
+ function isRegexPattern(pattern) {
140
+ if (pattern.startsWith('/')) {
141
+ return true;
142
+ }
143
+ // Characters that are regex metacharacters but are NOT our supported wildcards
144
+ const regexOnlyChars = /[\\^$.|+()[\]{}]/;
145
+ return regexOnlyChars.test(pattern);
146
+ }
147
+
148
+ /**
149
+ * Throws if any pattern in `exclude.tags` or `exclude.operationIds` looks like
150
+ * a regex. Only plain strings and * / ? wildcards are supported.
151
+ */
152
+ function verifyExcludeOptions(exclude) {
153
+ if (!exclude) {
154
+ return;
155
+ }
156
+ const allPatterns = [
157
+ ...(_nullishCoalesce(exclude.tags, () => ( []))).map((p) => ['exclude.tags', p]),
158
+ ...(_nullishCoalesce(exclude.operationIds, () => ( []))).map((p) => ['exclude.operationIds', p]),
159
+ ];
160
+ for (const [field, pattern] of allPatterns) {
161
+ if (isRegexPattern(pattern)) {
162
+ throw new Error(
163
+ `Invalid pattern "${pattern}" in ${field}: regex patterns are not supported. ` +
164
+ 'Use plain strings or wildcard patterns with * and ? only.'
165
+ );
166
+ }
167
+ }
129
168
  }
130
169
 
131
170
  /**
package/dist/types.d.ts CHANGED
@@ -4,6 +4,18 @@ interface QueryParamsSerializationOptions {
4
4
  arrayFormat?: ArrayFormat;
5
5
  queryParamsAsObject?: boolean | number;
6
6
  }
7
+ export interface ExcludeOptions {
8
+ /**
9
+ * Exclude operations whose first tag matches any of these values.
10
+ * Supports * (any sequence of characters) and ? (any single character) wildcards.
11
+ */
12
+ tags?: string[];
13
+ /**
14
+ * Exclude operations whose operationId matches any of these values.
15
+ * Supports * (any sequence of characters) and ? (any single character) wildcards.
16
+ */
17
+ operationIds?: string[];
18
+ }
7
19
  export interface ClientOptions {
8
20
  /**
9
21
  * Path or URL to the Swagger specification file (JSON or YAML).
@@ -106,6 +118,21 @@ export interface ClientOptions {
106
118
  [key: string]: 'optional' | 'required' | 'ignore';
107
119
  };
108
120
  };
121
+ /** Excludes specific operations from code generation by tag or operationId */
122
+ exclude?: ExcludeOptions;
123
+ /**
124
+ * Controls the shape of the value each generated operation returns.
125
+ * - `undefined` (default) — leave each template's current return shape untouched
126
+ * (non-breaking). axios/xior return their full native response, while
127
+ * fetch/ky/ng return the response body only.
128
+ * - `'body'` — standardize every template to return just the response body (`T`).
129
+ * - `'full'` — standardize every template to return a unified `APIResponse<T>`
130
+ * wrapper exposing `data`, `headers` (template-native type) and `statusCode`.
131
+ *
132
+ * Any explicit value (`'body'` or `'full'`) is a deliberate, potentially
133
+ * breaking change relative to the template's default return shape.
134
+ */
135
+ responseShape?: ResponseShape;
109
136
  }
110
137
  export interface CliOptions extends Omit<FullAppOptions, 'enumNamesStyle'> {
111
138
  allowDots?: boolean;
@@ -150,6 +177,12 @@ export type SchemaDeclarationStyle = 'interface' | 'type';
150
177
  export type EnumDeclarationStyle = 'union' | 'enum';
151
178
  export type EnumNamesStyle = 'original' | 'PascalCase';
152
179
  export type TestingFramework = 'vitest' | 'jest';
180
+ /**
181
+ * Controls the shape of the value each generated operation returns.
182
+ * `'body'` returns the response body (`T`); `'full'` returns an `APIResponse<T>`
183
+ * wrapper. When unset, each template keeps its current default return shape.
184
+ */
185
+ export type ResponseShape = 'body' | 'full';
153
186
  /**
154
187
  * Internal options type used throughout the app after `prepareAppOptions` has run.
155
188
  * All fields that have defaults are required here so the rest of the codebase never
@@ -1,5 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
+
4
+
5
+
6
+
7
+
8
+
3
9
  const L1_TEMPLATES = ['axios', 'fetch', 'xior', 'ng1', 'ng2', 'ky'];
4
10
  const L2_TEMPLATES = ['swr', 'tsq'];
5
11
 
@@ -138,11 +144,24 @@ const DEFAULT_L1_FOR_L2 = 'fetch';
138
144
  * The returned string is appended after the client call expression:
139
145
  * `client.method(...args)` + responseMapper
140
146
  *
141
- * Examples:
147
+ * When `responseShape` is set (`'body'` or `'full'`), every L1 operation already
148
+ * resolves to the final shape (`T` or `APIResponse<T>`), so no extra mapping is
149
+ * needed and this returns an empty string regardless of the L1 template.
150
+ *
151
+ * Examples (responseShape unset):
142
152
  * axios/xior → `.then((resp) => resp.data)`
143
153
  * fetch/ky → `` (empty — the promise already resolves to T)
144
154
  */
145
- function getResponseMapper(template) {
155
+ function getResponseMapper(
156
+ template,
157
+ responseShape
158
+ ) {
159
+ // When the response shape is standardized, the L1 operation already returns
160
+ // the final value (body or APIResponse wrapper) — no unwrapping needed.
161
+ if (responseShape) {
162
+ return '';
163
+ }
164
+
146
165
  const l1 = getL1Template(template);
147
166
  switch (l1) {
148
167
  case 'fetch':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swaggie",
3
- "version": "2.2.2",
3
+ "version": "2.4.0",
4
4
  "description": "Generate a fully typed TypeScript API client from your OpenAPI 3 spec",
5
5
  "author": {
6
6
  "name": "Piotr Dabrowski",
@@ -1,4 +1,4 @@
1
- import Axios, { type AxiosPromise, type AxiosRequestConfig } from "axios";
1
+ import Axios, { type AxiosPromise, type AxiosRequestConfig<% if (it.responseShape === 'full') { %>, type AxiosResponse<% } %> } from "axios";
2
2
 
3
3
  export const axios = Axios.create({
4
4
  baseURL: '<%= it.baseUrl || '' %>',
@@ -8,4 +8,11 @@ export const axios = Axios.create({
8
8
  arrayFormat: '<%= it.arrayFormat %>',
9
9
  }),
10
10
  });
11
+ <% if (it.responseShape === 'full') { %>
12
+ export interface <%= it.apiResponseType %><T> {
13
+ data: T;
14
+ headers: AxiosResponse<T>['headers'];
15
+ status: number;
16
+ }
17
+ <% } %>
11
18
 
@@ -1,10 +1,22 @@
1
+ <%
2
+ var returnTypeWrapped = 'AxiosPromise<' + it.returnType + '>';
3
+ var responseMapper = '';
4
+ if (it.responseShape === 'full') {
5
+ // AxiosResponse<T> already exposes { data, headers, status } and is therefore
6
+ // structurally assignable to APIResponse<T> — no remap needed.
7
+ returnTypeWrapped = 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>';
8
+ } else if (it.responseShape === 'body') {
9
+ returnTypeWrapped = 'Promise<' + it.returnType + '>';
10
+ responseMapper = '.then((resp) => resp.data)';
11
+ }
12
+ %>
1
13
  <%~ it.jsDocs %>
2
14
 
3
15
  <%= it.name %>(<% it.parameters.forEach((parameter) => { %>
4
16
  <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,
5
17
  <% }); %>
6
18
  $config?: AxiosRequestConfig
7
- ): AxiosPromise<<%~ it.returnType %>> {
19
+ ): <%~ returnTypeWrapped %> {
8
20
  const url = `<%= it.url %>`;
9
21
 
10
22
  return axios.request<<%~ it.returnType %>>({
@@ -40,5 +52,5 @@ $config?: AxiosRequestConfig
40
52
  },
41
53
  <% } -%>
42
54
  ...$config,
43
- });
55
+ })<%~ responseMapper %>;
44
56
  },
@@ -6,4 +6,11 @@ export const defaults = {
6
6
  arrayFormat: '<%= it.arrayFormat %>',
7
7
  }),
8
8
  };
9
+ <% if (it.responseShape === 'full') { %>
10
+ export interface <%= it.apiResponseType %><T> {
11
+ data: T;
12
+ headers: Headers;
13
+ status: number;
14
+ }
15
+ <% } %>
9
16
 
@@ -1,10 +1,16 @@
1
+ <%
2
+ var parseMethod = it.responseContentType === 'binary' ? 'blob' : (it.responseContentType === 'text' ? 'text' : 'json');
3
+ var returnTypeWrapped = it.responseShape === 'full'
4
+ ? 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>'
5
+ : 'Promise<' + it.returnType + '>';
6
+ %>
1
7
  <%~ it.jsDocs %>
2
8
 
3
9
  <%= it.name %>(<% it.parameters.forEach((parameter) => { %>
4
10
  <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,
5
11
  <% }); %>
6
12
  $config?: RequestInit
7
- ): Promise<<%~ it.returnType %>> {
13
+ ): <%~ returnTypeWrapped %> {
8
14
  const url = `${defaults.baseUrl}<%= it.url %>?<%
9
15
  if(it.query && it.query.length > 0) { %>${defaults.paramsSerializer({<%
10
16
  it.query.forEach((parameter) => { %>
@@ -59,11 +65,13 @@ $config?: RequestInit
59
65
  ...$config,
60
66
  })
61
67
  <% } %>
62
- <% if(it.responseContentType === 'binary') { %>
63
- .then((response) => response.blob() as Promise<<%~ it.returnType %>>);
64
- <% } else if(it.responseContentType === 'text') { %>
65
- .then((response) => response.text() as Promise<<%~ it.returnType %>>);
68
+ <% if (it.responseShape === 'full') { %>
69
+ .then(async (response) => ({
70
+ data: (await response.<%= parseMethod %>()) as <%~ it.returnType %>,
71
+ headers: response.headers,
72
+ status: response.status,
73
+ }));
66
74
  <% } else { %>
67
- .then((response) => response.json() as Promise<<%~ it.returnType %>>);
75
+ .then((response) => response.<%= parseMethod %>() as Promise<<%~ it.returnType %>>);
68
76
  <% } %>
69
77
  },
@@ -3,3 +3,10 @@ import ky, { type Options as KyOptions } from 'ky';
3
3
  export const http = ky.create({
4
4
  prefix: '<%= it.baseUrl || '' %>',
5
5
  });
6
+ <% if (it.responseShape === 'full') { %>
7
+ export interface <%= it.apiResponseType %><T> {
8
+ data: T;
9
+ headers: Headers;
10
+ status: number;
11
+ }
12
+ <% } %>
@@ -32,3 +32,10 @@ export function getKyHttp(): KyInstance {
32
32
  }
33
33
  return _http;
34
34
  }
35
+ <% if (it.responseShape === 'full') { %>
36
+ export interface <%= it.apiResponseType %><T> {
37
+ data: T;
38
+ headers: Headers;
39
+ status: number;
40
+ }
41
+ <% } %>
@@ -1,10 +1,16 @@
1
+ <%
2
+ var parseMethod = it.responseContentType === 'binary' ? 'blob' : (it.responseContentType === 'text' ? 'text' : 'json');
3
+ var returnTypeWrapped = it.responseShape === 'full'
4
+ ? 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>'
5
+ : 'Promise<' + it.returnType + '>';
6
+ %>
1
7
  <%~ it.jsDocs %>
2
8
 
3
9
  <%= it.name %>(<% it.parameters.forEach((parameter) => { %>
4
10
  <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,
5
11
  <% }); %>
6
12
  $config?: KyOptions
7
- ): Promise<<%~ it.returnType %>> {
13
+ ): <%~ returnTypeWrapped %> {
8
14
  const url = `<%= it.url.replace(/^\//, '') %>`;
9
15
 
10
16
  return <%= it.httpAccessor %>.<%= it.method.toLowerCase() %>(url, {
@@ -40,7 +46,13 @@ $config?: KyOptions
40
46
  },
41
47
  <% } -%>
42
48
  ...$config,
43
- <% if(it.responseContentType === 'binary') { -%>
49
+ <% if (it.responseShape === 'full') { -%>
50
+ }).then(async (response) => ({
51
+ data: (await response.<%= parseMethod %>()) as <%~ it.returnType %>,
52
+ headers: response.headers,
53
+ status: response.status,
54
+ }));
55
+ <% } else if(it.responseContentType === 'binary') { -%>
44
56
  }).blob() as Promise<<%~ it.returnType %>>;
45
57
  <% } else if(it.responseContentType === 'text') { -%>
46
58
  }).text() as Promise<<%~ it.returnType %>>;
@@ -1,4 +1,4 @@
1
- import type { IHttpService, IRequestShortcutConfig, IPromise } from 'angular';
1
+ import type { IHttpService, IRequestShortcutConfig, IPromise<% if (it.responseShape === 'full') { %>, IHttpHeadersGetter<% } %> } from 'angular';
2
2
 
3
3
  abstract class BaseService {
4
4
  constructor(protected readonly $http: IHttpService, public baseUrl: string) { }
@@ -88,6 +88,63 @@ abstract class BaseService {
88
88
  });
89
89
  }
90
90
 
91
+ <% if (it.responseShape === 'full') { %>
92
+ protected $getFull<T>(
93
+ url: string,
94
+ config?: IRequestShortcutConfig
95
+ ): IPromise<<%= it.apiResponseType %><T>> {
96
+ return this.$http.get(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));
97
+ }
98
+
99
+ protected $deleteFull<T>(
100
+ url: string,
101
+ config?: IRequestShortcutConfig
102
+ ): IPromise<<%= it.apiResponseType %><T>> {
103
+ return this.$http.delete(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));
104
+ }
105
+
106
+ protected $headFull<T>(
107
+ url: string,
108
+ config?: IRequestShortcutConfig
109
+ ): IPromise<<%= it.apiResponseType %><T>> {
110
+ return this.$http.head(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));
111
+ }
112
+
113
+ protected $jsonpFull<T>(
114
+ url: string,
115
+ config?: IRequestShortcutConfig
116
+ ): IPromise<<%= it.apiResponseType %><T>> {
117
+ return this.$http.jsonp(this.baseUrl + url, config).then((response: any) => this.processFull<T>(response));
118
+ }
119
+
120
+ protected $postFull<T>(
121
+ url: string,
122
+ data: any,
123
+ config?: IRequestShortcutConfig
124
+ ): IPromise<<%= it.apiResponseType %><T>> {
125
+ return this.$http.post(this.baseUrl + url, data, config).then((response: any) => this.processFull<T>(response));
126
+ }
127
+
128
+ protected $putFull<T>(
129
+ url: string,
130
+ data: any,
131
+ config?: IRequestShortcutConfig
132
+ ): IPromise<<%= it.apiResponseType %><T>> {
133
+ return this.$http.put(this.baseUrl + url, data, config).then((response: any) => this.processFull<T>(response));
134
+ }
135
+
136
+ protected $patchFull<T>(
137
+ url: string,
138
+ data: any,
139
+ config?: IRequestShortcutConfig
140
+ ): IPromise<<%= it.apiResponseType %><T>> {
141
+ return this.$http.patch(this.baseUrl + url, data, config).then((response: any) => this.processFull<T>(response));
142
+ }
143
+
144
+ protected processFull<T>(response: any): <%= it.apiResponseType %><T> {
145
+ return { data: response.data, headers: response.headers, status: response.status };
146
+ }
147
+ <% } %>
91
148
  protected processSingle<T>(response: any): T {
92
149
  var data = response.data;
93
150
  var status = response.status;
@@ -110,4 +167,11 @@ abstract class BaseService {
110
167
  }
111
168
  }
112
169
  }
170
+ <% if (it.responseShape === 'full') { %>
171
+ export interface <%= it.apiResponseType %><T> {
172
+ data: T;
173
+ headers: IHttpHeadersGetter;
174
+ status: number;
175
+ }
176
+ <% } %>
113
177
 
@@ -4,7 +4,7 @@
4
4
  <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,
5
5
  <% }); %>
6
6
  config?: IRequestShortcutConfig
7
- ): IPromise<<%~ it.returnType %>> {
7
+ ): <%~ it.responseShape === 'full' ? 'IPromise<' + it.apiResponseType + '<' + it.returnType + '>>' : 'IPromise<' + it.returnType + '>' %> {
8
8
  let url = `<%= it.url %>?`;
9
9
  <% if(it.query && it.query.length > 0) { %>
10
10
  <% it.query.forEach((parameter) => { %>
@@ -26,7 +26,7 @@
26
26
  config = { ...config, headers: { ...config?.headers, '<%= parameter.originalName %>': <%= parameter.name %> } };
27
27
  }
28
28
  <% } %><% }); } %>
29
- return this.$<%= it.method.toLowerCase() %>(
29
+ return this.$<%= it.method.toLowerCase() %><%= it.responseShape === 'full' ? 'Full' : '' %>(
30
30
  url,
31
31
  <% if(['POST', 'PUT', 'PATCH'].includes(it.method)) { %>
32
32
  <% if(it.body) { %>
@@ -1,5 +1,6 @@
1
1
  import type { Observable } from "rxjs";
2
- import { Injectable, InjectionToken, inject } from "@angular/core";
2
+ <% if (it.responseShape === 'full') { %>import { map } from "rxjs/operators";
3
+ <% } %>import { Injectable, InjectionToken, inject } from "@angular/core";
3
4
  import { HttpClient, type HttpContext, type HttpHeaders, type HttpParams } from "@angular/common/http";
4
5
 
5
6
  export const <%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL = new InjectionToken<string>("<%= (it.servicePrefix || 'API').toUpperCase() -%>_BASE_URL");
@@ -19,4 +20,11 @@ function paramsSerializer(params: any) {
19
20
  arrayFormat: '<%= it.arrayFormat %>',
20
21
  });
21
22
  }
23
+ <% if (it.responseShape === 'full') { %>
24
+ export interface <%= it.apiResponseType %><T> {
25
+ data: T;
26
+ headers: HttpHeaders;
27
+ status: number;
28
+ }
29
+ <% } %>
22
30
 
@@ -5,7 +5,7 @@
5
5
  <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,
6
6
  <% }); %>
7
7
  config?: HttpConfig
8
- ): Observable<<%~ it.returnType %>> {
8
+ ): <%~ it.responseShape === 'full' ? 'Observable<' + it.apiResponseType + '<' + it.returnType + '>>' : 'Observable<' + it.returnType + '>' %> {
9
9
  <% if(it.query && it.query.length > 0) { -%>
10
10
  const params = paramsSerializer({
11
11
  <% it.query.forEach((parameter) => { -%>
@@ -29,15 +29,27 @@ config?: HttpConfig
29
29
  }
30
30
  <% } -%>
31
31
  <% }); } -%>
32
+ <%
33
+ var fullMap = '.pipe(\n map((response) => ({ data: response.body as ' + it.returnType + ', headers: response.headers, status: response.status }))\n )';
34
+ var bodyArg = it.body
35
+ ? (it.body.contentType === 'urlencoded' ? 'new URLSearchParams(' + it.body.name + ' as any)' : it.body.name)
36
+ : 'null';
37
+ -%>
38
+ <% if (it.responseShape === 'full') { -%>
39
+ <% if(it.method === 'GET') { -%>
40
+ return this.http.get<<%~ it.returnType %>>(this.baseUrl + url, { ...config, observe: 'response' as const })<%~ fullMap %>;
41
+ <% } else if(it.method === 'DELETE') { -%>
42
+ return this.http.delete<<%~ it.returnType %>>(this.baseUrl + url, { ...config, observe: 'response' as const })<%~ fullMap %>;
43
+ <% } else if(['POST', 'PUT', 'PATCH'].includes(it.method)) { -%>
44
+ return this.http.<%= it.method.toLowerCase() %><%~ '<' + it.returnType + '>' %>(this.baseUrl + url, <%~ bodyArg %>, { ...config, observe: 'response' as const })<%~ fullMap %>;
45
+ <% } -%>
46
+ <% } else { -%>
32
47
  <% if(it.method === 'GET') { -%>
33
48
  return this.http.get<<%~ it.returnType %>>(this.baseUrl + url, config);
34
49
  <% } else if(it.method === 'DELETE') { -%>
35
50
  return this.http.delete<<%~ it.returnType %>>(this.baseUrl + url, config);
36
- <% } else if(['POST', 'PUT', 'PATCH'].includes(it.method)) {
37
- const bodyArg = it.body
38
- ? (it.body.contentType === 'urlencoded' ? 'new URLSearchParams(' + it.body.name + ' as any)' : it.body.name)
39
- : 'null';
40
- -%>
51
+ <% } else if(['POST', 'PUT', 'PATCH'].includes(it.method)) { -%>
41
52
  return this.http.<%= it.method.toLowerCase() %><%~ '<' + it.returnType + '>' %>(this.baseUrl + url, <%~ bodyArg %>, config);
42
- <% } %>
53
+ <% } -%>
54
+ <% } -%>
43
55
  }
@@ -1,7 +1,7 @@
1
- import useSWR, { type SWRConfiguration, type Key } from 'swr';
1
+ import useSWR, { type SWRConfiguration, type Key as SWRKey } from 'swr';
2
2
  import useSWRMutation, { type SWRMutationConfiguration } from 'swr/mutation';
3
3
 
4
4
  interface SwrConfig extends SWRConfiguration {
5
5
  /* 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. */
6
- key?: Key;
6
+ key?: SWRKey;
7
7
  }
@@ -21,6 +21,8 @@ export const <%= it.hooksCamelCaseName %> = {
21
21
  httpClientName: it.camelCaseName,
22
22
  httpConfigType: it.httpConfigType,
23
23
  responseMapper: it.responseMapper,
24
+ responseShape: it.responseShape,
25
+ apiResponseType: it.apiResponseType,
24
26
  }, it.safeOperation(operation, it.camelCaseName));
25
27
  %>
26
28
  <%~ include('swrOperation.ejs', swrOperation); %>
@@ -37,6 +39,8 @@ export const <%= it.hooksCamelCaseName %> = {
37
39
  httpClientName: it.camelCaseName,
38
40
  httpConfigType: it.httpConfigType,
39
41
  responseMapper: it.responseMapper,
42
+ responseShape: it.responseShape,
43
+ apiResponseType: it.apiResponseType,
40
44
  }, it.safeOperation(operation, it.camelCaseName));
41
45
  %>
42
46
  <%~ include('swrMutationOperation.ejs', swrMutationOperation); %>
@@ -15,6 +15,8 @@ export const <%= it.hooksCamelCaseName %> = {
15
15
  httpClientName: httpClientPrefix,
16
16
  httpConfigType: it.httpConfigType,
17
17
  responseMapper: it.responseMapper,
18
+ responseShape: it.responseShape,
19
+ apiResponseType: it.prefixApiType(it.apiResponseType),
18
20
  returnType: it.prefixApiType(safe.returnType),
19
21
  parameters: safe.parameters.map(function(p) {
20
22
  return Object.assign({}, p, { type: it.prefixApiType(p.type) });
@@ -36,6 +38,8 @@ export const <%= it.hooksCamelCaseName %> = {
36
38
  httpClientName: httpClientPrefix,
37
39
  httpConfigType: it.httpConfigType,
38
40
  responseMapper: it.responseMapper,
41
+ responseShape: it.responseShape,
42
+ apiResponseType: it.prefixApiType(it.apiResponseType),
39
43
  returnType: it.prefixApiType(safe.returnType),
40
44
  parameters: safe.parameters.map(function(p) {
41
45
  return Object.assign({}, p, { type: it.prefixApiType(p.type) });
@@ -18,13 +18,14 @@ var swrKey = it.url.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, '*');
18
18
 
19
19
  var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\n' : '';
20
20
  var httpClientName = it.httpClientName || it.clientName;
21
+ var dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType;
21
22
  %>
22
23
  <%~ docs %>
23
24
  <%= it.mutOpName %>(
24
- $config?: SWRMutationConfiguration<<%~ it.returnType %>, Error, string, <%~ variablesType %>>,
25
+ $config?: SWRMutationConfiguration<<%~ dataType %>, Error, string, <%~ variablesType %>>,
25
26
  $httpConfig?: <%= it.httpConfigType %>
26
27
  ) {
27
- return useSWRMutation<<%~ it.returnType %>, Error, string, <%~ variablesType %>>(
28
+ return useSWRMutation<<%~ dataType %>, Error, string, <%~ variablesType %>>(
28
29
  '<%= swrKey %>',
29
30
  (_key: string, { arg }<%~ hasParams ? ': { arg: ' + variablesType + ' }' : '' %>) =>
30
31
  <%= httpClientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig)<%~ it.responseMapper %>,
@@ -1,15 +1,16 @@
1
1
  <% var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\n' : ''; %>
2
2
  <% var httpClientName = it.httpClientName || it.clientName; %>
3
+ <% var dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType; %>
3
4
  <%~ docs %>
4
5
  <%= it.swrOpName %>(
5
6
  <% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,
6
- <% }); %> $config?: Omit<SwrConfig, 'key'> & { key?: Key },
7
+ <% }); %> $config?: Omit<SwrConfig, 'key'> & { key?: SWRKey },
7
8
  $httpConfig?: <%= it.httpConfigType %>
8
9
  ) {
9
10
  const { key, ...config } = $config || {};
10
11
  const cacheUrl = key ?? <%= it.clientName %>.queryKeys.<%= it.swrOpName.charAt(3).toLowerCase() + it.swrOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>);
11
12
 
12
- const { data, error, isLoading, isValidating, mutate } = useSWR<<%~ it.returnType %>>(
13
+ const { data, error, isLoading, isValidating, mutate } = useSWR<<%~ dataType %>>(
13
14
  cacheUrl,
14
15
  () => <%= httpClientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig)<%~ it.responseMapper %>,
15
16
  config
@@ -22,6 +22,8 @@ export const <%= it.hooksCamelCaseName %> = {
22
22
  httpClientName: it.camelCaseName,
23
23
  httpConfigType: it.httpConfigType,
24
24
  responseMapper: it.responseMapper,
25
+ responseShape: it.responseShape,
26
+ apiResponseType: it.apiResponseType,
25
27
  }, it.safeOperation(operation, it.camelCaseName));
26
28
  %>
27
29
  <%~ include('queryOperation.ejs', queryOperation); %>
@@ -38,6 +40,8 @@ export const <%= it.hooksCamelCaseName %> = {
38
40
  httpClientName: it.camelCaseName,
39
41
  httpConfigType: it.httpConfigType,
40
42
  responseMapper: it.responseMapper,
43
+ responseShape: it.responseShape,
44
+ apiResponseType: it.apiResponseType,
41
45
  }, it.safeOperation(operation, it.camelCaseName));
42
46
  %>
43
47
  <%~ include('mutationOperation.ejs', mutationOperation); %>
@@ -16,6 +16,8 @@ export const <%= it.hooksCamelCaseName %> = {
16
16
  httpClientName: httpClientPrefix,
17
17
  httpConfigType: it.httpConfigType,
18
18
  responseMapper: it.responseMapper,
19
+ responseShape: it.responseShape,
20
+ apiResponseType: it.prefixApiType(it.apiResponseType),
19
21
  returnType: it.prefixApiType(safe.returnType),
20
22
  parameters: safe.parameters.map(function(p) {
21
23
  return Object.assign({}, p, { type: it.prefixApiType(p.type) });
@@ -37,6 +39,8 @@ export const <%= it.hooksCamelCaseName %> = {
37
39
  httpClientName: httpClientPrefix,
38
40
  httpConfigType: it.httpConfigType,
39
41
  responseMapper: it.responseMapper,
42
+ responseShape: it.responseShape,
43
+ apiResponseType: it.prefixApiType(it.apiResponseType),
40
44
  returnType: it.prefixApiType(safe.returnType),
41
45
  parameters: safe.parameters.map(function(p) {
42
46
  return Object.assign({}, p, { type: it.prefixApiType(p.type) });
@@ -19,13 +19,14 @@ var rawDocs = it.jsDocs
19
19
  : '';
20
20
  var docs = rawDocs ? rawDocs.replace(/^/gm, ' ') + '\n' : '';
21
21
  var httpClientName = it.httpClientName || it.clientName;
22
+ var dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType;
22
23
  %>
23
24
  <%~ docs %>
24
- <%= it.mutOpName %><TData = <%~ it.returnType %>, TError = Error>(
25
- $config?: UseMutationOptions<<%~ it.returnType %>, TError, <%~ variablesType %>>,
25
+ <%= it.mutOpName %><TData = <%~ dataType %>, TError = Error>(
26
+ $config?: UseMutationOptions<<%~ dataType %>, TError, <%~ variablesType %>>,
26
27
  $httpConfig?: <%= it.httpConfigType %>
27
28
  ) {
28
- return useMutation<<%~ it.returnType %>, TError, <%~ variablesType %>>({
29
+ return useMutation<<%~ dataType %>, TError, <%~ variablesType %>>({
29
30
  mutationFn: (<%= hasParams ? 'vars' : '' %>) => <%= httpClientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig)<%~ it.responseMapper %>,
30
31
  ...$config
31
32
  });
@@ -5,14 +5,15 @@ var rawDocs = it.jsDocs
5
5
  : '';
6
6
  var docs = rawDocs ? rawDocs.replace(/^/gm, ' ') + '\n' : '';
7
7
  var httpClientName = it.httpClientName || it.clientName;
8
+ var dataType = it.responseShape === 'full' ? it.apiResponseType + '<' + it.returnType + '>' : it.returnType;
8
9
  %>
9
10
  <%~ docs %>
10
- <%= it.rqOpName %><TData = <%~ it.returnType %>, TError = Error>(
11
+ <%= it.rqOpName %><TData = <%~ dataType %>, TError = Error>(
11
12
  <% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,
12
- <% }); %> $config?: Omit<UseQueryOptions<<%~ it.returnType %>, TError, TData>, 'queryKey' | 'queryFn'>,
13
+ <% }); %> $config?: Omit<UseQueryOptions<<%~ dataType %>, TError, TData>, 'queryKey' | 'queryFn'>,
13
14
  $httpConfig?: <%= it.httpConfigType %>
14
15
  ) {
15
- return useQuery<<%~ it.returnType %>, TError, TData>({
16
+ return useQuery<<%~ dataType %>, TError, TData>({
16
17
  queryKey: <%= it.clientName %>.queryKeys.<%= it.rqOpName.charAt(3).toLowerCase() + it.rqOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>),
17
18
  queryFn: () => <%= httpClientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig)<%~ it.responseMapper %>,
18
19
  ...$config
@@ -8,4 +8,11 @@ export const http = xior.create({
8
8
  arrayFormat: '<%= it.arrayFormat %>',
9
9
  }),
10
10
  });
11
+ <% if (it.responseShape === 'full') { %>
12
+ export interface <%= it.apiResponseType %><T> {
13
+ data: T;
14
+ headers: XiorResponse<T>['headers'];
15
+ status: number;
16
+ }
17
+ <% } %>
11
18
 
@@ -1,10 +1,22 @@
1
+ <%
2
+ var returnTypeWrapped = 'Promise<XiorResponse<' + it.returnType + '>>';
3
+ var responseMapper = '';
4
+ if (it.responseShape === 'full') {
5
+ // XiorResponse<T> already exposes { data, headers, status } and is therefore
6
+ // structurally assignable to APIResponse<T> — no remap needed.
7
+ returnTypeWrapped = 'Promise<' + it.apiResponseType + '<' + it.returnType + '>>';
8
+ } else if (it.responseShape === 'body') {
9
+ returnTypeWrapped = 'Promise<' + it.returnType + '>';
10
+ responseMapper = '.then((resp) => resp.data)';
11
+ }
12
+ %>
1
13
  <%~ it.jsDocs %>
2
14
 
3
15
  <%= it.name %>(<% it.parameters.forEach((parameter) => { %>
4
16
  <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,
5
17
  <% }); %>
6
18
  $config?: XiorRequestConfig
7
- ): Promise<XiorResponse<<%~ it.returnType %>>> {
19
+ ): <%~ returnTypeWrapped %> {
8
20
  const url = `<%= it.url %>`;
9
21
 
10
22
  return http.request<<%~ it.returnType %>>({
@@ -40,5 +52,5 @@ $config?: XiorRequestConfig
40
52
  },
41
53
  <% } -%>
42
54
  ...$config,
43
- });
55
+ })<%~ responseMapper %>;
44
56
  },