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.
- package/CHANGELOG.md +68 -0
- package/README.md +221 -220
- package/dist/cjs/httpClient.js +258 -0
- package/dist/cjs/httpClient.js.map +1 -0
- package/dist/cjs/index.js +32 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/interfaces/ApiResult.js +3 -0
- package/dist/cjs/interfaces/ApiResult.js.map +1 -0
- package/dist/cjs/interfaces/Filter.js +3 -0
- package/dist/cjs/interfaces/Filter.js.map +1 -0
- package/dist/cjs/interfaces/SortType.js +3 -0
- package/dist/cjs/interfaces/SortType.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types/ApiCallOptions.js +3 -0
- package/dist/cjs/types/ApiCallOptions.js.map +1 -0
- package/dist/cjs/types/ApiMethodArguments.js +3 -0
- package/dist/cjs/types/ApiMethodArguments.js.map +1 -0
- package/dist/cjs/types/ApiMethodOptions.js +3 -0
- package/dist/cjs/types/ApiMethodOptions.js.map +1 -0
- package/dist/cjs/types/ApiMethodTypes.js +3 -0
- package/dist/cjs/types/ApiMethodTypes.js.map +1 -0
- package/dist/cjs/types/HttpResponse.js +3 -0
- package/dist/cjs/types/HttpResponse.js.map +1 -0
- package/dist/cjs/types/httpClientTypes.js +3 -0
- package/dist/cjs/types/httpClientTypes.js.map +1 -0
- package/dist/cjs/utils/SortTypeFromSortDirection.js +43 -0
- package/dist/cjs/utils/SortTypeFromSortDirection.js.map +1 -0
- package/dist/cjs/utils/buildQuery.js +96 -0
- package/dist/cjs/utils/buildQuery.js.map +1 -0
- package/dist/cjs/utils/extractArgsCallbacksAndParams.js +51 -0
- package/dist/cjs/utils/extractArgsCallbacksAndParams.js.map +1 -0
- package/dist/cjs/utils/extractArgsToastsAndParams.js +49 -0
- package/dist/cjs/utils/extractArgsToastsAndParams.js.map +1 -0
- package/dist/cjs/utils/handleApiResponse.js +124 -0
- package/dist/cjs/utils/handleApiResponse.js.map +1 -0
- package/dist/cjs/utils/toFormData.js +58 -0
- package/dist/cjs/utils/toFormData.js.map +1 -0
- package/dist/cjs/utils/wireMapping.js +123 -0
- package/dist/cjs/utils/wireMapping.js.map +1 -0
- package/dist/esm/httpClient.js +251 -0
- package/dist/esm/httpClient.js.map +1 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/interfaces/ApiResult.js +2 -0
- package/dist/esm/interfaces/ApiResult.js.map +1 -0
- package/dist/esm/interfaces/Filter.js +2 -0
- package/dist/esm/interfaces/Filter.js.map +1 -0
- package/dist/esm/interfaces/SortType.js +2 -0
- package/dist/esm/interfaces/SortType.js.map +1 -0
- package/dist/esm/types/ApiCallOptions.js +2 -0
- package/dist/esm/types/ApiCallOptions.js.map +1 -0
- package/dist/esm/types/ApiMethodArguments.js +2 -0
- package/dist/esm/types/ApiMethodArguments.js.map +1 -0
- package/dist/esm/types/ApiMethodOptions.js +2 -0
- package/dist/esm/types/ApiMethodOptions.js.map +1 -0
- package/dist/esm/types/ApiMethodTypes.js +2 -0
- package/dist/esm/types/ApiMethodTypes.js.map +1 -0
- package/dist/esm/types/HttpResponse.js +2 -0
- package/dist/esm/types/HttpResponse.js.map +1 -0
- package/dist/esm/types/httpClientTypes.js +2 -0
- package/dist/esm/types/httpClientTypes.js.map +1 -0
- package/dist/esm/utils/SortTypeFromSortDirection.js +38 -0
- package/dist/esm/utils/SortTypeFromSortDirection.js.map +1 -0
- package/dist/esm/utils/buildQuery.js +93 -0
- package/dist/esm/utils/buildQuery.js.map +1 -0
- package/dist/esm/utils/extractArgsCallbacksAndParams.js +48 -0
- package/dist/esm/utils/extractArgsCallbacksAndParams.js.map +1 -0
- package/dist/esm/utils/extractArgsToastsAndParams.js +46 -0
- package/dist/esm/utils/extractArgsToastsAndParams.js.map +1 -0
- package/dist/esm/utils/handleApiResponse.js +121 -0
- package/dist/esm/utils/handleApiResponse.js.map +1 -0
- package/dist/esm/utils/toFormData.js +55 -0
- package/dist/esm/utils/toFormData.js.map +1 -0
- package/dist/esm/utils/wireMapping.js +119 -0
- package/dist/esm/utils/wireMapping.js.map +1 -0
- package/dist/types/httpClient.d.ts +63 -0
- package/dist/types/httpClient.d.ts.map +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/interfaces/ApiResult.d.ts +32 -0
- package/dist/types/interfaces/ApiResult.d.ts.map +1 -0
- package/dist/types/interfaces/Filter.d.ts +31 -0
- package/dist/types/interfaces/Filter.d.ts.map +1 -0
- package/dist/types/interfaces/SortType.d.ts +7 -0
- package/dist/types/interfaces/SortType.d.ts.map +1 -0
- package/dist/types/types/ApiCallOptions.d.ts +26 -0
- package/dist/types/types/ApiCallOptions.d.ts.map +1 -0
- package/dist/types/types/ApiMethodArguments.d.ts +6 -0
- package/dist/types/types/ApiMethodArguments.d.ts.map +1 -0
- package/dist/types/types/ApiMethodOptions.d.ts +8 -0
- package/dist/types/types/ApiMethodOptions.d.ts.map +1 -0
- package/dist/types/types/ApiMethodTypes.d.ts +47 -0
- package/dist/types/types/ApiMethodTypes.d.ts.map +1 -0
- package/dist/types/types/HttpResponse.d.ts +2 -0
- package/dist/types/types/HttpResponse.d.ts.map +1 -0
- package/dist/types/types/httpClientTypes.d.ts +2 -0
- package/dist/types/types/httpClientTypes.d.ts.map +1 -0
- package/dist/types/utils/SortTypeFromSortDirection.d.ts +13 -0
- package/dist/types/utils/SortTypeFromSortDirection.d.ts.map +1 -0
- package/dist/types/utils/buildQuery.d.ts +16 -0
- package/dist/types/utils/buildQuery.d.ts.map +1 -0
- package/dist/types/utils/extractArgsCallbacksAndParams.d.ts +22 -0
- package/dist/types/utils/extractArgsCallbacksAndParams.d.ts.map +1 -0
- package/dist/types/utils/extractArgsToastsAndParams.d.ts +20 -0
- package/dist/types/utils/extractArgsToastsAndParams.d.ts.map +1 -0
- package/dist/types/utils/handleApiResponse.d.ts +11 -0
- package/dist/types/utils/handleApiResponse.d.ts.map +1 -0
- package/dist/types/utils/toFormData.d.ts +10 -0
- package/dist/types/utils/toFormData.d.ts.map +1 -0
- package/dist/types/utils/wireMapping.d.ts +30 -0
- package/dist/types/utils/wireMapping.d.ts.map +1 -0
- package/package.json +90 -84
- package/scripts/build.mjs +74 -0
- package/scripts/generate-api-minimal.mjs +568 -476
- package/scripts/openapi-generator/controller-template.mjs +1 -1
- package/scripts/openapi-generator/index.mjs +357 -113
- package/scripts/openapi-generator/method-template.mjs +201 -244
- package/scripts/openapi-generator/names.mjs +37 -86
- package/scripts/openapi-generator/openapi-utils.mjs +411 -147
- package/scripts/openapi-generator/schema.mjs +518 -238
- package/dist/index.cjs +0 -536
- package/dist/index.d.cts +0 -285
- package/dist/index.d.ts +0 -285
- package/dist/index.js +0 -495
|
@@ -1,44 +1,212 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dereference,
|
|
3
|
+
getErrorResponses,
|
|
4
|
+
getParameterSchema,
|
|
3
5
|
getRequestBodyContent,
|
|
4
6
|
getResponseContent,
|
|
5
7
|
getSuccessResponse,
|
|
6
8
|
} from "./openapi-utils.mjs";
|
|
7
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
operationTypeName,
|
|
11
|
+
pascalCase,
|
|
12
|
+
propertyKey,
|
|
13
|
+
refName,
|
|
14
|
+
sanitizeIdentifier,
|
|
15
|
+
typePropertyName,
|
|
16
|
+
uniqueName,
|
|
17
|
+
} from "./names.mjs";
|
|
8
18
|
|
|
9
19
|
function jsDocFromSchema(schema, indent = "") {
|
|
10
20
|
const lines = [];
|
|
11
|
-
|
|
12
|
-
if (schema?.description) {
|
|
21
|
+
if (schema?.description)
|
|
13
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)}`);
|
|
14
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
|
+
}
|
|
15
57
|
|
|
16
|
-
|
|
17
|
-
|
|
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);
|
|
18
70
|
}
|
|
71
|
+
}
|
|
19
72
|
|
|
20
|
-
|
|
21
|
-
|
|
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 };
|
|
22
100
|
}
|
|
23
101
|
|
|
24
|
-
if (schema
|
|
25
|
-
|
|
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
|
+
};
|
|
26
109
|
}
|
|
27
110
|
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
};
|
|
30
120
|
}
|
|
31
121
|
|
|
32
|
-
if (
|
|
33
|
-
|
|
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
|
+
};
|
|
34
156
|
}
|
|
35
157
|
|
|
36
|
-
return
|
|
158
|
+
return { kind: "identity" };
|
|
37
159
|
}
|
|
38
160
|
|
|
39
|
-
function
|
|
40
|
-
const
|
|
41
|
-
|
|
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;
|
|
42
210
|
}
|
|
43
211
|
|
|
44
212
|
export class TypeResolver {
|
|
@@ -46,34 +214,39 @@ export class TypeResolver {
|
|
|
46
214
|
this.openApi = openApi;
|
|
47
215
|
this.options = options;
|
|
48
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);
|
|
49
228
|
}
|
|
50
229
|
|
|
51
230
|
resolve(schemaOrRef, contextName = "Anonymous", options = {}) {
|
|
52
|
-
if (!schemaOrRef)
|
|
53
|
-
return "any";
|
|
54
|
-
}
|
|
231
|
+
if (!schemaOrRef) return "unknown";
|
|
55
232
|
|
|
56
233
|
if (schemaOrRef.$ref) {
|
|
57
|
-
|
|
234
|
+
dereference(this.openApi, schemaOrRef, "Schema reference");
|
|
235
|
+
return this.componentName(refName(schemaOrRef.$ref));
|
|
58
236
|
}
|
|
59
237
|
|
|
60
|
-
const schema =
|
|
238
|
+
const schema =
|
|
239
|
+
dereference(this.openApi, schemaOrRef, "Schema reference") ?? schemaOrRef;
|
|
61
240
|
const nullable =
|
|
62
241
|
schema.nullable ||
|
|
63
242
|
(Array.isArray(schema.type) && schema.type.includes("null"));
|
|
64
243
|
let type = this.resolveNonNullable(schema, contextName, options);
|
|
65
|
-
|
|
66
|
-
if (nullable && !type.includes("null")) {
|
|
67
|
-
type = `${type} | null`;
|
|
68
|
-
}
|
|
69
|
-
|
|
244
|
+
if (nullable && !/(^|\W)null(\W|$)/.test(type)) type = `${type} | null`;
|
|
70
245
|
return type;
|
|
71
246
|
}
|
|
72
247
|
|
|
73
248
|
resolveNonNullable(schema, contextName, options = {}) {
|
|
74
|
-
if (schema.const !== undefined)
|
|
75
|
-
return JSON.stringify(schema.const);
|
|
76
|
-
}
|
|
249
|
+
if (schema.const !== undefined) return JSON.stringify(schema.const);
|
|
77
250
|
|
|
78
251
|
if (Array.isArray(schema.enum)) {
|
|
79
252
|
if (options.inlineEnumAsUnion) {
|
|
@@ -81,8 +254,7 @@ export class TypeResolver {
|
|
|
81
254
|
this.enumLiteralValues(schema).map((value) => JSON.stringify(value)),
|
|
82
255
|
);
|
|
83
256
|
}
|
|
84
|
-
|
|
85
|
-
return pascalCase(contextName);
|
|
257
|
+
return operationTypeName(contextName);
|
|
86
258
|
}
|
|
87
259
|
|
|
88
260
|
if (schema.oneOf || schema.anyOf) {
|
|
@@ -95,11 +267,20 @@ export class TypeResolver {
|
|
|
95
267
|
}
|
|
96
268
|
|
|
97
269
|
if (schema.allOf) {
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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);
|
|
103
284
|
}
|
|
104
285
|
|
|
105
286
|
const schemaType = Array.isArray(schema.type)
|
|
@@ -113,9 +294,8 @@ export class TypeResolver {
|
|
|
113
294
|
case "boolean":
|
|
114
295
|
return "boolean";
|
|
115
296
|
case "string":
|
|
116
|
-
if (schema.format === "binary")
|
|
297
|
+
if (schema.format === "binary")
|
|
117
298
|
return options.binaryAsBlob ? "Blob" : "File";
|
|
118
|
-
}
|
|
119
299
|
return "string";
|
|
120
300
|
case "array": {
|
|
121
301
|
const itemType = this.resolve(
|
|
@@ -130,14 +310,12 @@ export class TypeResolver {
|
|
|
130
310
|
}
|
|
131
311
|
case "object":
|
|
132
312
|
default:
|
|
133
|
-
if (schema.properties)
|
|
313
|
+
if (schema.properties)
|
|
134
314
|
return this.createInlineObjectType(schema, contextName, options);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
315
|
if (schema.additionalProperties) {
|
|
138
316
|
const valueType =
|
|
139
317
|
schema.additionalProperties === true
|
|
140
|
-
? "
|
|
318
|
+
? "unknown"
|
|
141
319
|
: this.resolve(
|
|
142
320
|
schema.additionalProperties,
|
|
143
321
|
`${contextName}Value`,
|
|
@@ -145,311 +323,413 @@ export class TypeResolver {
|
|
|
145
323
|
);
|
|
146
324
|
return `Record<string, ${valueType}>`;
|
|
147
325
|
}
|
|
148
|
-
|
|
149
|
-
return schemaType === "object" ? "Record<string, any>" : "any";
|
|
326
|
+
return schemaType === "object" ? "Record<string, unknown>" : "unknown";
|
|
150
327
|
}
|
|
151
328
|
}
|
|
152
329
|
|
|
153
|
-
createInlineObjectType(schema, contextName
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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++}`;
|
|
158
339
|
}
|
|
159
|
-
|
|
340
|
+
if (!this.inlineTypes.has(name)) this.inlineTypes.set(name, schema);
|
|
160
341
|
return name;
|
|
161
342
|
}
|
|
162
343
|
|
|
163
|
-
|
|
344
|
+
componentNamesHasValue(value) {
|
|
345
|
+
return [...this.componentNames.values()].includes(value);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
objectMembers(name, schema) {
|
|
164
349
|
const required = new Set(schema.required ?? []);
|
|
165
|
-
const lines = [
|
|
350
|
+
const lines = [];
|
|
166
351
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const optionalToken = propertyRequired ? "" : "?";
|
|
352
|
+
const propertyEntries = Object.entries(schema.properties ?? {});
|
|
353
|
+
assertUniqueTypePropertyNames(
|
|
354
|
+
propertyEntries.map(([propertyName]) => propertyName),
|
|
355
|
+
name,
|
|
356
|
+
);
|
|
173
357
|
|
|
358
|
+
for (const [propertyName, propertySchema] of propertyEntries) {
|
|
359
|
+
const localPropertyName = typePropertyName(propertyName);
|
|
360
|
+
const optionalToken = required.has(propertyName) ? "" : "?";
|
|
174
361
|
const propertyType = this.resolve(
|
|
175
362
|
propertySchema,
|
|
176
363
|
`${name}${pascalCase(propertyName)}`,
|
|
177
|
-
{
|
|
364
|
+
{
|
|
365
|
+
inlineEnumAsUnion: true,
|
|
366
|
+
},
|
|
367
|
+
);
|
|
368
|
+
const resolvedSchema = dereference(
|
|
369
|
+
this.openApi,
|
|
370
|
+
propertySchema,
|
|
371
|
+
"Property schema reference",
|
|
178
372
|
);
|
|
179
|
-
|
|
180
|
-
const resolvedSchema = dereference(this.openApi, propertySchema);
|
|
181
|
-
const typescriptPropertyName = camelCase(propertyName);
|
|
182
|
-
|
|
183
373
|
lines.push(
|
|
184
374
|
jsDocFromSchema(resolvedSchema, " ") +
|
|
185
|
-
` ${propertyKey(
|
|
375
|
+
` ${propertyKey(localPropertyName)}${optionalToken}: ${propertyType};`,
|
|
186
376
|
);
|
|
187
377
|
}
|
|
188
378
|
|
|
189
379
|
if (schema.additionalProperties) {
|
|
190
380
|
const valueType =
|
|
191
381
|
schema.additionalProperties === true
|
|
192
|
-
? "
|
|
382
|
+
? "unknown"
|
|
193
383
|
: this.resolve(schema.additionalProperties, `${name}Value`, {
|
|
194
384
|
inlineEnumAsUnion: true,
|
|
195
385
|
});
|
|
196
|
-
|
|
197
386
|
lines.push(` [key: string]: ${valueType};`);
|
|
198
387
|
}
|
|
199
388
|
|
|
200
|
-
lines
|
|
201
|
-
return lines.join("\n");
|
|
389
|
+
return lines;
|
|
202
390
|
}
|
|
203
391
|
|
|
204
|
-
|
|
205
|
-
|
|
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);
|
|
206
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) {
|
|
207
422
|
if (this.options.generateUnionEnums) {
|
|
208
|
-
return `export type ${name} = ${unionTypes(
|
|
423
|
+
return `export type ${name} = ${unionTypes(
|
|
424
|
+
this.enumLiteralValues(schema).map((value) => JSON.stringify(value)),
|
|
425
|
+
)};`;
|
|
209
426
|
}
|
|
210
427
|
|
|
211
428
|
const lines = [`export const ${name} = {`];
|
|
212
|
-
|
|
213
|
-
for (const [index, value] of values.entries()) {
|
|
429
|
+
for (const [index, value] of (schema.enum ?? []).entries()) {
|
|
214
430
|
const memberName = this.enumMemberName(schema, value, index);
|
|
215
431
|
const enumValue = this.enumRuntimeValue(schema, value, index);
|
|
216
|
-
lines.push(` ${memberName}: ${JSON.stringify(enumValue)},`);
|
|
432
|
+
lines.push(` ${propertyKey(memberName)}: ${JSON.stringify(enumValue)},`);
|
|
217
433
|
}
|
|
218
|
-
|
|
219
434
|
lines.push("} as const;");
|
|
220
435
|
lines.push(`export type ${name} = (typeof ${name})[keyof typeof ${name}];`);
|
|
221
436
|
return lines.join("\n");
|
|
222
437
|
}
|
|
223
438
|
|
|
224
439
|
enumMetadataNames(schema) {
|
|
225
|
-
const
|
|
440
|
+
const names =
|
|
226
441
|
schema?.["x-enumNames"] ??
|
|
227
442
|
schema?.["x-enum-varnames"] ??
|
|
228
443
|
schema?.["x-enumVarnames"];
|
|
229
|
-
|
|
230
|
-
return Array.isArray(enumNames) ? enumNames : [];
|
|
444
|
+
return Array.isArray(names) ? names : [];
|
|
231
445
|
}
|
|
232
446
|
|
|
233
447
|
enumLiteralValues(schema) {
|
|
234
448
|
const values = schema.enum ?? [];
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return values;
|
|
449
|
+
const names = this.enumMetadataNames(schema);
|
|
450
|
+
return this.options.enumNamesAsValues && names.length > 0
|
|
451
|
+
? values.map((value, index) => names[index] ?? value)
|
|
452
|
+
: values;
|
|
242
453
|
}
|
|
243
454
|
|
|
244
455
|
enumRuntimeValue(schema, value, index) {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return value;
|
|
456
|
+
const names = this.enumMetadataNames(schema);
|
|
457
|
+
return this.options.enumNamesAsValues && names.length > 0
|
|
458
|
+
? (names[index] ?? value)
|
|
459
|
+
: value;
|
|
252
460
|
}
|
|
253
461
|
|
|
254
462
|
enumMemberName(schema, value, index) {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
return pascalCase(String(rawName), "Value").replace(/^[0-9]/, "_$&");
|
|
463
|
+
const rawName = this.enumMetadataNames(schema)[index] ?? value;
|
|
464
|
+
return operationTypeName(String(rawName) || "Value");
|
|
258
465
|
}
|
|
259
466
|
}
|
|
260
467
|
|
|
261
|
-
function
|
|
262
|
-
return
|
|
263
|
-
schema?.properties ||
|
|
264
|
-
schema?.additionalProperties ||
|
|
265
|
-
schema?.type === "object",
|
|
266
|
-
);
|
|
468
|
+
function parameterTypeName(operationId, location) {
|
|
469
|
+
return `${operationId}${pascalCase(location)}Params`;
|
|
267
470
|
}
|
|
268
471
|
|
|
269
|
-
|
|
270
|
-
|
|
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`;
|
|
472
|
+
function combinedParameterTypeName(operationId) {
|
|
473
|
+
return `${operationId}Params`;
|
|
356
474
|
}
|
|
357
475
|
|
|
358
476
|
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
477
|
const lines = [`export interface ${name} {`];
|
|
365
|
-
|
|
478
|
+
assertUniqueTypePropertyNames(
|
|
479
|
+
parameters.map((parameter) => parameter.name),
|
|
480
|
+
name,
|
|
481
|
+
);
|
|
366
482
|
for (const parameter of parameters) {
|
|
367
|
-
const parameterSchema =
|
|
368
|
-
const optionalToken =
|
|
483
|
+
const parameterSchema = getParameterSchema(openApi, parameter);
|
|
484
|
+
const optionalToken =
|
|
485
|
+
parameter.required || parameter.in === "path" ? "" : "?";
|
|
369
486
|
const parameterType = resolver.resolve(
|
|
370
|
-
|
|
487
|
+
parameterSchema,
|
|
371
488
|
`${name}${pascalCase(parameter.name)}`,
|
|
372
|
-
{
|
|
489
|
+
{
|
|
490
|
+
inlineEnumAsUnion: true,
|
|
491
|
+
},
|
|
373
492
|
);
|
|
374
493
|
lines.push(
|
|
375
494
|
jsDocFromSchema(parameterSchema, " ") +
|
|
376
|
-
` ${propertyKey(parameter.name)}${optionalToken}: ${parameterType};`,
|
|
495
|
+
` ${propertyKey(typePropertyName(parameter.name))}${optionalToken}: ${parameterType};`,
|
|
377
496
|
);
|
|
378
497
|
}
|
|
379
|
-
|
|
380
498
|
lines.push("}");
|
|
381
499
|
return lines.join("\n");
|
|
382
500
|
}
|
|
383
501
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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;
|
|
387
526
|
const parameters = operationInfo.parameters ?? [];
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
527
|
+
const parametersByLocation = Object.fromEntries(
|
|
528
|
+
["path", "query", "header", "cookie"].map((location) => [
|
|
529
|
+
location,
|
|
530
|
+
parameters.filter((parameter) => parameter.in === location),
|
|
531
|
+
]),
|
|
391
532
|
);
|
|
533
|
+
|
|
392
534
|
const requestBodyContent = getRequestBodyContent(
|
|
535
|
+
openApi,
|
|
393
536
|
operationInfo.operation.requestBody,
|
|
394
537
|
);
|
|
395
538
|
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
539
|
let bodyType;
|
|
404
540
|
if (requestBodySchema) {
|
|
405
541
|
if (requestBodySchema.$ref) {
|
|
406
|
-
bodyType = resolver.resolve(requestBodySchema, `${
|
|
542
|
+
bodyType = resolver.resolve(requestBodySchema, `${operationTypeNameBase}Payload`);
|
|
407
543
|
} else if (
|
|
408
544
|
requestBodyContent?.contentType === "multipart/form-data" ||
|
|
409
|
-
|
|
545
|
+
hasObjectShape(requestBodySchema)
|
|
410
546
|
) {
|
|
411
|
-
bodyType = `${
|
|
547
|
+
bodyType = `${operationTypeNameBase}Payload`;
|
|
412
548
|
} else {
|
|
413
|
-
bodyType = resolver.resolve(requestBodySchema, `${
|
|
549
|
+
bodyType = resolver.resolve(requestBodySchema, `${operationTypeNameBase}Payload`, {
|
|
414
550
|
inlineEnumAsUnion: true,
|
|
415
551
|
});
|
|
416
552
|
}
|
|
417
553
|
}
|
|
418
554
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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 errorType = unionTypes(errorTypes, "unknown");
|
|
580
|
+
const responseSchema = responseContent?.mediaType?.schema;
|
|
581
|
+
const errorSchemas = errorResponses
|
|
582
|
+
.map(({ response }) => getResponseContent(response)?.mediaType?.schema)
|
|
583
|
+
.filter(Boolean);
|
|
584
|
+
|
|
585
|
+
const hasNonBodyInput = parameters.length > 0;
|
|
586
|
+
const paginated =
|
|
587
|
+
Boolean(operationInfo.operation["x-typedapi-pagination"]) ||
|
|
588
|
+
/(?:Api)?Pagination(?:Sort)?Response\b|Paged(?:Response|Result)?\b|Paginated(?:Response|Result)?\b/.test(
|
|
589
|
+
responseType,
|
|
590
|
+
);
|
|
591
|
+
const isPurePaginatedQuery =
|
|
592
|
+
paginated &&
|
|
593
|
+
parametersByLocation.query.length > 0 &&
|
|
594
|
+
!parametersByLocation.path.length &&
|
|
595
|
+
!parametersByLocation.header.length &&
|
|
596
|
+
!parametersByLocation.cookie.length &&
|
|
597
|
+
!bodyType;
|
|
598
|
+
const parameterTypeNames = isPurePaginatedQuery
|
|
599
|
+
? { query: parameterTypeName(operationTypeNameBase, "query") }
|
|
600
|
+
: {};
|
|
426
601
|
|
|
427
602
|
return {
|
|
428
603
|
operationId,
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
604
|
+
operationTypeNameBase,
|
|
605
|
+
originalOperationId: operationInfo.originalOperationId,
|
|
606
|
+
methodName: operationInfo.methodName,
|
|
607
|
+
methodNameSource: operationInfo.methodNameSource,
|
|
608
|
+
paramsTypeName:
|
|
609
|
+
hasNonBodyInput && !isPurePaginatedQuery
|
|
610
|
+
? combinedParameterTypeName(operationTypeNameBase)
|
|
611
|
+
: undefined,
|
|
612
|
+
parameterTypeNames,
|
|
613
|
+
parameters,
|
|
614
|
+
parametersByLocation,
|
|
615
|
+
allParametersOptional: parameters.every(
|
|
616
|
+
(parameter) => parameter.in !== "path" && !parameter.required,
|
|
617
|
+
),
|
|
433
618
|
bodyType,
|
|
619
|
+
bodyRequired: Boolean(requestBodyContent?.requestBody?.required),
|
|
620
|
+
bodyWireSchemaKey: requestBodySchema
|
|
621
|
+
? operationWireSchemaKey(operationId, "body")
|
|
622
|
+
: undefined,
|
|
434
623
|
responseType,
|
|
624
|
+
responseWireSchemaKey: responseSchema
|
|
625
|
+
? operationWireSchemaKey(operationId, "response")
|
|
626
|
+
: undefined,
|
|
627
|
+
errorType,
|
|
628
|
+
errorWireSchemaKey:
|
|
629
|
+
errorSchemas.length > 0
|
|
630
|
+
? operationWireSchemaKey(operationId, "error")
|
|
631
|
+
: undefined,
|
|
435
632
|
contentType: requestBodyContent?.contentType,
|
|
436
633
|
responseContentType: responseContent?.contentType,
|
|
437
|
-
hasPathParams:
|
|
438
|
-
hasQueryParams:
|
|
439
|
-
|
|
440
|
-
|
|
634
|
+
hasPathParams: parametersByLocation.path.length > 0,
|
|
635
|
+
hasQueryParams: parametersByLocation.query.length > 0,
|
|
636
|
+
hasHeaderParams: parametersByLocation.header.length > 0,
|
|
637
|
+
hasCookieParams: parametersByLocation.cookie.length > 0,
|
|
638
|
+
paginationMetadata: operationInfo.operation["x-typedapi-pagination"],
|
|
441
639
|
};
|
|
442
640
|
}
|
|
443
641
|
|
|
642
|
+
export function generateDataContracts(openApi, operations, options = {}) {
|
|
643
|
+
const resolver = new TypeResolver(openApi, options);
|
|
644
|
+
const declarations = [];
|
|
645
|
+
const schemas = openApi.components?.schemas ?? {};
|
|
646
|
+
|
|
647
|
+
const entries = Object.entries(schemas).sort(([a], [b]) =>
|
|
648
|
+
a.localeCompare(b),
|
|
649
|
+
);
|
|
650
|
+
for (const [rawName, schemaOrRef] of entries) {
|
|
651
|
+
const schema = dereference(
|
|
652
|
+
openApi,
|
|
653
|
+
schemaOrRef,
|
|
654
|
+
"Component schema reference",
|
|
655
|
+
);
|
|
656
|
+
declarations.push(
|
|
657
|
+
resolver.createDeclaration(resolver.componentName(rawName), schema),
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
for (const operationInfo of operations) {
|
|
662
|
+
const operationTypes = getOperationTypes(
|
|
663
|
+
openApi,
|
|
664
|
+
operationInfo,
|
|
665
|
+
options,
|
|
666
|
+
resolver,
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
if (operationTypes.paramsTypeName) {
|
|
670
|
+
declarations.push(
|
|
671
|
+
createParamsInterface(
|
|
672
|
+
openApi,
|
|
673
|
+
resolver,
|
|
674
|
+
operationTypes.paramsTypeName,
|
|
675
|
+
operationTypes.parameters,
|
|
676
|
+
),
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
for (const [location, parameters] of Object.entries(
|
|
681
|
+
operationTypes.parametersByLocation,
|
|
682
|
+
)) {
|
|
683
|
+
const typeName = operationTypes.parameterTypeNames[location];
|
|
684
|
+
if (typeName)
|
|
685
|
+
declarations.push(
|
|
686
|
+
createParamsInterface(openApi, resolver, typeName, parameters),
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const bodyContent = getRequestBodyContent(
|
|
691
|
+
openApi,
|
|
692
|
+
operationInfo.operation.requestBody,
|
|
693
|
+
);
|
|
694
|
+
const bodySchema = bodyContent?.mediaType?.schema;
|
|
695
|
+
if (
|
|
696
|
+
bodySchema &&
|
|
697
|
+
!bodySchema.$ref &&
|
|
698
|
+
(bodyContent.contentType === "multipart/form-data" ||
|
|
699
|
+
hasObjectShape(bodySchema))
|
|
700
|
+
) {
|
|
701
|
+
declarations.push(
|
|
702
|
+
resolver.createDeclaration(
|
|
703
|
+
`${operationTypes.operationTypeNameBase}Payload`,
|
|
704
|
+
bodySchema,
|
|
705
|
+
),
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
for (const [name, schema] of resolver.inlineTypes.entries()) {
|
|
711
|
+
if (!resolver.componentNamesHasValue(name))
|
|
712
|
+
declarations.push(resolver.createDeclaration(name, schema));
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const wireSchemas = generateWireSchemaRegistry(openApi, operations, options);
|
|
716
|
+
declarations.push(
|
|
717
|
+
`export const typedApiWireSchemas = ${JSON.stringify(wireSchemas, null, 2)} as const;`,
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
return `${generatedFileHeader()}\n\n${declarations.join("\n\n")}\n`;
|
|
721
|
+
}
|
|
722
|
+
|
|
444
723
|
export function generatedFileHeader() {
|
|
445
724
|
return `/* eslint-disable */
|
|
446
725
|
/* tslint:disable */
|
|
447
726
|
/*
|
|
448
|
-
*
|
|
449
|
-
* ## THIS FILE WAS GENERATED BY typedapi-client-helpers
|
|
450
|
-
* ##
|
|
451
|
-
* ##
|
|
727
|
+
* -------------------------------------------------------------------------------------
|
|
728
|
+
* ## THIS FILE WAS GENERATED BY typedapi-client-helpers ##
|
|
729
|
+
* ## DO NOT EDIT THIS FILE DIRECTLY ##
|
|
730
|
+
* ## ##
|
|
731
|
+
* ## AUTHOR: Stef Buurman ##
|
|
452
732
|
* ## SOURCE: https://github.com/Stef-Buurman/TypedApi/tree/main/npm/typed-api-client ##
|
|
453
|
-
*
|
|
733
|
+
* -------------------------------------------------------------------------------------
|
|
454
734
|
*/`;
|
|
455
735
|
}
|