swagger-typescript-api 13.9.3 → 13.11.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swagger-typescript-api",
3
- "version": "13.9.3",
3
+ "version": "13.11.0",
4
4
  "description": "Generate the API client for Fetch or Axios from an OpenAPI Specification",
5
5
  "homepage": "https://github.com/acacode/swagger-typescript-api",
6
6
  "bugs": "https://github.com/acacode/swagger-typescript-api/issues",
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "@apidevtools/swagger-parser": "12.1.0",
48
48
  "@biomejs/js-api": "4.0.0",
49
- "@biomejs/wasm-nodejs": "2.4.15",
49
+ "@biomejs/wasm-nodejs": "2.4.16",
50
50
  "@types/swagger-schema-official": "^2.0.25",
51
51
  "c12": "^3.3.3",
52
52
  "citty": "^0.2.1",
@@ -63,17 +63,17 @@
63
63
  "yummies": "7.19.4"
64
64
  },
65
65
  "devDependencies": {
66
- "@biomejs/biome": "2.4.15",
66
+ "@biomejs/biome": "2.4.16",
67
67
  "@changesets/changelog-github": "0.7.0",
68
68
  "@changesets/cli": "2.31.0",
69
69
  "@tsconfig/node20": "20.1.9",
70
70
  "@tsconfig/strictest": "2.0.8",
71
- "@types/node": "25.9.0",
71
+ "@types/node": "25.9.1",
72
72
  "@types/swagger2openapi": "7.0.4",
73
73
  "axios": "1.16.1",
74
74
  "tsdown": "0.21.10",
75
75
  "typedoc": "0.28.19",
76
- "vitest": "4.1.6"
76
+ "vitest": "4.1.7"
77
77
  },
78
78
  "engines": {
79
79
  "node": ">=20"
@@ -0,0 +1,18 @@
1
+ <%
2
+ const { config } = it;
3
+ const isUnion = config.enumStyle === "union";
4
+
5
+ // camelCase keys (Json, FormData, …) are used by http-client templates as CT.Json, CT.FormData, …
6
+ // They mirror the ContentType property names so the access pattern is identical regardless of enumStyle.
7
+ const v = {
8
+ Json: isUnion ? '"application/json"' : "ContentType.Json",
9
+ JsonApi: isUnion ? '"application/vnd.api+json"' : "ContentType.JsonApi",
10
+ FormData: isUnion ? '"multipart/form-data"' : "ContentType.FormData",
11
+ UrlEncoded: isUnion ? '"application/x-www-form-urlencoded"' : "ContentType.UrlEncoded",
12
+ Text: isUnion ? '"text/plain"' : "ContentType.Text",
13
+ };
14
+
15
+ // UPPER_SNAKE aliases (JSON, FORM_DATA, …) are used by procedure-call templates as
16
+ // requestContentKind["JSON"], matching the CONTENT_KIND constants produced by schema-routes.ts.
17
+ return { ...v, JSON: v.Json, JSON_API: v.JsonApi, FORM_DATA: v.FormData, URL_ENCODED: v.UrlEncoded, TEXT: v.Text };
18
+ %>
@@ -25,6 +25,16 @@ const dataContractTemplates = {
25
25
  type: (contract) => {
26
26
  return `type ${contract.name}${buildGenerics(contract)} = ${contract.content}`;
27
27
  },
28
+ const: (contract) => {
29
+ const entries = contract.$content
30
+ .map(({ key, value }) => ` ${key}: ${value}`)
31
+ .join(',\n');
32
+ const typeExport = contract.internal ? '' : 'export ';
33
+ return (
34
+ `const ${contract.name} = {\n${entries},\n} as const\n` +
35
+ `${typeExport}type ${contract.name} = (typeof ${contract.name})[keyof typeof ${contract.name}]`
36
+ );
37
+ },
28
38
  }
29
39
  %>
30
40
 
@@ -3,7 +3,12 @@ const { contract, utils, config } = it;
3
3
  const { formatDescription, require, _ } = utils;
4
4
  const { name, $content } = contract;
5
5
  %>
6
- <% if (config.generateUnionEnums) { %>
6
+ <% if (config.enumStyle === "const") { %>
7
+ export const <%~ name %> = {
8
+ <%~ _.map($content, ({ key, value }) => `${key}: ${value}`).join(",\n ") %>
9
+ } as const;
10
+ export type <%~ name %> = (typeof <%~ name %>)[keyof typeof <%~ name %>];
11
+ <% } else if (config.enumStyle === "union") { %>
7
12
  export type <%~ name %> = <%~ _.map($content, ({ value }) => value).join(" | ") %>
8
13
  <% } else { %>
9
14
  export enum <%~ name %> {
@@ -1,5 +1,6 @@
1
1
  <%
2
2
  const { apiConfig, generateResponses, config } = it;
3
+ const CT = includeFile("@base/content-type-accessors", { config });
3
4
  %>
4
5
 
5
6
  import type { AxiosInstance, AxiosRequestConfig, HeadersDefaults, ResponseType, AxiosResponse } from "axios";
@@ -30,6 +31,18 @@ export interface ApiConfig<SecurityDataType = unknown> extends Omit<AxiosRequest
30
31
  format?: ResponseType;
31
32
  }
32
33
 
34
+ <% if (config.enumStyle === "const") { %>
35
+ export const ContentType = {
36
+ Json: "application/json",
37
+ JsonApi: "application/vnd.api+json",
38
+ FormData: "multipart/form-data",
39
+ UrlEncoded: "application/x-www-form-urlencoded",
40
+ Text: "text/plain",
41
+ } as const;
42
+ export type ContentType = (typeof ContentType)[keyof typeof ContentType];
43
+ <% } else if (config.enumStyle === "union") { %>
44
+ export type ContentType = "application/json" | "application/vnd.api+json" | "multipart/form-data" | "application/x-www-form-urlencoded" | "text/plain";
45
+ <% } else { %>
33
46
  export enum ContentType {
34
47
  Json = "application/json",
35
48
  JsonApi = "application/vnd.api+json",
@@ -37,6 +50,7 @@ export enum ContentType {
37
50
  UrlEncoded = "application/x-www-form-urlencoded",
38
51
  Text = "text/plain",
39
52
  }
53
+ <% } %>
40
54
 
41
55
  export class HttpClient<SecurityDataType = unknown> {
42
56
  public instance: AxiosInstance;
@@ -116,11 +130,11 @@ export class HttpClient<SecurityDataType = unknown> {
116
130
  const requestParams = this.mergeRequestParams(params, secureParams);
117
131
  const responseFormat = (format || this.format) || undefined;
118
132
 
119
- if (type === ContentType.FormData && body && body !== null && typeof body === "object") {
133
+ if (type === <%~ CT.FormData %> && body && body !== null && typeof body === "object") {
120
134
  body = this.createFormData(body as Record<string, unknown>);
121
135
  }
122
136
 
123
- if (type === ContentType.Text && body && body !== null && typeof body !== "string") {
137
+ if (type === <%~ CT.Text %> && body && body !== null && typeof body !== "string") {
124
138
  body = JSON.stringify(body);
125
139
  }
126
140
 
@@ -1,5 +1,6 @@
1
1
  <%
2
2
  const { apiConfig, generateResponses, config } = it;
3
+ const CT = includeFile("@base/content-type-accessors", { config });
3
4
  %>
4
5
 
5
6
  export type QueryParamsType = Record<string | number, any>;
@@ -41,6 +42,18 @@ export interface HttpResponse<D extends unknown, E extends unknown = unknown> ex
41
42
 
42
43
  type CancelToken = Symbol | string | number;
43
44
 
45
+ <% if (config.enumStyle === "const") { %>
46
+ export const ContentType = {
47
+ Json: "application/json",
48
+ JsonApi: "application/vnd.api+json",
49
+ FormData: "multipart/form-data",
50
+ UrlEncoded: "application/x-www-form-urlencoded",
51
+ Text: "text/plain",
52
+ } as const;
53
+ export type ContentType = (typeof ContentType)[keyof typeof ContentType];
54
+ <% } else if (config.enumStyle === "union") { %>
55
+ export type ContentType = "application/json" | "application/vnd.api+json" | "multipart/form-data" | "application/x-www-form-urlencoded" | "text/plain";
56
+ <% } else { %>
44
57
  export enum ContentType {
45
58
  Json = "application/json",
46
59
  JsonApi = "application/vnd.api+json",
@@ -48,6 +61,7 @@ export enum ContentType {
48
61
  UrlEncoded = "application/x-www-form-urlencoded",
49
62
  Text = "text/plain",
50
63
  }
64
+ <% } %>
51
65
 
52
66
  export class HttpClient<SecurityDataType = unknown> {
53
67
  public baseUrl: string = "<%~ apiConfig.baseUrl %>";
@@ -103,10 +117,10 @@ export class HttpClient<SecurityDataType = unknown> {
103
117
  }
104
118
 
105
119
  private contentFormatters: Record<ContentType, (input: any) => any> = {
106
- [ContentType.Json]: (input:any) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
107
- [ContentType.JsonApi]: (input:any) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
108
- [ContentType.Text]: (input:any) => input !== null && typeof input !== "string" ? JSON.stringify(input) : input,
109
- [ContentType.FormData]: (input: any) => {
120
+ [<%~ CT.Json %>]: (input:any) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
121
+ [<%~ CT.JsonApi %>]: (input:any) => input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
122
+ [<%~ CT.Text %>]: (input:any) => input !== null && typeof input !== "string" ? JSON.stringify(input) : input,
123
+ [<%~ CT.FormData %>]: (input: any) => {
110
124
  if (input instanceof FormData) {
111
125
  return input;
112
126
  }
@@ -124,7 +138,7 @@ export class HttpClient<SecurityDataType = unknown> {
124
138
  return formData;
125
139
  }, new FormData());
126
140
  },
127
- [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
141
+ [<%~ CT.UrlEncoded %>]: (input: any) => this.toQueryString(input),
128
142
  }
129
143
 
130
144
  protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
@@ -181,7 +195,7 @@ export class HttpClient<SecurityDataType = unknown> {
181
195
  const secureParams = ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && this.securityWorker && await this.securityWorker(this.securityData)) || {};
182
196
  const requestParams = this.mergeRequestParams(params, secureParams);
183
197
  const queryString = query && this.toQueryString(query);
184
- const payloadFormatter = this.contentFormatters[type || ContentType.Json];
198
+ const payloadFormatter = this.contentFormatters[type || <%~ CT.Json %>];
185
199
  const responseFormat = format || requestParams.format;
186
200
 
187
201
  return this.customFetch(
@@ -190,7 +204,7 @@ export class HttpClient<SecurityDataType = unknown> {
190
204
  ...requestParams,
191
205
  headers: {
192
206
  ...(requestParams.headers || {}),
193
- ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
207
+ ...(type && type !== <%~ CT.FormData %> ? { "Content-Type": type } : {}),
194
208
  },
195
209
  signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
196
210
  body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
@@ -31,8 +31,9 @@ const rawWrapperArgs = config.extractRequestParams ?
31
31
  _.compact([
32
32
  requestParams && {
33
33
  name: extractedRequestParamsName,
34
- optional: false,
34
+ optional: route.request.requestParamsOptional,
35
35
  type: getInlineParseContent(requestParams),
36
+ defaultValue: route.request.requestParamsOptional ? '{}' : undefined,
36
37
  },
37
38
  ...(!requestParams ? pathParams : []),
38
39
  payload,
@@ -52,13 +53,7 @@ const wrapperArgs = _
52
53
  .join(', ')
53
54
 
54
55
  // RequestParams["type"]
55
- const requestContentKind = {
56
- "JSON": "ContentType.Json",
57
- "JSON_API": "ContentType.JsonApi",
58
- "URL_ENCODED": "ContentType.UrlEncoded",
59
- "FORM_DATA": "ContentType.FormData",
60
- "TEXT": "ContentType.Text",
61
- }
56
+ const requestContentKind = includeFile("@base/content-type-accessors", { config });
62
57
  // RequestParams["format"]
63
58
  const responseContentKind = {
64
59
  "JSON": '"json"',
@@ -8,7 +8,11 @@ const dataContracts = _.map(modelTypes, "name");
8
8
 
9
9
  <% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import type { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %>
10
10
 
11
+ <% if (config.enumStyle === "union") { %>
12
+ import { HttpClient, RequestParams, type ContentType, HttpResponse } from "./<%~ config.fileNames.httpClient %>";
13
+ <% } else { %>
11
14
  import { HttpClient, RequestParams, ContentType, HttpResponse } from "./<%~ config.fileNames.httpClient %>";
15
+ <% } %>
12
16
  <% if (dataContracts.length) { %>
13
17
  import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
14
18
  <% } %>
@@ -31,8 +31,9 @@ const rawWrapperArgs = config.extractRequestParams ?
31
31
  _.compact([
32
32
  requestParams && {
33
33
  name: extractedRequestParamsName,
34
- optional: false,
34
+ optional: route.request.requestParamsOptional,
35
35
  type: getInlineParseContent(requestParams),
36
+ defaultValue: route.request.requestParamsOptional ? '{}' : undefined,
36
37
  },
37
38
  ...(!requestParams ? pathParams : []),
38
39
  payload,
@@ -52,13 +53,7 @@ const wrapperArgs = _
52
53
  .join(', ')
53
54
 
54
55
  // RequestParams["type"]
55
- const requestContentKind = {
56
- "JSON": "ContentType.Json",
57
- "JSON_API": "ContentType.JsonApi",
58
- "URL_ENCODED": "ContentType.UrlEncoded",
59
- "FORM_DATA": "ContentType.FormData",
60
- "TEXT": "ContentType.Text",
61
- }
56
+ const requestContentKind = includeFile("@base/content-type-accessors", { config });
62
57
  // RequestParams["format"]
63
58
  const responseContentKind = {
64
59
  "JSON": '"json"',