typedapi-client-helpers 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +221 -220
  3. package/dist/cjs/httpClient.js +258 -0
  4. package/dist/cjs/httpClient.js.map +1 -0
  5. package/dist/cjs/index.js +32 -0
  6. package/dist/cjs/index.js.map +1 -0
  7. package/dist/cjs/interfaces/ApiResult.js +3 -0
  8. package/dist/cjs/interfaces/ApiResult.js.map +1 -0
  9. package/dist/cjs/interfaces/Filter.js +3 -0
  10. package/dist/cjs/interfaces/Filter.js.map +1 -0
  11. package/dist/cjs/interfaces/SortType.js +3 -0
  12. package/dist/cjs/interfaces/SortType.js.map +1 -0
  13. package/dist/cjs/package.json +3 -0
  14. package/dist/cjs/types/ApiCallOptions.js +3 -0
  15. package/dist/cjs/types/ApiCallOptions.js.map +1 -0
  16. package/dist/cjs/types/ApiMethodArguments.js +3 -0
  17. package/dist/cjs/types/ApiMethodArguments.js.map +1 -0
  18. package/dist/cjs/types/ApiMethodOptions.js +3 -0
  19. package/dist/cjs/types/ApiMethodOptions.js.map +1 -0
  20. package/dist/cjs/types/ApiMethodTypes.js +3 -0
  21. package/dist/cjs/types/ApiMethodTypes.js.map +1 -0
  22. package/dist/cjs/types/HttpResponse.js +3 -0
  23. package/dist/cjs/types/HttpResponse.js.map +1 -0
  24. package/dist/cjs/types/httpClientTypes.js +3 -0
  25. package/dist/cjs/types/httpClientTypes.js.map +1 -0
  26. package/dist/cjs/utils/SortTypeFromSortDirection.js +43 -0
  27. package/dist/cjs/utils/SortTypeFromSortDirection.js.map +1 -0
  28. package/dist/cjs/utils/buildQuery.js +96 -0
  29. package/dist/cjs/utils/buildQuery.js.map +1 -0
  30. package/dist/cjs/utils/extractArgsCallbacksAndParams.js +51 -0
  31. package/dist/cjs/utils/extractArgsCallbacksAndParams.js.map +1 -0
  32. package/dist/cjs/utils/extractArgsToastsAndParams.js +49 -0
  33. package/dist/cjs/utils/extractArgsToastsAndParams.js.map +1 -0
  34. package/dist/cjs/utils/handleApiResponse.js +124 -0
  35. package/dist/cjs/utils/handleApiResponse.js.map +1 -0
  36. package/dist/cjs/utils/toFormData.js +58 -0
  37. package/dist/cjs/utils/toFormData.js.map +1 -0
  38. package/dist/cjs/utils/wireMapping.js +123 -0
  39. package/dist/cjs/utils/wireMapping.js.map +1 -0
  40. package/dist/esm/httpClient.js +251 -0
  41. package/dist/esm/httpClient.js.map +1 -0
  42. package/dist/esm/index.js +16 -0
  43. package/dist/esm/index.js.map +1 -0
  44. package/dist/esm/interfaces/ApiResult.js +2 -0
  45. package/dist/esm/interfaces/ApiResult.js.map +1 -0
  46. package/dist/esm/interfaces/Filter.js +2 -0
  47. package/dist/esm/interfaces/Filter.js.map +1 -0
  48. package/dist/esm/interfaces/SortType.js +2 -0
  49. package/dist/esm/interfaces/SortType.js.map +1 -0
  50. package/dist/esm/types/ApiCallOptions.js +2 -0
  51. package/dist/esm/types/ApiCallOptions.js.map +1 -0
  52. package/dist/esm/types/ApiMethodArguments.js +2 -0
  53. package/dist/esm/types/ApiMethodArguments.js.map +1 -0
  54. package/dist/esm/types/ApiMethodOptions.js +2 -0
  55. package/dist/esm/types/ApiMethodOptions.js.map +1 -0
  56. package/dist/esm/types/ApiMethodTypes.js +2 -0
  57. package/dist/esm/types/ApiMethodTypes.js.map +1 -0
  58. package/dist/esm/types/HttpResponse.js +2 -0
  59. package/dist/esm/types/HttpResponse.js.map +1 -0
  60. package/dist/esm/types/httpClientTypes.js +2 -0
  61. package/dist/esm/types/httpClientTypes.js.map +1 -0
  62. package/dist/esm/utils/SortTypeFromSortDirection.js +38 -0
  63. package/dist/esm/utils/SortTypeFromSortDirection.js.map +1 -0
  64. package/dist/esm/utils/buildQuery.js +93 -0
  65. package/dist/esm/utils/buildQuery.js.map +1 -0
  66. package/dist/esm/utils/extractArgsCallbacksAndParams.js +48 -0
  67. package/dist/esm/utils/extractArgsCallbacksAndParams.js.map +1 -0
  68. package/dist/esm/utils/extractArgsToastsAndParams.js +46 -0
  69. package/dist/esm/utils/extractArgsToastsAndParams.js.map +1 -0
  70. package/dist/esm/utils/handleApiResponse.js +121 -0
  71. package/dist/esm/utils/handleApiResponse.js.map +1 -0
  72. package/dist/esm/utils/toFormData.js +55 -0
  73. package/dist/esm/utils/toFormData.js.map +1 -0
  74. package/dist/esm/utils/wireMapping.js +119 -0
  75. package/dist/esm/utils/wireMapping.js.map +1 -0
  76. package/dist/types/httpClient.d.ts +63 -0
  77. package/dist/types/httpClient.d.ts.map +1 -0
  78. package/dist/types/index.d.ts +16 -0
  79. package/dist/types/index.d.ts.map +1 -0
  80. package/dist/types/interfaces/ApiResult.d.ts +32 -0
  81. package/dist/types/interfaces/ApiResult.d.ts.map +1 -0
  82. package/dist/types/interfaces/Filter.d.ts +31 -0
  83. package/dist/types/interfaces/Filter.d.ts.map +1 -0
  84. package/dist/types/interfaces/SortType.d.ts +7 -0
  85. package/dist/types/interfaces/SortType.d.ts.map +1 -0
  86. package/dist/types/types/ApiCallOptions.d.ts +26 -0
  87. package/dist/types/types/ApiCallOptions.d.ts.map +1 -0
  88. package/dist/types/types/ApiMethodArguments.d.ts +6 -0
  89. package/dist/types/types/ApiMethodArguments.d.ts.map +1 -0
  90. package/dist/types/types/ApiMethodOptions.d.ts +8 -0
  91. package/dist/types/types/ApiMethodOptions.d.ts.map +1 -0
  92. package/dist/types/types/ApiMethodTypes.d.ts +47 -0
  93. package/dist/types/types/ApiMethodTypes.d.ts.map +1 -0
  94. package/dist/types/types/HttpResponse.d.ts +2 -0
  95. package/dist/types/types/HttpResponse.d.ts.map +1 -0
  96. package/dist/types/types/httpClientTypes.d.ts +2 -0
  97. package/dist/types/types/httpClientTypes.d.ts.map +1 -0
  98. package/dist/types/utils/SortTypeFromSortDirection.d.ts +13 -0
  99. package/dist/types/utils/SortTypeFromSortDirection.d.ts.map +1 -0
  100. package/dist/types/utils/buildQuery.d.ts +16 -0
  101. package/dist/types/utils/buildQuery.d.ts.map +1 -0
  102. package/dist/types/utils/extractArgsCallbacksAndParams.d.ts +22 -0
  103. package/dist/types/utils/extractArgsCallbacksAndParams.d.ts.map +1 -0
  104. package/dist/types/utils/extractArgsToastsAndParams.d.ts +20 -0
  105. package/dist/types/utils/extractArgsToastsAndParams.d.ts.map +1 -0
  106. package/dist/types/utils/handleApiResponse.d.ts +11 -0
  107. package/dist/types/utils/handleApiResponse.d.ts.map +1 -0
  108. package/dist/types/utils/toFormData.d.ts +10 -0
  109. package/dist/types/utils/toFormData.d.ts.map +1 -0
  110. package/dist/types/utils/wireMapping.d.ts +30 -0
  111. package/dist/types/utils/wireMapping.d.ts.map +1 -0
  112. package/package.json +90 -84
  113. package/scripts/build.mjs +74 -0
  114. package/scripts/generate-api-minimal.mjs +568 -476
  115. package/scripts/openapi-generator/controller-template.mjs +1 -1
  116. package/scripts/openapi-generator/index.mjs +357 -113
  117. package/scripts/openapi-generator/method-template.mjs +201 -244
  118. package/scripts/openapi-generator/names.mjs +37 -86
  119. package/scripts/openapi-generator/openapi-utils.mjs +411 -147
  120. package/scripts/openapi-generator/schema.mjs +518 -238
  121. package/dist/index.cjs +0 -536
  122. package/dist/index.d.cts +0 -285
  123. package/dist/index.d.ts +0 -285
  124. package/dist/index.js +0 -495
@@ -1,5 +1,5 @@
1
1
  import { getOperationTypes, generatedFileHeader } from "./schema.mjs";
2
- import { pascalCase, sanitizeIdentifier } from "./names.mjs";
2
+ import { pascalCase, typePropertyName } from "./names.mjs";
3
3
 
4
4
  function unique(values) {
5
5
  return [...new Set(values.filter(Boolean))];
@@ -9,45 +9,48 @@ function createImport(names, from, typeOnly = false) {
9
9
  const cleanNames = unique(names).sort((a, b) => a.localeCompare(b));
10
10
  if (cleanNames.length === 0) return "";
11
11
  const keyword = typeOnly ? "import type" : "import";
12
- if (cleanNames.length <= 3) {
13
- return `${keyword} { ${cleanNames.join(", ")} } from ${JSON.stringify(from)};`;
14
- }
12
+ if (cleanNames.length <= 3) return `${keyword} { ${cleanNames.join(", ")} } from ${JSON.stringify(from)};`;
15
13
  return `${keyword} {\n${cleanNames.map((name) => ` ${name},`).join("\n")}\n} from ${JSON.stringify(from)};`;
16
14
  }
17
15
 
18
16
  function isPrimitiveType(type) {
19
- return /^(void|any|unknown|string|number|boolean|Blob|File|Record<)/.test(
20
- type,
21
- );
17
+ return /^(void|any|unknown|string|number|boolean|Blob|File|Record<)/.test(type);
22
18
  }
23
19
 
24
20
  function extractTopLevelTypeNames(type) {
25
- const withoutLiterals = String(type ?? "").replace(
26
- /"(?:\\.|[^"])*"|'(?:\\.|[^'])*'/g,
27
- "",
28
- );
21
+ const withoutLiterals = String(type ?? "").replace(/"(?:\\.|[^"])*"|'(?:\\.|[^'])*'/g, "");
29
22
  const matches = withoutLiterals.match(/\b[A-Z][A-Za-z0-9_]*\b/g) ?? [];
30
23
  return matches.filter(
31
- (name) =>
32
- ![
33
- "Array",
34
- "Record",
35
- "Promise",
36
- "HttpResponse",
37
- "Blob",
38
- "File",
39
- "ApiResult",
40
- "RequestParams",
41
- "FormData",
42
- ].includes(name),
24
+ (name) => ![
25
+ "Array", "Record", "Promise", "HttpResponse", "Blob", "File", "ApiResult",
26
+ "RequestParams", "FormData", "ApiClientError",
27
+ ].includes(name),
28
+ );
29
+ }
30
+
31
+ function localPropertyAccess(baseExpression, wireName) {
32
+ return `${baseExpression}[${JSON.stringify(typePropertyName(wireName))}]`;
33
+ }
34
+
35
+ function parameterValueExpression(sourceExpression, parameter) {
36
+ return localPropertyAccess(sourceExpression, parameter.name);
37
+ }
38
+
39
+ function parameterObjectExpression(sourceExpression, parameters) {
40
+ const properties = parameters.map(
41
+ (parameter) => `${JSON.stringify(parameter.name)}: ${parameterValueExpression(
42
+ sourceExpression,
43
+ parameter,
44
+ )}`,
43
45
  );
46
+ return properties.length <= 2
47
+ ? `{ ${properties.join(", ")} }`
48
+ : `{
49
+ ${properties.map((property) => ` ${property},`).join("\n")}
50
+ }`;
44
51
  }
45
52
 
46
- function pathExpression(
47
- routePath,
48
- pathParams,
49
- paramsObjectName = "pathParams",
50
- ) {
53
+ function pathExpression(routePath, pathParams, inputName = "input") {
51
54
  const parts = [];
52
55
  let cursor = 0;
53
56
  const regex = /\{([^}]+)\}/g;
@@ -57,144 +60,169 @@ function pathExpression(
57
60
  const [placeholder, paramName] = match;
58
61
  const before = routePath.slice(cursor, match.index);
59
62
  if (before) parts.push(JSON.stringify(before));
60
- parts.push(
61
- `encodeURIComponent(String(${paramsObjectName}.${sanitizeIdentifier(paramName)}))`,
62
- );
63
+ const parameter = pathParams.find((item) => item.name === paramName);
64
+ const valueExpression = parameter
65
+ ? parameterValueExpression(inputName, parameter)
66
+ : localPropertyAccess(inputName, paramName);
67
+ parts.push(`encodeURIComponent(String(${valueExpression}))`);
63
68
  cursor = match.index + placeholder.length;
64
69
  }
65
70
 
66
71
  const after = routePath.slice(cursor);
67
72
  if (after) parts.push(JSON.stringify(after));
68
-
69
73
  if (parts.length === 0) return JSON.stringify(routePath);
70
74
 
71
75
  return parts
72
- .map((part) =>
73
- /^"/.test(part) ? part.slice(1, -1).replace(/`/g, "\\`") : `\${${part}}`,
74
- )
76
+ .map((part) => /^"/.test(part) ? part.slice(1, -1).replace(/`/g, "\\`") : `\${${part}}`)
75
77
  .join("")
76
78
  .replace(/^/, "`")
77
79
  .replace(/$/, "`");
78
80
  }
79
81
 
80
82
  function requestFormat(responseContentType, responseType) {
81
- if (responseType === "void") return undefined;
82
- if (!responseContentType) return undefined;
83
+ if (responseType === "void" || !responseContentType) return undefined;
83
84
  if (responseContentType.includes("json")) return "json";
84
85
  if (responseContentType.startsWith("text/")) return "text";
85
- if (
86
- responseContentType === "application/octet-stream" ||
87
- responseType === "Blob"
88
- )
89
- return "blob";
86
+ if (responseContentType === "application/octet-stream" || responseType === "Blob") return "blob";
90
87
  return undefined;
91
88
  }
92
89
 
93
90
  function contentTypeEnum(contentType) {
94
91
  switch (contentType) {
95
- case "application/json":
96
- return "ContentType.Json";
97
- case "application/vnd.api+json":
98
- return "ContentType.JsonApi";
99
- case "multipart/form-data":
100
- return "ContentType.FormData";
101
- case "application/x-www-form-urlencoded":
102
- return "ContentType.UrlEncoded";
103
- case "text/plain":
104
- return "ContentType.Text";
105
- default:
106
- return contentType?.includes("json") ? "ContentType.Json" : undefined;
92
+ case "application/json": return "ContentType.Json";
93
+ case "application/vnd.api+json": return "ContentType.JsonApi";
94
+ case "multipart/form-data": return "ContentType.FormData";
95
+ case "application/x-www-form-urlencoded": return "ContentType.UrlEncoded";
96
+ case "text/plain": return "ContentType.Text";
97
+ default: return contentType?.includes("json") ? "ContentType.Json" : undefined;
107
98
  }
108
99
  }
109
100
 
110
- function queryExpression(operationTypes, queryValueName = "query") {
111
- if (!operationTypes.hasQueryParams) return undefined;
112
-
113
- if (!operationTypes.hasPathParams) {
114
- return `${queryValueName} ?? {}`;
115
- }
116
-
117
- const entries = operationTypes.queryParams.map((parameter) => {
118
- const identifier = sanitizeIdentifier(parameter.name);
119
- return `${JSON.stringify(parameter.name)}: pathParams.${identifier}`;
120
- });
121
-
122
- return `{ ${entries.join(", ")} }`;
101
+ function toWireExpression(valueExpression, schemaKey) {
102
+ if (!schemaKey) return valueExpression;
103
+ return `toWireValue(${valueExpression}, typedApiWireSchemas[${JSON.stringify(schemaKey)}], typedApiWireSchemas)`;
123
104
  }
124
105
 
125
106
  function requestItems(operationInfo, operationTypes, options = {}) {
107
+ const inputName = options.inputName ?? "input";
126
108
  const items = [
127
- `path: ${pathExpression(operationInfo.routePath, operationTypes.pathParams)}`,
109
+ "...params",
110
+ `path: ${pathExpression(
111
+ operationInfo.routePath,
112
+ operationTypes.parametersByLocation.path,
113
+ inputName,
114
+ )}`,
128
115
  `method: ${JSON.stringify(operationInfo.method)}`,
129
116
  ];
130
117
 
131
- const query = queryExpression(
132
- operationTypes,
133
- options.queryValueName ?? "query",
134
- );
135
- if (query) items.push(`query: ${query}`);
118
+ if (options.queryValueName) {
119
+ items.push(`query: ${options.queryValueName}`);
120
+ } else if (operationTypes.hasQueryParams) {
121
+ items.push(
122
+ `query: ${parameterObjectExpression(
123
+ inputName,
124
+ operationTypes.parametersByLocation.query,
125
+ )}`,
126
+ );
127
+ }
136
128
 
137
129
  if (operationTypes.bodyType) {
138
- items.push(`body: ${options.bodyExpression ?? "data"}`);
130
+ const bodyExpression = options.bodyValueName ?? "data";
131
+ items.push(`body: ${toWireExpression(bodyExpression, operationTypes.bodyWireSchemaKey)}`);
132
+ }
133
+
134
+ if (operationTypes.hasHeaderParams || operationTypes.hasCookieParams) {
135
+ const headerSources = ["params.headers"];
136
+ if (operationTypes.hasHeaderParams) {
137
+ const headersExpression = parameterObjectExpression(
138
+ inputName,
139
+ operationTypes.parametersByLocation.header,
140
+ );
141
+ headerSources.push(`toRequestHeaders(${headersExpression})`);
142
+ }
143
+ if (operationTypes.hasCookieParams) {
144
+ const cookiesExpression = parameterObjectExpression(
145
+ inputName,
146
+ operationTypes.parametersByLocation.cookie,
147
+ );
148
+ const cookieCondition = operationTypes.parametersByLocation.cookie
149
+ .map((parameter) => `${parameterValueExpression(inputName, parameter)} !== undefined`)
150
+ .join(" || ") || "false";
151
+ headerSources.push(
152
+ `${cookieCondition} ? { Cookie: toCookieHeader(${cookiesExpression}) } : undefined`,
153
+ );
154
+ }
155
+ items.push(`headers: mergeHeaders(${headerSources.join(", ")})`);
139
156
  }
140
157
 
141
158
  const type = contentTypeEnum(operationTypes.contentType);
142
159
  if (type) items.push(`type: ${type}`);
143
-
144
- const format = requestFormat(
145
- operationTypes.responseContentType,
146
- operationTypes.responseType,
147
- );
160
+ const format = requestFormat(operationTypes.responseContentType, operationTypes.responseType);
148
161
  if (format) items.push(`format: ${JSON.stringify(format)}`);
149
-
150
- items.push("...params");
151
162
  return items;
152
163
  }
153
164
 
154
- function callbackOptions(defaultHandlers) {
155
- if (!defaultHandlers) return "{ onSuccess, onError }";
165
+ function callbackOptions(operationTypes, defaultHandlers) {
166
+ const items = [];
167
+ if (defaultHandlers) {
168
+ items.push("onSuccess: onSuccess ?? typedApiDefaultSuccessHandler");
169
+ items.push("onError: onError ?? typedApiDefaultErrorHandler");
170
+ } else {
171
+ items.push("onSuccess");
172
+ items.push("onError");
173
+ }
174
+
175
+ if (operationTypes.responseWireSchemaKey) {
176
+ items.push(
177
+ `transformResponse: (value) => fromWireValue(value, typedApiWireSchemas[${JSON.stringify(
178
+ operationTypes.responseWireSchemaKey,
179
+ )}], typedApiWireSchemas) as ${operationTypes.responseType}`,
180
+ );
181
+ }
182
+ if (operationTypes.errorWireSchemaKey) {
183
+ items.push(
184
+ `transformError: (value) => fromWireValue(value, typedApiWireSchemas[${JSON.stringify(
185
+ operationTypes.errorWireSchemaKey,
186
+ )}], typedApiWireSchemas) as ${operationTypes.errorType}`,
187
+ );
188
+ }
156
189
 
157
- return "{ onSuccess: onSuccess ?? typedApiDefaultSuccessHandler, onError: onError ?? typedApiDefaultErrorHandler }";
190
+ return items.length <= 2
191
+ ? `{ ${items.join(", ")} }`
192
+ : `{\n ${items.join(",\n ")},\n }`;
158
193
  }
159
194
 
160
195
  function optionsType(operationTypes) {
161
- return `ApiMethodOptions<${operationTypes.responseType}, any, RequestParams>`;
196
+ return `ApiMethodOptions<${operationTypes.responseType}, ${operationTypes.errorType}, RequestParams>`;
162
197
  }
163
198
 
164
199
  function promiseType(operationTypes) {
165
- return `Promise<ApiResult<${operationTypes.responseType}>>`;
200
+ return `Promise<ApiResult<${operationTypes.responseType}, ${operationTypes.errorType}>>`;
166
201
  }
167
202
 
168
203
  function isPaginated(operationTypes) {
169
- return /(?:Api)?Pagination(?:Sort)?Response\b|Paged(?:Response|Result)?\b|Paginated(?:Response|Result)?\b/.test(
170
- operationTypes.responseType,
171
- );
204
+ return Boolean(operationTypes.paginationMetadata) ||
205
+ /(?:Api)?Pagination(?:Sort)?Response\b|Paged(?:Response|Result)?\b|Paginated(?:Response|Result)?\b/.test(
206
+ operationTypes.responseType,
207
+ );
172
208
  }
173
209
 
174
210
  function methodDescription(operationInfo, operationTypes) {
175
- const description =
176
- operationInfo.operation.description ||
177
- operationInfo.operation.summary ||
178
- "No description";
211
+ const description = operationInfo.operation.description || operationInfo.operation.summary || "No description";
179
212
  const tags = (operationInfo.operation.tags ?? []).join(", ");
180
-
181
- return `/**\n * ${description.replace(/\n/g, "\n * ")}\n *\n * @tags ${tags}\n * @name ${operationTypes.operationId}\n * @request ${operationInfo.method}:${operationInfo.routePath}\n */`;
213
+ return `/**\n * ${description.replace(/\n/g, "\n * ")}\n *\n * @tags ${tags}\n * @name ${operationTypes.methodNameSource}\n * @request ${operationInfo.method}:${operationInfo.routePath}\n */`;
182
214
  }
183
215
 
184
216
  function createRequestCall(operationInfo, operationTypes, options = {}) {
185
217
  const items = requestItems(operationInfo, operationTypes, options)
186
218
  .map((item) => ` ${item},`)
187
219
  .join("\n");
188
-
189
- return `request<${operationTypes.responseType}, any>({\n${items}\n })`;
220
+ return `request<${operationTypes.responseType}, ${operationTypes.errorType}>({\n${items}\n })`;
190
221
  }
191
222
 
192
223
  function createPaginatedMethod(operationInfo, operationTypes, defaultHandlers) {
193
- const queryTypeName = `${pascalCase(operationTypes.methodName)}Query`;
194
- const requestCall = createRequestCall(operationInfo, operationTypes, {
195
- queryValueName: "builtQuery",
196
- });
197
-
224
+ const queryTypeName = operationTypes.parameterTypeNames.query;
225
+ const requestCall = createRequestCall(operationInfo, operationTypes, { queryValueName: "builtQuery" });
198
226
  return `${methodDescription(operationInfo, operationTypes)}
199
227
  export async function ${operationTypes.methodName}(
200
228
  filters: FilterFormValues<${queryTypeName}>[] = [],
@@ -204,48 +232,40 @@ export async function ${operationTypes.methodName}(
204
232
  sortDirection?: SortDirection,
205
233
  options: ${optionsType(operationTypes)} = {},
206
234
  ): ${promiseType(operationTypes)} {
207
- const { onSuccess, onError, params } = options;
235
+ const { onSuccess, onError, params = {} } = options;
208
236
  const builtQuery = buildQuery<
209
237
  ${queryTypeName},
210
238
  UnwrapArray<ExtractDataIfPaginated<${operationTypes.responseType}>>
211
239
  >(filters, page, pageSize, sortBy, sortDirection);
212
240
 
213
- return handleApiResponse<${operationTypes.responseType}, any>(
241
+ return handleApiResponse<${operationTypes.responseType}, ${operationTypes.errorType}>(
214
242
  () => ${requestCall},
215
- ${callbackOptions(defaultHandlers)},
243
+ ${callbackOptions(operationTypes, defaultHandlers)},
216
244
  );
217
245
  }`;
218
246
  }
219
247
 
220
- function createSimpleQueryMethod(
221
- operationInfo,
222
- operationTypes,
223
- defaultHandlers,
224
- ) {
225
- const queryTypeName = `${pascalCase(operationTypes.methodName)}Query`;
226
- const requestCall = createRequestCall(operationInfo, operationTypes, {
227
- queryValueName: "query",
228
- });
229
-
230
- return `${methodDescription(operationInfo, operationTypes)}
231
- export async function ${operationTypes.methodName}(
232
- query?: ${queryTypeName},
233
- options: ${optionsType(operationTypes)} = {},
234
- ): ${promiseType(operationTypes)} {
235
- const { onSuccess, onError, params } = options;
236
-
237
- return handleApiResponse<${operationTypes.responseType}, any>(
238
- () => ${requestCall},
239
- ${callbackOptions(defaultHandlers)},
240
- );
241
- }`;
248
+ function regularParameterArgumentName(operationTypes) {
249
+ if (operationTypes.hasPathParams) return "pathParams";
250
+ if (
251
+ operationTypes.hasQueryParams &&
252
+ !operationTypes.hasHeaderParams &&
253
+ !operationTypes.hasCookieParams
254
+ ) return "query";
255
+ return "requestParams";
242
256
  }
243
257
 
244
- function wrapperArguments(operationTypes) {
258
+ function createRegularMethod(operationInfo, operationTypes, defaultHandlers) {
245
259
  const args = [];
246
-
247
- if (operationTypes.paramsTypeName && operationTypes.hasPathParams) {
248
- args.push(`pathParams: ${operationTypes.paramsTypeName}`);
260
+ const parameterArgumentName = operationTypes.paramsTypeName
261
+ ? regularParameterArgumentName(operationTypes)
262
+ : undefined;
263
+
264
+ if (operationTypes.paramsTypeName) {
265
+ const canOmitParams = operationTypes.allParametersOptional && !operationTypes.bodyType;
266
+ args.push(
267
+ `${parameterArgumentName}: ${operationTypes.paramsTypeName}${canOmitParams ? " = {}" : ""}`,
268
+ );
249
269
  }
250
270
 
251
271
  if (operationTypes.bodyType) {
@@ -253,27 +273,20 @@ function wrapperArguments(operationTypes) {
253
273
  }
254
274
 
255
275
  args.push(`options: ${optionsType(operationTypes)} = {}`);
256
- return args.join(",\n ");
257
- }
258
-
259
- function createRegularMethod(operationInfo, operationTypes, defaultHandlers) {
260
- const isFormData = operationTypes.contentType === "multipart/form-data";
261
276
  const requestCall = createRequestCall(operationInfo, operationTypes, {
262
- bodyExpression: isFormData ? "formData" : "data",
277
+ inputName: parameterArgumentName ?? "undefined",
278
+ bodyValueName: operationTypes.bodyType ? "data" : undefined,
263
279
  });
264
280
 
265
- const formDataLine = isFormData
266
- ? "\n const formData = toFormData(data);\n"
267
- : "";
268
-
269
281
  return `${methodDescription(operationInfo, operationTypes)}
270
282
  export async function ${operationTypes.methodName}(
271
- ${wrapperArguments(operationTypes)}
283
+ ${args.join(",\n ")}
272
284
  ): ${promiseType(operationTypes)} {
273
- const { onSuccess, onError, params } = options;${formDataLine}
274
- return handleApiResponse<${operationTypes.responseType}, any>(
285
+ const { onSuccess, onError, params = {} } = options;
286
+
287
+ return handleApiResponse<${operationTypes.responseType}, ${operationTypes.errorType}>(
275
288
  () => ${requestCall},
276
- ${callbackOptions(defaultHandlers)},
289
+ ${callbackOptions(operationTypes, defaultHandlers)},
277
290
  );
278
291
  }`;
279
292
  }
@@ -282,28 +295,14 @@ function createMethod(operationInfo, operationTypes, defaultHandlers, options) {
282
295
  if (
283
296
  operationTypes.hasQueryParams &&
284
297
  !operationTypes.hasPathParams &&
298
+ !operationTypes.bodyType &&
299
+ !operationTypes.hasHeaderParams &&
300
+ !operationTypes.hasCookieParams &&
285
301
  options.useFilterFormValues !== false &&
286
302
  isPaginated(operationTypes)
287
303
  ) {
288
- return createPaginatedMethod(
289
- operationInfo,
290
- operationTypes,
291
- defaultHandlers,
292
- );
304
+ return createPaginatedMethod(operationInfo, operationTypes, defaultHandlers);
293
305
  }
294
-
295
- if (
296
- operationTypes.hasQueryParams &&
297
- !operationTypes.hasPathParams &&
298
- !operationTypes.bodyType
299
- ) {
300
- return createSimpleQueryMethod(
301
- operationInfo,
302
- operationTypes,
303
- defaultHandlers,
304
- );
305
- }
306
-
307
306
  return createRegularMethod(operationInfo, operationTypes, defaultHandlers);
308
307
  }
309
308
 
@@ -316,125 +315,83 @@ export function controllerNameForOperation(operationInfo, options = {}) {
316
315
  const [tag] = operationInfo.operation.tags ?? [];
317
316
  if (tag) return pascalCase(tag, "Default");
318
317
  }
319
-
320
- const firstPathPart =
321
- operationInfo.routePath.split("/").filter(Boolean)[1] ??
322
- operationInfo.routePath.split("/").filter(Boolean)[0] ??
323
- "Default";
324
-
325
- return pascalCase(firstPathPart, "Default");
318
+ const parts = operationInfo.routePath.split("/").filter(Boolean);
319
+ return pascalCase(parts[1] ?? parts[0] ?? "Default", "Default");
326
320
  }
327
321
 
328
- export function generateMethodFile(
329
- openApi,
330
- controllerName,
331
- operations,
332
- options = {},
333
- ) {
322
+ export function generateMethodFile(openApi, controllerName, operations, options = {}) {
334
323
  const dataContractImports = [];
324
+ const dataContractValueImports = [];
335
325
  const helperTypeImports = ["ApiResult", "ApiMethodOptions"];
336
326
  const helperValueImports = ["handleApiResponse"];
337
327
  const methods = [];
338
- const queryAliases = [];
339
328
  let needsContentType = false;
340
- let needsToFormData = false;
341
329
  let needsPaginationTypes = false;
330
+ let needsHeaderHelpers = false;
331
+ let needsWireMappings = false;
342
332
 
343
333
  for (const operationInfo of operations) {
344
334
  const operationTypes = getOperationTypes(openApi, operationInfo, options);
345
- if (!operationTypes.operationId || !operationTypes.methodName) continue;
346
335
 
347
- if (operationTypes.paramsTypeName)
348
- dataContractImports.push(operationTypes.paramsTypeName);
349
- if (
350
- operationTypes.bodyType &&
351
- !isPrimitiveType(operationTypes.bodyType)
352
- ) {
353
- dataContractImports.push(
354
- ...extractTopLevelTypeNames(operationTypes.bodyType),
355
- );
336
+ for (const typeName of Object.values(operationTypes.parameterTypeNames)) dataContractImports.push(typeName);
337
+ if (operationTypes.paramsTypeName) dataContractImports.push(operationTypes.paramsTypeName);
338
+ if (operationTypes.bodyType && !isPrimitiveType(operationTypes.bodyType)) {
339
+ dataContractImports.push(...extractTopLevelTypeNames(operationTypes.bodyType));
356
340
  }
357
341
  if (!isPrimitiveType(operationTypes.responseType)) {
358
- dataContractImports.push(
359
- ...extractTopLevelTypeNames(operationTypes.responseType),
360
- );
342
+ dataContractImports.push(...extractTopLevelTypeNames(operationTypes.responseType));
361
343
  }
362
-
363
- if (operationTypes.hasQueryParams && !operationTypes.hasPathParams) {
364
- const queryTypeName = `${pascalCase(operationTypes.methodName)}Query`;
365
- queryAliases.push(
366
- `export type ${queryTypeName} = NonNullable<${operationTypes.paramsTypeName}>;`,
367
- );
344
+ if (!isPrimitiveType(operationTypes.errorType)) {
345
+ dataContractImports.push(...extractTopLevelTypeNames(operationTypes.errorType));
368
346
  }
369
347
 
370
348
  if (contentTypeEnum(operationTypes.contentType)) needsContentType = true;
371
- if (operationTypes.contentType === "multipart/form-data")
372
- needsToFormData = true;
349
+ if (operationTypes.hasHeaderParams || operationTypes.hasCookieParams) needsHeaderHelpers = true;
350
+ if (
351
+ operationTypes.bodyWireSchemaKey ||
352
+ operationTypes.responseWireSchemaKey ||
353
+ operationTypes.errorWireSchemaKey
354
+ ) {
355
+ needsWireMappings = true;
356
+ }
373
357
  if (
374
358
  operationTypes.hasQueryParams &&
375
- !operationTypes.hasPathParams &&
376
- options.useFilterFormValues !== false &&
377
- isPaginated(operationTypes)
359
+ !operationTypes.hasPathParams && !operationTypes.bodyType &&
360
+ !operationTypes.hasHeaderParams && !operationTypes.hasCookieParams &&
361
+ options.useFilterFormValues !== false && isPaginated(operationTypes)
378
362
  ) {
379
363
  needsPaginationTypes = true;
380
364
  }
381
365
 
382
- methods.push(
383
- createMethod(
384
- operationInfo,
385
- operationTypes,
386
- options.defaultHandlers,
387
- options,
388
- ),
389
- );
366
+ methods.push(createMethod(operationInfo, operationTypes, options.defaultHandlers, options));
390
367
  }
391
368
 
392
- if (needsToFormData) helperValueImports.push("toFormData");
369
+ if (needsHeaderHelpers) helperValueImports.push("mergeHeaders", "toCookieHeader", "toRequestHeaders");
370
+ if (needsWireMappings) {
371
+ dataContractValueImports.push("typedApiWireSchemas");
372
+ helperValueImports.push("fromWireValue", "toWireValue");
373
+ }
393
374
  if (needsPaginationTypes) {
394
375
  helperValueImports.push("buildQuery");
395
376
  helperTypeImports.push(
396
- "ExtractDataIfPaginated",
397
- "FilterFormValues",
398
- "SortableKeys",
399
- "SortDirection",
400
- "UnwrapArray",
377
+ "ExtractDataIfPaginated", "FilterFormValues", "SortableKeys", "SortDirection", "UnwrapArray",
401
378
  );
402
379
  }
403
380
 
404
- const defaultHandlers = options.defaultHandlers;
405
381
  const useTypeOnlyImports = options.useTypeOnlyImports !== false;
382
+ const defaultHandlers = options.defaultHandlers;
406
383
  const imports = [
407
384
  generatedFileHeader(),
408
- createImport(
409
- ["request", ...(needsContentType ? ["ContentType"] : [])],
410
- "../generated/http-client",
411
- ),
412
- createImport(
413
- ["RequestParams"],
414
- "../generated/http-client",
415
- useTypeOnlyImports,
416
- ),
417
- createImport(
418
- dataContractImports,
419
- "../generated/data-contracts",
420
- useTypeOnlyImports,
421
- ),
422
- createImport(
423
- helperValueImports,
424
- options.runtimePackageName ?? "typedapi-client-helpers",
425
- ),
426
- createImport(
427
- helperTypeImports,
428
- options.runtimePackageName ?? "typedapi-client-helpers",
429
- useTypeOnlyImports,
430
- ),
385
+ createImport(["request", ...(needsContentType ? ["ContentType"] : [])], "../generated/http-client"),
386
+ createImport(["RequestParams"], "../generated/http-client", useTypeOnlyImports),
387
+ createImport(dataContractValueImports, "../generated/data-contracts"),
388
+ createImport(dataContractImports, "../generated/data-contracts", useTypeOnlyImports),
389
+ createImport(helperValueImports, options.runtimePackageName ?? "typedapi-client-helpers"),
390
+ createImport(helperTypeImports, options.runtimePackageName ?? "typedapi-client-helpers", useTypeOnlyImports),
431
391
  defaultHandlers
432
392
  ? `import { ${defaultHandlers.success} as typedApiDefaultSuccessHandler, ${defaultHandlers.error} as typedApiDefaultErrorHandler } from ${JSON.stringify(defaultHandlers.path)};`
433
393
  : "",
434
394
  ].filter(Boolean);
435
395
 
436
- const aliases = unique(queryAliases).join("\n");
437
- const aliasBlock = aliases ? `${aliases}\n\n` : "";
438
-
439
- return `${imports.join("\n")}\n\n${aliasBlock}${methods.join("\n\n")}\n`;
396
+ return `${imports.join("\n")}\n\n${methods.join("\n\n")}\n`;
440
397
  }