typedapi-client-helpers 0.2.1 → 0.3.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +231 -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 +132 -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 +128 -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 +41 -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 +12 -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 +348 -113
  117. package/scripts/openapi-generator/method-template.mjs +405 -440
  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 +737 -455
  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,455 +1,737 @@
1
- import {
2
- dereference,
3
- getRequestBodyContent,
4
- getResponseContent,
5
- getSuccessResponse,
6
- } from "./openapi-utils.mjs";
7
- import { camelCase, pascalCase, propertyKey, refName } from "./names.mjs";
8
-
9
- function jsDocFromSchema(schema, indent = "") {
10
- const lines = [];
11
-
12
- if (schema?.description) {
13
- lines.push(...String(schema.description).split(/\r?\n/));
14
- }
15
-
16
- if (schema?.format) {
17
- lines.push(`@format ${schema.format}`);
18
- }
19
-
20
- if (Object.prototype.hasOwnProperty.call(schema ?? {}, "default")) {
21
- lines.push(`@default ${JSON.stringify(schema.default)}`);
22
- }
23
-
24
- if (schema?.deprecated) {
25
- lines.push("@deprecated");
26
- }
27
-
28
- if (lines.length === 0) {
29
- return "";
30
- }
31
-
32
- if (lines.length === 1) {
33
- return `${indent}/** ${lines[0]} */\n`;
34
- }
35
-
36
- return `${indent}/**\n${lines.map((line) => `${indent} * ${line}`).join("\n")}\n${indent} */\n`;
37
- }
38
-
39
- function unionTypes(types) {
40
- const unique = [...new Set(types.filter(Boolean))];
41
- return unique.length > 0 ? unique.join(" | ") : "any";
42
- }
43
-
44
- export class TypeResolver {
45
- constructor(openApi, options = {}) {
46
- this.openApi = openApi;
47
- this.options = options;
48
- this.inlineTypes = new Map();
49
- }
50
-
51
- resolve(schemaOrRef, contextName = "Anonymous", options = {}) {
52
- if (!schemaOrRef) {
53
- return "any";
54
- }
55
-
56
- if (schemaOrRef.$ref) {
57
- return refName(schemaOrRef.$ref);
58
- }
59
-
60
- const schema = dereference(this.openApi, schemaOrRef) ?? schemaOrRef;
61
- const nullable =
62
- schema.nullable ||
63
- (Array.isArray(schema.type) && schema.type.includes("null"));
64
- let type = this.resolveNonNullable(schema, contextName, options);
65
-
66
- if (nullable && !type.includes("null")) {
67
- type = `${type} | null`;
68
- }
69
-
70
- return type;
71
- }
72
-
73
- resolveNonNullable(schema, contextName, options = {}) {
74
- if (schema.const !== undefined) {
75
- return JSON.stringify(schema.const);
76
- }
77
-
78
- if (Array.isArray(schema.enum)) {
79
- if (options.inlineEnumAsUnion) {
80
- return unionTypes(
81
- this.enumLiteralValues(schema).map((value) => JSON.stringify(value)),
82
- );
83
- }
84
-
85
- return pascalCase(contextName);
86
- }
87
-
88
- if (schema.oneOf || schema.anyOf) {
89
- const variants = schema.oneOf ?? schema.anyOf;
90
- return unionTypes(
91
- variants.map((item, index) =>
92
- this.resolve(item, `${contextName}${index + 1}`, options),
93
- ),
94
- );
95
- }
96
-
97
- if (schema.allOf) {
98
- return schema.allOf
99
- .map((item, index) =>
100
- this.resolve(item, `${contextName}${index + 1}`, options),
101
- )
102
- .join(" & ");
103
- }
104
-
105
- const schemaType = Array.isArray(schema.type)
106
- ? schema.type.find((item) => item !== "null")
107
- : schema.type;
108
-
109
- switch (schemaType) {
110
- case "integer":
111
- case "number":
112
- return "number";
113
- case "boolean":
114
- return "boolean";
115
- case "string":
116
- if (schema.format === "binary") {
117
- return options.binaryAsBlob ? "Blob" : "File";
118
- }
119
- return "string";
120
- case "array": {
121
- const itemType = this.resolve(
122
- schema.items ?? {},
123
- `${contextName}Item`,
124
- options,
125
- );
126
- return /^[A-Za-z_$][\w$]*(?:<.*>)?$/.test(itemType) ||
127
- itemType.endsWith("[]")
128
- ? `${itemType}[]`
129
- : `(${itemType})[]`;
130
- }
131
- case "object":
132
- default:
133
- if (schema.properties) {
134
- return this.createInlineObjectType(schema, contextName, options);
135
- }
136
-
137
- if (schema.additionalProperties) {
138
- const valueType =
139
- schema.additionalProperties === true
140
- ? "any"
141
- : this.resolve(
142
- schema.additionalProperties,
143
- `${contextName}Value`,
144
- options,
145
- );
146
- return `Record<string, ${valueType}>`;
147
- }
148
-
149
- return schemaType === "object" ? "Record<string, any>" : "any";
150
- }
151
- }
152
-
153
- createInlineObjectType(schema, contextName, options = {}) {
154
- const name = pascalCase(contextName);
155
-
156
- if (!this.inlineTypes.has(name)) {
157
- this.inlineTypes.set(name, schema);
158
- }
159
-
160
- return name;
161
- }
162
-
163
- createInterface(name, schema) {
164
- const required = new Set(schema.required ?? []);
165
- const lines = [`export interface ${name} {`];
166
-
167
- for (const [propertyName, propertySchema] of Object.entries(
168
- schema.properties ?? {},
169
- )) {
170
- // Required names must still be checked against the original OpenAPI name.
171
- const propertyRequired = required.has(propertyName);
172
- const optionalToken = propertyRequired ? "" : "?";
173
-
174
- const propertyType = this.resolve(
175
- propertySchema,
176
- `${name}${pascalCase(propertyName)}`,
177
- { inlineEnumAsUnion: true },
178
- );
179
-
180
- const resolvedSchema = dereference(this.openApi, propertySchema);
181
- const typescriptPropertyName = camelCase(propertyName);
182
-
183
- lines.push(
184
- jsDocFromSchema(resolvedSchema, " ") +
185
- ` ${propertyKey(typescriptPropertyName)}${optionalToken}: ${propertyType};`,
186
- );
187
- }
188
-
189
- if (schema.additionalProperties) {
190
- const valueType =
191
- schema.additionalProperties === true
192
- ? "any"
193
- : this.resolve(schema.additionalProperties, `${name}Value`, {
194
- inlineEnumAsUnion: true,
195
- });
196
-
197
- lines.push(` [key: string]: ${valueType};`);
198
- }
199
-
200
- lines.push("}");
201
- return lines.join("\n");
202
- }
203
-
204
- createEnum(name, schema) {
205
- const values = schema.enum ?? [];
206
-
207
- if (this.options.generateUnionEnums) {
208
- return `export type ${name} = ${unionTypes(this.enumLiteralValues(schema).map((value) => JSON.stringify(value)))};`;
209
- }
210
-
211
- const lines = [`export const ${name} = {`];
212
-
213
- for (const [index, value] of values.entries()) {
214
- const memberName = this.enumMemberName(schema, value, index);
215
- const enumValue = this.enumRuntimeValue(schema, value, index);
216
- lines.push(` ${memberName}: ${JSON.stringify(enumValue)},`);
217
- }
218
-
219
- lines.push("} as const;");
220
- lines.push(`export type ${name} = (typeof ${name})[keyof typeof ${name}];`);
221
- return lines.join("\n");
222
- }
223
-
224
- enumMetadataNames(schema) {
225
- const enumNames =
226
- schema?.["x-enumNames"] ??
227
- schema?.["x-enum-varnames"] ??
228
- schema?.["x-enumVarnames"];
229
-
230
- return Array.isArray(enumNames) ? enumNames : [];
231
- }
232
-
233
- enumLiteralValues(schema) {
234
- const values = schema.enum ?? [];
235
- const enumNames = this.enumMetadataNames(schema);
236
-
237
- if (this.options.enumNamesAsValues && enumNames.length > 0) {
238
- return values.map((value, index) => enumNames[index] ?? value);
239
- }
240
-
241
- return values;
242
- }
243
-
244
- enumRuntimeValue(schema, value, index) {
245
- const enumNames = this.enumMetadataNames(schema);
246
-
247
- if (this.options.enumNamesAsValues && enumNames.length > 0) {
248
- return enumNames[index] ?? value;
249
- }
250
-
251
- return value;
252
- }
253
-
254
- enumMemberName(schema, value, index) {
255
- const enumNames = this.enumMetadataNames(schema);
256
- const rawName = enumNames[index] ?? value;
257
- return pascalCase(String(rawName), "Value").replace(/^[0-9]/, "_$&");
258
- }
259
- }
260
-
261
- function shouldUseInterface(schema) {
262
- return Boolean(
263
- schema?.properties ||
264
- schema?.additionalProperties ||
265
- schema?.type === "object",
266
- );
267
- }
268
-
269
- export function generateDataContracts(openApi, operations, options = {}) {
270
- const resolver = new TypeResolver(openApi, options);
271
- const declarations = [];
272
- const schemas = openApi.components?.schemas ?? {};
273
-
274
- const schemaEntries = Object.entries(schemas).sort(
275
- ([aName, aSchema], [bName, bSchema]) => {
276
- const aIsEnum = Array.isArray(aSchema.enum);
277
- const bIsEnum = Array.isArray(bSchema.enum);
278
- if (aIsEnum !== bIsEnum) return aIsEnum ? -1 : 1;
279
- return aName.localeCompare(bName);
280
- },
281
- );
282
-
283
- for (const [name, schema] of schemaEntries) {
284
- if (Array.isArray(schema.enum)) {
285
- declarations.push(resolver.createEnum(name, schema));
286
- } else if (shouldUseInterface(schema)) {
287
- declarations.push(resolver.createInterface(name, schema));
288
- } else {
289
- declarations.push(
290
- `export type ${name} = ${resolver.resolve(schema, name, { inlineEnumAsUnion: true })};`,
291
- );
292
- }
293
- }
294
-
295
- for (const operationInfo of operations) {
296
- const operationId = operationInfo.operation.operationId;
297
- if (!operationId) continue;
298
-
299
- const parameterProperties = operationInfo.parameters.map((parameter) => ({
300
- name: parameter.name,
301
- schema: parameter.schema ?? {},
302
- required: Boolean(parameter.required),
303
- }));
304
-
305
- if (parameterProperties.length > 0) {
306
- declarations.push(
307
- createParamsInterface(
308
- openApi,
309
- resolver,
310
- `${operationId}Params`,
311
- parameterProperties,
312
- ),
313
- );
314
- }
315
-
316
- const bodyContent = getRequestBodyContent(
317
- operationInfo.operation.requestBody,
318
- );
319
- const bodySchema = bodyContent?.mediaType?.schema;
320
-
321
- if (
322
- bodySchema &&
323
- !bodySchema.$ref &&
324
- (bodyContent.contentType === "multipart/form-data" ||
325
- shouldUseInterface(bodySchema))
326
- ) {
327
- const payloadName = `${operationId}Payload`;
328
- if (!schemas[payloadName]) {
329
- declarations.push(resolver.createInterface(payloadName, bodySchema));
330
- }
331
- }
332
-
333
- const successResponse = getSuccessResponse(
334
- operationInfo.operation,
335
- options.defaultResponseAsSuccess,
336
- );
337
- const responseContent = getResponseContent(successResponse?.response);
338
- const responseSchema = responseContent?.mediaType?.schema;
339
-
340
- if (responseSchema && !responseSchema.$ref) {
341
- resolver.resolve(responseSchema, `${operationId}Response`, {
342
- binaryAsBlob:
343
- responseContent?.contentType === "application/octet-stream",
344
- inlineEnumAsUnion: true,
345
- });
346
- }
347
- }
348
-
349
- for (const [name, schema] of resolver.inlineTypes.entries()) {
350
- if (!schemas[name]) {
351
- declarations.push(resolver.createInterface(name, schema));
352
- }
353
- }
354
-
355
- return `${generatedFileHeader()}\n\n${declarations.join("\n\n")}\n`;
356
- }
357
-
358
- function createParamsInterface(openApi, resolver, name, parameters) {
359
- const required = new Set(
360
- parameters
361
- .filter((parameter) => parameter.required)
362
- .map((parameter) => parameter.name),
363
- );
364
- const lines = [`export interface ${name} {`];
365
-
366
- for (const parameter of parameters) {
367
- const parameterSchema = dereference(openApi, parameter.schema);
368
- const optionalToken = required.has(parameter.name) ? "" : "?";
369
- const parameterType = resolver.resolve(
370
- parameter.schema,
371
- `${name}${pascalCase(parameter.name)}`,
372
- { inlineEnumAsUnion: true },
373
- );
374
- lines.push(
375
- jsDocFromSchema(parameterSchema, " ") +
376
- ` ${propertyKey(parameter.name)}${optionalToken}: ${parameterType};`,
377
- );
378
- }
379
-
380
- lines.push("}");
381
- return lines.join("\n");
382
- }
383
-
384
- export function getOperationTypes(openApi, operationInfo, options = {}) {
385
- const resolver = new TypeResolver(openApi, options);
386
- const operationId = operationInfo.operation.operationId;
387
- const parameters = operationInfo.parameters ?? [];
388
- const pathParams = parameters.filter((parameter) => parameter.in === "path");
389
- const queryParams = parameters.filter(
390
- (parameter) => parameter.in === "query",
391
- );
392
- const requestBodyContent = getRequestBodyContent(
393
- operationInfo.operation.requestBody,
394
- );
395
- const requestBodySchema = requestBodyContent?.mediaType?.schema;
396
- const successResponse = getSuccessResponse(
397
- operationInfo.operation,
398
- options.defaultResponseAsSuccess,
399
- );
400
- const responseContent = getResponseContent(successResponse?.response);
401
- const responseSchema = responseContent?.mediaType?.schema;
402
-
403
- let bodyType;
404
- if (requestBodySchema) {
405
- if (requestBodySchema.$ref) {
406
- bodyType = resolver.resolve(requestBodySchema, `${operationId}Payload`);
407
- } else if (
408
- requestBodyContent?.contentType === "multipart/form-data" ||
409
- shouldUseInterface(requestBodySchema)
410
- ) {
411
- bodyType = `${operationId}Payload`;
412
- } else {
413
- bodyType = resolver.resolve(requestBodySchema, `${operationId}Payload`, {
414
- inlineEnumAsUnion: true,
415
- });
416
- }
417
- }
418
-
419
- let responseType = "void";
420
- if (responseSchema) {
421
- responseType = resolver.resolve(responseSchema, `${operationId}Response`, {
422
- binaryAsBlob: responseContent?.contentType === "application/octet-stream",
423
- inlineEnumAsUnion: true,
424
- });
425
- }
426
-
427
- return {
428
- operationId,
429
- methodName: operationId
430
- ? operationId.charAt(0).toLowerCase() + operationId.slice(1)
431
- : undefined,
432
- paramsTypeName: parameters.length > 0 ? `${operationId}Params` : undefined,
433
- bodyType,
434
- responseType,
435
- contentType: requestBodyContent?.contentType,
436
- responseContentType: responseContent?.contentType,
437
- hasPathParams: pathParams.length > 0,
438
- hasQueryParams: queryParams.length > 0,
439
- pathParams,
440
- queryParams,
441
- };
442
- }
443
-
444
- export function generatedFileHeader() {
445
- return `/* eslint-disable */
446
- /* tslint:disable */
447
- /*
448
- * --------------------------------------------------------------------------------
449
- * ## THIS FILE WAS GENERATED BY typedapi-client-helpers ##
450
- * ## ##
451
- * ## AUTHOR: Stef Buurman ##
452
- * ## SOURCE: https://github.com/Stef-Buurman/TypedApi/tree/main/npm/typed-api-client ##
453
- * --------------------------------------------------------------------------------
454
- */`;
455
- }
1
+ import {
2
+ dereference,
3
+ getErrorResponses,
4
+ getParameterSchema,
5
+ getRequestBodyContent,
6
+ getResponseContent,
7
+ getSuccessResponse,
8
+ } from "./openapi-utils.mjs";
9
+ import {
10
+ operationTypeName,
11
+ pascalCase,
12
+ propertyKey,
13
+ refName,
14
+ sanitizeIdentifier,
15
+ typePropertyName,
16
+ uniqueName,
17
+ } from "./names.mjs";
18
+
19
+ function jsDocFromSchema(schema, indent = "") {
20
+ const lines = [];
21
+ if (schema?.description)
22
+ lines.push(...String(schema.description).split(/\r?\n/));
23
+ if (schema?.format) lines.push(`@format ${schema.format}`);
24
+ if (Object.prototype.hasOwnProperty.call(schema ?? {}, "default")) {
25
+ lines.push(`@default ${JSON.stringify(schema.default)}`);
26
+ }
27
+ if (schema?.readOnly) lines.push("@readonly");
28
+ if (schema?.writeOnly) lines.push("@writeOnly");
29
+ if (schema?.deprecated) lines.push("@deprecated");
30
+ if (lines.length === 0) return "";
31
+ if (lines.length === 1) return `${indent}/** ${lines[0]} */\n`;
32
+ return `${indent}/**\n${lines.map((line) => `${indent} * ${line}`).join("\n")}\n${indent} */\n`;
33
+ }
34
+
35
+ function unionTypes(types, fallback = "unknown") {
36
+ const unique = [...new Set(types.filter(Boolean))];
37
+ return unique.length > 0 ? unique.join(" | ") : fallback;
38
+ }
39
+
40
+ function intersectionTypes(types, fallback = "Record<string, unknown>") {
41
+ const unique = [...new Set(types.filter(Boolean))];
42
+ return unique.length > 0 ? unique.join(" & ") : fallback;
43
+ }
44
+
45
+ function hasObjectShape(schema) {
46
+ return Boolean(
47
+ schema?.properties ||
48
+ schema?.additionalProperties ||
49
+ schema?.type === "object",
50
+ );
51
+ }
52
+
53
+ function schemaWithoutComposition(schema) {
54
+ const { allOf, oneOf, anyOf, nullable, ...local } = schema ?? {};
55
+ return local;
56
+ }
57
+
58
+ function assertUniqueTypePropertyNames(entries, contextName) {
59
+ const seen = new Map();
60
+ for (const rawName of entries) {
61
+ const localName = typePropertyName(rawName);
62
+ const existing = seen.get(localName);
63
+ if (existing !== undefined && existing !== rawName) {
64
+ throw new Error(
65
+ `OpenAPI members ${JSON.stringify(existing)} and ${JSON.stringify(rawName)} in ${contextName} ` +
66
+ `both become TypeScript property ${JSON.stringify(localName)} when only the first character is lowercased.`,
67
+ );
68
+ }
69
+ seen.set(localName, rawName);
70
+ }
71
+ }
72
+
73
+ function escapeJsonPointerPart(value) {
74
+ return String(value).replace(/~/g, "~0").replace(/\//g, "~1");
75
+ }
76
+
77
+ function componentWireSchemaKey(rawName) {
78
+ return `#/components/schemas/${escapeJsonPointerPart(rawName)}`;
79
+ }
80
+
81
+ function operationWireSchemaKey(operationId, kind) {
82
+ return `operation:${operationId}:${kind}`;
83
+ }
84
+
85
+ function wireSchemaDescriptor(openApi, schemaOrRef) {
86
+ if (!schemaOrRef) return { kind: "identity" };
87
+ if (schemaOrRef.$ref) return { kind: "ref", ref: String(schemaOrRef.$ref) };
88
+
89
+ const schema =
90
+ dereference(openApi, schemaOrRef, "Wire schema reference") ?? schemaOrRef;
91
+
92
+ if (schema.allOf) {
93
+ const schemas = schema.allOf.map((item) =>
94
+ wireSchemaDescriptor(openApi, item),
95
+ );
96
+ const localSchema = schemaWithoutComposition(schema);
97
+ if (hasObjectShape(localSchema))
98
+ schemas.push(wireSchemaDescriptor(openApi, localSchema));
99
+ return { kind: "allOf", schemas };
100
+ }
101
+
102
+ if (schema.oneOf || schema.anyOf) {
103
+ return {
104
+ kind: "union",
105
+ schemas: (schema.oneOf ?? schema.anyOf).map((item) =>
106
+ wireSchemaDescriptor(openApi, item),
107
+ ),
108
+ };
109
+ }
110
+
111
+ const schemaType = Array.isArray(schema.type)
112
+ ? schema.type.find((item) => item !== "null")
113
+ : schema.type;
114
+
115
+ if (schemaType === "array") {
116
+ return {
117
+ kind: "array",
118
+ items: wireSchemaDescriptor(openApi, schema.items ?? {}),
119
+ };
120
+ }
121
+
122
+ if (
123
+ schemaType === "object" ||
124
+ schema.properties ||
125
+ schema.additionalProperties
126
+ ) {
127
+ const rawPropertyNames = Object.keys(schema.properties ?? {});
128
+ assertUniqueTypePropertyNames(rawPropertyNames, "an OpenAPI object schema");
129
+ const properties = {};
130
+
131
+ for (const [wireName, propertySchema] of Object.entries(
132
+ schema.properties ?? {},
133
+ )) {
134
+ const localName = typePropertyName(wireName);
135
+ const nested = wireSchemaDescriptor(openApi, propertySchema);
136
+ properties[localName] = {
137
+ wireName,
138
+ ...(nested.kind === "identity" ? {} : { schema: nested }),
139
+ };
140
+ }
141
+
142
+ let additionalProperties;
143
+ if (schema.additionalProperties === true) additionalProperties = true;
144
+ else if (schema.additionalProperties) {
145
+ additionalProperties = wireSchemaDescriptor(
146
+ openApi,
147
+ schema.additionalProperties,
148
+ );
149
+ }
150
+
151
+ return {
152
+ kind: "object",
153
+ ...(Object.keys(properties).length > 0 ? { properties } : {}),
154
+ ...(additionalProperties !== undefined ? { additionalProperties } : {}),
155
+ };
156
+ }
157
+
158
+ return { kind: "identity" };
159
+ }
160
+
161
+ function generateWireSchemaRegistry(openApi, operations, options = {}) {
162
+ const registry = {};
163
+
164
+ for (const [rawName, schema] of Object.entries(
165
+ openApi.components?.schemas ?? {},
166
+ )) {
167
+ registry[componentWireSchemaKey(rawName)] = wireSchemaDescriptor(
168
+ openApi,
169
+ schema,
170
+ );
171
+ }
172
+
173
+ for (const operationInfo of operations) {
174
+ const operationId = operationInfo.operationId;
175
+ const requestBody = getRequestBodyContent(
176
+ openApi,
177
+ operationInfo.operation.requestBody,
178
+ );
179
+ const requestSchema = requestBody?.mediaType?.schema;
180
+ if (requestSchema) {
181
+ registry[operationWireSchemaKey(operationId, "body")] =
182
+ wireSchemaDescriptor(openApi, requestSchema);
183
+ }
184
+
185
+ const successResponse = getSuccessResponse(
186
+ openApi,
187
+ operationInfo.operation,
188
+ options.defaultResponseAsSuccess,
189
+ );
190
+ const responseSchema = getResponseContent(successResponse?.response)
191
+ ?.mediaType?.schema;
192
+ if (responseSchema) {
193
+ registry[operationWireSchemaKey(operationId, "response")] =
194
+ wireSchemaDescriptor(openApi, responseSchema);
195
+ }
196
+
197
+ const errorSchemas = getErrorResponses(openApi, operationInfo.operation)
198
+ .map(({ response }) => getResponseContent(response)?.mediaType?.schema)
199
+ .filter(Boolean)
200
+ .map((schema) => wireSchemaDescriptor(openApi, schema));
201
+ if (errorSchemas.length > 0) {
202
+ registry[operationWireSchemaKey(operationId, "error")] =
203
+ errorSchemas.length === 1
204
+ ? errorSchemas[0]
205
+ : { kind: "union", schemas: errorSchemas };
206
+ }
207
+ }
208
+
209
+ return registry;
210
+ }
211
+
212
+ export class TypeResolver {
213
+ constructor(openApi, options = {}) {
214
+ this.openApi = openApi;
215
+ this.options = options;
216
+ this.inlineTypes = new Map();
217
+ this.componentNames = new Map();
218
+ const usedNames = new Set();
219
+
220
+ for (const rawName of Object.keys(openApi.components?.schemas ?? {})) {
221
+ const name = uniqueName(operationTypeName(rawName), usedNames);
222
+ this.componentNames.set(rawName, name);
223
+ }
224
+ }
225
+
226
+ componentName(rawName) {
227
+ return this.componentNames.get(rawName) ?? operationTypeName(rawName);
228
+ }
229
+
230
+ resolve(schemaOrRef, contextName = "Anonymous", options = {}) {
231
+ if (!schemaOrRef) return "unknown";
232
+
233
+ if (schemaOrRef.$ref) {
234
+ dereference(this.openApi, schemaOrRef, "Schema reference");
235
+ return this.componentName(refName(schemaOrRef.$ref));
236
+ }
237
+
238
+ const schema =
239
+ dereference(this.openApi, schemaOrRef, "Schema reference") ?? schemaOrRef;
240
+ const nullable =
241
+ schema.nullable ||
242
+ (Array.isArray(schema.type) && schema.type.includes("null"));
243
+ let type = this.resolveNonNullable(schema, contextName, options);
244
+ if (nullable && !/(^|\W)null(\W|$)/.test(type)) type = `${type} | null`;
245
+ return type;
246
+ }
247
+
248
+ resolveNonNullable(schema, contextName, options = {}) {
249
+ if (schema.const !== undefined) return JSON.stringify(schema.const);
250
+
251
+ if (Array.isArray(schema.enum)) {
252
+ if (options.inlineEnumAsUnion) {
253
+ return unionTypes(
254
+ this.enumLiteralValues(schema).map((value) => JSON.stringify(value)),
255
+ );
256
+ }
257
+ return operationTypeName(contextName);
258
+ }
259
+
260
+ if (schema.oneOf || schema.anyOf) {
261
+ const variants = schema.oneOf ?? schema.anyOf;
262
+ return unionTypes(
263
+ variants.map((item, index) =>
264
+ this.resolve(item, `${contextName}${index + 1}`, options),
265
+ ),
266
+ );
267
+ }
268
+
269
+ if (schema.allOf) {
270
+ const parts = schema.allOf.map((item, index) =>
271
+ this.resolve(item, `${contextName}${index + 1}`, options),
272
+ );
273
+ const localSchema = schemaWithoutComposition(schema);
274
+ if (hasObjectShape(localSchema)) {
275
+ parts.push(
276
+ this.createInlineObjectType(
277
+ localSchema,
278
+ `${contextName}Own`,
279
+ options,
280
+ ),
281
+ );
282
+ }
283
+ return intersectionTypes(parts);
284
+ }
285
+
286
+ const schemaType = Array.isArray(schema.type)
287
+ ? schema.type.find((item) => item !== "null")
288
+ : schema.type;
289
+
290
+ switch (schemaType) {
291
+ case "integer":
292
+ case "number":
293
+ return "number";
294
+ case "boolean":
295
+ return "boolean";
296
+ case "string":
297
+ if (schema.format === "binary")
298
+ return options.binaryAsBlob ? "Blob" : "File";
299
+ return "string";
300
+ case "array": {
301
+ const itemType = this.resolve(
302
+ schema.items ?? {},
303
+ `${contextName}Item`,
304
+ options,
305
+ );
306
+ return /^[A-Za-z_$][\w$]*(?:<.*>)?$/.test(itemType) ||
307
+ itemType.endsWith("[]")
308
+ ? `${itemType}[]`
309
+ : `(${itemType})[]`;
310
+ }
311
+ case "object":
312
+ default:
313
+ if (schema.properties)
314
+ return this.createInlineObjectType(schema, contextName, options);
315
+ if (schema.additionalProperties) {
316
+ const valueType =
317
+ schema.additionalProperties === true
318
+ ? "unknown"
319
+ : this.resolve(
320
+ schema.additionalProperties,
321
+ `${contextName}Value`,
322
+ options,
323
+ );
324
+ return `Record<string, ${valueType}>`;
325
+ }
326
+ return schemaType === "object" ? "Record<string, unknown>" : "unknown";
327
+ }
328
+ }
329
+
330
+ createInlineObjectType(schema, contextName) {
331
+ const base = operationTypeName(contextName);
332
+ let name = base;
333
+ let counter = 2;
334
+ while (
335
+ this.componentNamesHasValue(name) ||
336
+ (this.inlineTypes.has(name) && this.inlineTypes.get(name) !== schema)
337
+ ) {
338
+ name = `${base}${counter++}`;
339
+ }
340
+ if (!this.inlineTypes.has(name)) this.inlineTypes.set(name, schema);
341
+ return name;
342
+ }
343
+
344
+ componentNamesHasValue(value) {
345
+ return [...this.componentNames.values()].includes(value);
346
+ }
347
+
348
+ objectMembers(name, schema) {
349
+ const required = new Set(schema.required ?? []);
350
+ const lines = [];
351
+
352
+ const propertyEntries = Object.entries(schema.properties ?? {});
353
+ assertUniqueTypePropertyNames(
354
+ propertyEntries.map(([propertyName]) => propertyName),
355
+ name,
356
+ );
357
+
358
+ for (const [propertyName, propertySchema] of propertyEntries) {
359
+ const localPropertyName = typePropertyName(propertyName);
360
+ const optionalToken = required.has(propertyName) ? "" : "?";
361
+ const propertyType = this.resolve(
362
+ propertySchema,
363
+ `${name}${pascalCase(propertyName)}`,
364
+ {
365
+ inlineEnumAsUnion: true,
366
+ },
367
+ );
368
+ const resolvedSchema = dereference(
369
+ this.openApi,
370
+ propertySchema,
371
+ "Property schema reference",
372
+ );
373
+ lines.push(
374
+ jsDocFromSchema(resolvedSchema, " ") +
375
+ ` ${propertyKey(localPropertyName)}${optionalToken}: ${propertyType};`,
376
+ );
377
+ }
378
+
379
+ if (schema.additionalProperties) {
380
+ const valueType =
381
+ schema.additionalProperties === true
382
+ ? "unknown"
383
+ : this.resolve(schema.additionalProperties, `${name}Value`, {
384
+ inlineEnumAsUnion: true,
385
+ });
386
+ lines.push(` [key: string]: ${valueType};`);
387
+ }
388
+
389
+ return lines;
390
+ }
391
+
392
+ createInterface(name, schema) {
393
+ return [
394
+ `export interface ${name} {`,
395
+ ...this.objectMembers(name, schema),
396
+ "}",
397
+ ].join("\n");
398
+ }
399
+
400
+ createObjectTypeLiteral(name, schema) {
401
+ return ["{", ...this.objectMembers(name, schema), "}"].join("\n");
402
+ }
403
+
404
+ createDeclaration(name, schema) {
405
+ if (Array.isArray(schema.enum)) return this.createEnum(name, schema);
406
+
407
+ if (schema.allOf) {
408
+ const parts = schema.allOf.map((item, index) =>
409
+ this.resolve(item, `${name}${index + 1}`, { inlineEnumAsUnion: true }),
410
+ );
411
+ const localSchema = schemaWithoutComposition(schema);
412
+ if (hasObjectShape(localSchema))
413
+ parts.push(this.createObjectTypeLiteral(`${name}Own`, localSchema));
414
+ return `export type ${name} = ${intersectionTypes(parts)};`;
415
+ }
416
+
417
+ if (hasObjectShape(schema)) return this.createInterface(name, schema);
418
+ return `export type ${name} = ${this.resolve(schema, name, { inlineEnumAsUnion: true })};`;
419
+ }
420
+
421
+ createEnum(name, schema) {
422
+ if (this.options.generateUnionEnums) {
423
+ return `export type ${name} = ${unionTypes(
424
+ this.enumLiteralValues(schema).map((value) => JSON.stringify(value)),
425
+ )};`;
426
+ }
427
+
428
+ const lines = [`export const ${name} = {`];
429
+ for (const [index, value] of (schema.enum ?? []).entries()) {
430
+ const memberName = this.enumMemberName(schema, value, index);
431
+ const enumValue = this.enumRuntimeValue(schema, value, index);
432
+ lines.push(` ${propertyKey(memberName)}: ${JSON.stringify(enumValue)},`);
433
+ }
434
+ lines.push("} as const;");
435
+ lines.push(`export type ${name} = (typeof ${name})[keyof typeof ${name}];`);
436
+ return lines.join("\n");
437
+ }
438
+
439
+ enumMetadataNames(schema) {
440
+ const names =
441
+ schema?.["x-enumNames"] ??
442
+ schema?.["x-enum-varnames"] ??
443
+ schema?.["x-enumVarnames"];
444
+ return Array.isArray(names) ? names : [];
445
+ }
446
+
447
+ enumLiteralValues(schema) {
448
+ const values = schema.enum ?? [];
449
+ const names = this.enumMetadataNames(schema);
450
+ return this.options.enumNamesAsValues && names.length > 0
451
+ ? values.map((value, index) => names[index] ?? value)
452
+ : values;
453
+ }
454
+
455
+ enumRuntimeValue(schema, value, index) {
456
+ const names = this.enumMetadataNames(schema);
457
+ return this.options.enumNamesAsValues && names.length > 0
458
+ ? (names[index] ?? value)
459
+ : value;
460
+ }
461
+
462
+ enumMemberName(schema, value, index) {
463
+ const rawName = this.enumMetadataNames(schema)[index] ?? value;
464
+ return operationTypeName(String(rawName) || "Value");
465
+ }
466
+ }
467
+
468
+ function parameterTypeName(operationId, location) {
469
+ return `${operationId}${pascalCase(location)}Params`;
470
+ }
471
+
472
+ function combinedParameterTypeName(operationId) {
473
+ return `${operationId}Params`;
474
+ }
475
+
476
+ function createParamsInterface(openApi, resolver, name, parameters) {
477
+ const lines = [`export interface ${name} {`];
478
+ assertUniqueTypePropertyNames(
479
+ parameters.map((parameter) => parameter.name),
480
+ name,
481
+ );
482
+ for (const parameter of parameters) {
483
+ const parameterSchema = getParameterSchema(openApi, parameter);
484
+ const optionalToken =
485
+ parameter.required || parameter.in === "path" ? "" : "?";
486
+ const parameterType = resolver.resolve(
487
+ parameterSchema,
488
+ `${name}${pascalCase(parameter.name)}`,
489
+ {
490
+ inlineEnumAsUnion: true,
491
+ },
492
+ );
493
+ lines.push(
494
+ jsDocFromSchema(parameterSchema, " ") +
495
+ ` ${propertyKey(typePropertyName(parameter.name))}${optionalToken}: ${parameterType};`,
496
+ );
497
+ }
498
+ lines.push("}");
499
+ return lines.join("\n");
500
+ }
501
+
502
+ function resolveResponseType(
503
+ resolver,
504
+ response,
505
+ contextName,
506
+ binaryAsBlob = false,
507
+ ) {
508
+ const content = getResponseContent(response);
509
+ const schema = content?.mediaType?.schema;
510
+ if (!schema) return undefined;
511
+ return resolver.resolve(schema, contextName, {
512
+ binaryAsBlob:
513
+ binaryAsBlob || content?.contentType === "application/octet-stream",
514
+ inlineEnumAsUnion: true,
515
+ });
516
+ }
517
+
518
+ export function getOperationTypes(
519
+ openApi,
520
+ operationInfo,
521
+ options = {},
522
+ resolver = new TypeResolver(openApi, options),
523
+ ) {
524
+ const operationId = operationInfo.operationId;
525
+ const operationTypeNameBase = operationInfo.typeName ?? operationId;
526
+ const parameters = operationInfo.parameters ?? [];
527
+ const parametersByLocation = Object.fromEntries(
528
+ ["path", "query", "header", "cookie"].map((location) => [
529
+ location,
530
+ parameters.filter((parameter) => parameter.in === location),
531
+ ]),
532
+ );
533
+
534
+ const requestBodyContent = getRequestBodyContent(
535
+ openApi,
536
+ operationInfo.operation.requestBody,
537
+ );
538
+ const requestBodySchema = requestBodyContent?.mediaType?.schema;
539
+ let bodyType;
540
+ if (requestBodySchema) {
541
+ if (requestBodySchema.$ref) {
542
+ bodyType = resolver.resolve(requestBodySchema, `${operationTypeNameBase}Payload`);
543
+ } else if (
544
+ requestBodyContent?.contentType === "multipart/form-data" ||
545
+ hasObjectShape(requestBodySchema)
546
+ ) {
547
+ bodyType = `${operationTypeNameBase}Payload`;
548
+ } else {
549
+ bodyType = resolver.resolve(requestBodySchema, `${operationTypeNameBase}Payload`, {
550
+ inlineEnumAsUnion: true,
551
+ });
552
+ }
553
+ }
554
+
555
+ const successResponse = getSuccessResponse(
556
+ openApi,
557
+ operationInfo.operation,
558
+ options.defaultResponseAsSuccess,
559
+ );
560
+ const responseContent = getResponseContent(successResponse?.response);
561
+ const responseType = successResponse
562
+ ? (resolveResponseType(
563
+ resolver,
564
+ successResponse.response,
565
+ `${operationTypeNameBase}Response`,
566
+ ) ?? "void")
567
+ : "void";
568
+
569
+ const errorResponses = getErrorResponses(openApi, operationInfo.operation);
570
+ const errorTypes = errorResponses
571
+ .map(({ statusCode, response }) =>
572
+ resolveResponseType(
573
+ resolver,
574
+ response,
575
+ `${operationTypeNameBase}Error${statusCode}`,
576
+ ),
577
+ )
578
+ .filter(Boolean);
579
+ const usesHttpErrorFallback = errorTypes.length === 0;
580
+ const errorType = unionTypes(errorTypes, "ApiHttpError");
581
+ const responseSchema = responseContent?.mediaType?.schema;
582
+ const errorSchemas = errorResponses
583
+ .map(({ response }) => getResponseContent(response)?.mediaType?.schema)
584
+ .filter(Boolean);
585
+
586
+ const hasNonBodyInput = parameters.length > 0;
587
+ const paginated =
588
+ Boolean(operationInfo.operation["x-typedapi-pagination"]) ||
589
+ /(?:Api)?Pagination(?:Sort)?Response\b|Paged(?:Response|Result)?\b|Paginated(?:Response|Result)?\b/.test(
590
+ responseType,
591
+ );
592
+ const isPurePaginatedQuery =
593
+ paginated &&
594
+ parametersByLocation.query.length > 0 &&
595
+ !parametersByLocation.path.length &&
596
+ !parametersByLocation.header.length &&
597
+ !parametersByLocation.cookie.length &&
598
+ !bodyType;
599
+ const parameterTypeNames = isPurePaginatedQuery
600
+ ? { query: parameterTypeName(operationTypeNameBase, "query") }
601
+ : {};
602
+
603
+ return {
604
+ operationId,
605
+ operationTypeNameBase,
606
+ originalOperationId: operationInfo.originalOperationId,
607
+ methodName: operationInfo.methodName,
608
+ methodNameSource: operationInfo.methodNameSource,
609
+ paramsTypeName:
610
+ hasNonBodyInput && !isPurePaginatedQuery
611
+ ? combinedParameterTypeName(operationTypeNameBase)
612
+ : undefined,
613
+ parameterTypeNames,
614
+ parameters,
615
+ parametersByLocation,
616
+ allParametersOptional: parameters.every(
617
+ (parameter) => parameter.in !== "path" && !parameter.required,
618
+ ),
619
+ bodyType,
620
+ bodyRequired: Boolean(requestBodyContent?.requestBody?.required),
621
+ bodyWireSchemaKey: requestBodySchema
622
+ ? operationWireSchemaKey(operationId, "body")
623
+ : undefined,
624
+ responseType,
625
+ responseWireSchemaKey: responseSchema
626
+ ? operationWireSchemaKey(operationId, "response")
627
+ : undefined,
628
+ errorType,
629
+ usesHttpErrorFallback,
630
+ errorWireSchemaKey:
631
+ errorSchemas.length > 0
632
+ ? operationWireSchemaKey(operationId, "error")
633
+ : undefined,
634
+ contentType: requestBodyContent?.contentType,
635
+ responseContentType: responseContent?.contentType,
636
+ hasPathParams: parametersByLocation.path.length > 0,
637
+ hasQueryParams: parametersByLocation.query.length > 0,
638
+ hasHeaderParams: parametersByLocation.header.length > 0,
639
+ hasCookieParams: parametersByLocation.cookie.length > 0,
640
+ paginationMetadata: operationInfo.operation["x-typedapi-pagination"],
641
+ };
642
+ }
643
+
644
+ export function generateDataContracts(openApi, operations, options = {}) {
645
+ const resolver = new TypeResolver(openApi, options);
646
+ const declarations = [];
647
+ const schemas = openApi.components?.schemas ?? {};
648
+
649
+ const entries = Object.entries(schemas).sort(([a], [b]) =>
650
+ a.localeCompare(b),
651
+ );
652
+ for (const [rawName, schemaOrRef] of entries) {
653
+ const schema = dereference(
654
+ openApi,
655
+ schemaOrRef,
656
+ "Component schema reference",
657
+ );
658
+ declarations.push(
659
+ resolver.createDeclaration(resolver.componentName(rawName), schema),
660
+ );
661
+ }
662
+
663
+ for (const operationInfo of operations) {
664
+ const operationTypes = getOperationTypes(
665
+ openApi,
666
+ operationInfo,
667
+ options,
668
+ resolver,
669
+ );
670
+
671
+ if (operationTypes.paramsTypeName) {
672
+ declarations.push(
673
+ createParamsInterface(
674
+ openApi,
675
+ resolver,
676
+ operationTypes.paramsTypeName,
677
+ operationTypes.parameters,
678
+ ),
679
+ );
680
+ }
681
+
682
+ for (const [location, parameters] of Object.entries(
683
+ operationTypes.parametersByLocation,
684
+ )) {
685
+ const typeName = operationTypes.parameterTypeNames[location];
686
+ if (typeName)
687
+ declarations.push(
688
+ createParamsInterface(openApi, resolver, typeName, parameters),
689
+ );
690
+ }
691
+
692
+ const bodyContent = getRequestBodyContent(
693
+ openApi,
694
+ operationInfo.operation.requestBody,
695
+ );
696
+ const bodySchema = bodyContent?.mediaType?.schema;
697
+ if (
698
+ bodySchema &&
699
+ !bodySchema.$ref &&
700
+ (bodyContent.contentType === "multipart/form-data" ||
701
+ hasObjectShape(bodySchema))
702
+ ) {
703
+ declarations.push(
704
+ resolver.createDeclaration(
705
+ `${operationTypes.operationTypeNameBase}Payload`,
706
+ bodySchema,
707
+ ),
708
+ );
709
+ }
710
+ }
711
+
712
+ for (const [name, schema] of resolver.inlineTypes.entries()) {
713
+ if (!resolver.componentNamesHasValue(name))
714
+ declarations.push(resolver.createDeclaration(name, schema));
715
+ }
716
+
717
+ const wireSchemas = generateWireSchemaRegistry(openApi, operations, options);
718
+ declarations.push(
719
+ `export const typedApiWireSchemas = ${JSON.stringify(wireSchemas, null, 2)} as const;`,
720
+ );
721
+
722
+ return `${generatedFileHeader()}\n\n${declarations.join("\n\n")}\n`;
723
+ }
724
+
725
+ export function generatedFileHeader() {
726
+ return `/* eslint-disable */
727
+ /* tslint:disable */
728
+ /*
729
+ * -------------------------------------------------------------------------------------
730
+ * ## THIS FILE WAS GENERATED BY typedapi-client-helpers ##
731
+ * ## DO NOT EDIT THIS FILE DIRECTLY ##
732
+ * ## ##
733
+ * ## AUTHOR: Stef Buurman ##
734
+ * ## SOURCE: https://github.com/Stef-Buurman/TypedApi/tree/main/npm/typed-api-client ##
735
+ * -------------------------------------------------------------------------------------
736
+ */`;
737
+ }