swaggie 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,8 @@ var _templateEngine = require('../utils/templateEngine');
16
16
 
17
17
  const viewData = {
18
18
  servicePrefix: clientOptions.servicePrefix,
19
+ allowDots: clientOptions.queryParamsSerialization.allowDots,
20
+ arrayFormat: clientOptions.queryParamsSerialization.arrayFormat,
19
21
  clients: files
20
22
  .filter((c) => c)
21
23
  .map((c) => ({
@@ -17,12 +17,12 @@
17
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"
18
18
  },
19
19
  "ky": {
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 */\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",
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
21
  "baseClient.ejs": "import ky, { type Options as KyOptions } from 'ky';\n\nexport const http = ky.create({\n prefix: '<%= it.baseUrl || '' %>',\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
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",
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: {\n<% it.query.forEach((parameter) => { -%>\n<% if (it.queryParamObject) { -%>\n '<%= parameter.originalName %>': <%= it.queryParamObject.name %>?.<%= parameter.name %> as any,\n<% } else { -%>\n '<%= parameter.originalName %>': <%= parameter.name %> as any,\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": "<%~ 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"
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swaggie",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Generate a fully typed TypeScript API client from your OpenAPI 3 spec",
5
5
  "author": {
6
6
  "name": "Piotr Dabrowski",
@@ -4,7 +4,7 @@
4
4
  * Implementation from: https://github.com/suhaotian/xior/blob/main/src/utils.ts
5
5
  * Kudos to @suhaotian for the original implementation
6
6
  */
7
- export function encodeParams<T = any>(
7
+ function _encodeParams<T = any>(
8
8
  params: T,
9
9
  parentKey: string | null = null,
10
10
  options?: {
@@ -43,7 +43,7 @@ export function encodeParams<T = any>(
43
43
  }
44
44
  if (typeof value === 'object') {
45
45
  // If the value is an object or array, recursively encode its contents
46
- const result = encodeParams(value, encodedKey, options);
46
+ const result = _encodeParams(value, encodedKey, options);
47
47
  if (result !== '') encodedParams.push(result);
48
48
  } else {
49
49
  // Otherwise, encode the key-value pair
@@ -55,3 +55,20 @@ export function encodeParams<T = any>(
55
55
 
56
56
  return encodedParams.join('&');
57
57
  }
58
+
59
+ /** Serializes a params object into a query string. Options override the generated defaults (allowDots, arrayFormat). */
60
+ export function encodeParams<T = any>(
61
+ params: T,
62
+ parentKey: string | null = null,
63
+ options?: {
64
+ allowDots?: boolean;
65
+ serializeDate?: (value: Date) => string;
66
+ arrayFormat?: 'indices' | 'repeat' | 'brackets';
67
+ }
68
+ ): string {
69
+ return _encodeParams(params, parentKey, {
70
+ allowDots: <%= it.allowDots %>,
71
+ arrayFormat: '<%= it.arrayFormat %>',
72
+ ...options,
73
+ });
74
+ }
@@ -18,15 +18,15 @@ $config?: KyOptions
18
18
  <% } -%>
19
19
  <% } -%>
20
20
  <% if(it.query && it.query.length > 0) { -%>
21
- searchParams: {
21
+ searchParams: encodeParams({
22
22
  <% it.query.forEach((parameter) => { -%>
23
23
  <% if (it.queryParamObject) { -%>
24
- '<%= parameter.originalName %>': <%= it.queryParamObject.name %>?.<%= parameter.name %> as any,
24
+ '<%= parameter.originalName %>': <%= it.queryParamObject.name %>?.<%= parameter.name %>,
25
25
  <% } else { -%>
26
- '<%= parameter.originalName %>': <%= parameter.name %> as any,
26
+ '<%= parameter.originalName %>': <%= parameter.name %>,
27
27
  <% } -%>
28
28
  <% }); -%>
29
- },
29
+ }),
30
30
  <% } -%>
31
31
  <% if(it.headers && it.headers.length > 0) { -%>
32
32
  headers: {