@sdk-it/typescript 0.22.0 → 0.23.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/dist/index.js +430 -467
- package/dist/index.js.map +4 -4
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/emitters/zod.d.ts +1 -13
- package/dist/lib/emitters/zod.d.ts.map +1 -1
- package/dist/lib/generate.d.ts +1 -1
- package/dist/lib/generate.d.ts.map +1 -1
- package/dist/lib/generator.d.ts.map +1 -1
- package/dist/lib/import-utilities.d.ts +17 -0
- package/dist/lib/import-utilities.d.ts.map +1 -0
- package/dist/lib/sdk.d.ts +3 -8
- package/dist/lib/sdk.d.ts.map +1 -1
- package/dist/lib/typescript-snippet.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +0 -4
- package/dist/lib/utils.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -15,24 +15,295 @@ import {
|
|
|
15
15
|
augmentSpec,
|
|
16
16
|
cleanFiles,
|
|
17
17
|
readWriteMetadata,
|
|
18
|
-
sanitizeTag as sanitizeTag4
|
|
18
|
+
sanitizeTag as sanitizeTag4,
|
|
19
|
+
securityToOptions as securityToOptions2
|
|
19
20
|
} from "@sdk-it/spec";
|
|
20
21
|
|
|
21
22
|
// packages/typescript/src/lib/client.ts
|
|
22
23
|
import { toLitObject } from "@sdk-it/core";
|
|
24
|
+
|
|
25
|
+
// packages/typescript/src/lib/emitters/zod.ts
|
|
26
|
+
import { followRef, isRef, parseRef, pascalcase } from "@sdk-it/core";
|
|
27
|
+
import { isPrimitiveSchema, sanitizeTag } from "@sdk-it/spec";
|
|
28
|
+
var ZodEmitter = class {
|
|
29
|
+
#generatedRefs = /* @__PURE__ */ new Set();
|
|
30
|
+
#spec;
|
|
31
|
+
#onRef;
|
|
32
|
+
constructor(spec, onRef) {
|
|
33
|
+
this.#spec = spec;
|
|
34
|
+
this.#onRef = onRef;
|
|
35
|
+
}
|
|
36
|
+
#object(schema) {
|
|
37
|
+
const properties = schema.properties || {};
|
|
38
|
+
const propEntries = Object.entries(properties).map(([key, propSchema]) => {
|
|
39
|
+
const isRequired = (schema.required ?? []).includes(key);
|
|
40
|
+
return `'${key}': ${this.handle(propSchema, isRequired)}`;
|
|
41
|
+
});
|
|
42
|
+
let additionalProps = "";
|
|
43
|
+
if (schema.additionalProperties) {
|
|
44
|
+
if (typeof schema.additionalProperties === "object") {
|
|
45
|
+
const addPropZod = this.handle(schema.additionalProperties, true);
|
|
46
|
+
additionalProps = `.catchall(${addPropZod})`;
|
|
47
|
+
} else if (schema.additionalProperties === true) {
|
|
48
|
+
additionalProps = `.catchall(z.unknown())`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return `z.object({${propEntries.join(", ")}})${additionalProps}`;
|
|
52
|
+
}
|
|
53
|
+
#array(schema, required = false) {
|
|
54
|
+
const { items } = schema;
|
|
55
|
+
if (!items) {
|
|
56
|
+
return `z.array(z.unknown())${appendOptional(required)}`;
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(items)) {
|
|
59
|
+
const tupleItems = items.map((sub) => this.handle(sub, true));
|
|
60
|
+
const base = `z.tuple([${tupleItems.join(", ")}])`;
|
|
61
|
+
return `${base}${appendOptional(required)}`;
|
|
62
|
+
}
|
|
63
|
+
const itemsSchema = this.handle(items, true);
|
|
64
|
+
return `z.array(${itemsSchema})${this.#suffixes(JSON.stringify(schema.default), required, false)}`;
|
|
65
|
+
}
|
|
66
|
+
#suffixes = (defaultValue, required, nullable) => {
|
|
67
|
+
return `${nullable ? ".nullable()" : ""}${appendDefault(defaultValue)}${appendOptional(required)}`;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Convert a basic type (string | number | boolean | object | array, etc.) to Zod.
|
|
71
|
+
* We'll also handle .optional() if needed.
|
|
72
|
+
*/
|
|
73
|
+
normal(type, schema, required = false, nullable = false) {
|
|
74
|
+
switch (type) {
|
|
75
|
+
case "string":
|
|
76
|
+
return `${this.string(schema)}${this.#suffixes(JSON.stringify(schema.default), required, nullable)}`;
|
|
77
|
+
case "number":
|
|
78
|
+
case "integer": {
|
|
79
|
+
const { base, defaultValue } = this.#number(schema);
|
|
80
|
+
return `${base}${this.#suffixes(defaultValue, required, nullable)}`;
|
|
81
|
+
}
|
|
82
|
+
case "boolean":
|
|
83
|
+
return `z.boolean()${this.#suffixes(schema.default, required, nullable)}`;
|
|
84
|
+
case "object":
|
|
85
|
+
return `${this.#object(schema)}${this.#suffixes(JSON.stringify(schema.default), required, nullable)}`;
|
|
86
|
+
// required always
|
|
87
|
+
case "array":
|
|
88
|
+
return this.#array(schema, required);
|
|
89
|
+
case "null":
|
|
90
|
+
return `z.null()${appendOptional(required)}`;
|
|
91
|
+
default:
|
|
92
|
+
return `z.unknown()${appendOptional(required)}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
#ref($ref, required) {
|
|
96
|
+
const schemaName = pascalcase(sanitizeTag(parseRef($ref).model));
|
|
97
|
+
const schema = followRef(this.#spec, $ref);
|
|
98
|
+
if (isPrimitiveSchema(schema)) {
|
|
99
|
+
const result = this.handle(schema, required);
|
|
100
|
+
this.#onRef?.(schemaName, result);
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
if (this.#generatedRefs.has(schemaName)) {
|
|
104
|
+
return schemaName;
|
|
105
|
+
}
|
|
106
|
+
this.#generatedRefs.add(schemaName);
|
|
107
|
+
this.#onRef?.(schemaName, this.handle(schema, required));
|
|
108
|
+
return schemaName;
|
|
109
|
+
}
|
|
110
|
+
#toIntersection(schemas) {
|
|
111
|
+
const [left, ...right] = schemas;
|
|
112
|
+
if (!right.length) {
|
|
113
|
+
return left;
|
|
114
|
+
}
|
|
115
|
+
return `z.intersection(${left}, ${this.#toIntersection(right)})`;
|
|
116
|
+
}
|
|
117
|
+
allOf(schemas, required) {
|
|
118
|
+
const allOfSchemas = schemas.map((sub) => this.handle(sub, true));
|
|
119
|
+
if (allOfSchemas.length === 0) {
|
|
120
|
+
return `z.unknown()`;
|
|
121
|
+
}
|
|
122
|
+
if (allOfSchemas.length === 1) {
|
|
123
|
+
return `${allOfSchemas[0]}${appendOptional(required)}`;
|
|
124
|
+
}
|
|
125
|
+
return `${this.#toIntersection(allOfSchemas)}${appendOptional(required)}`;
|
|
126
|
+
}
|
|
127
|
+
anyOf(schemas, required) {
|
|
128
|
+
const anyOfSchemas = schemas.map((sub) => this.handle(sub, true));
|
|
129
|
+
if (anyOfSchemas.length === 1) {
|
|
130
|
+
return `${anyOfSchemas[0]}${appendOptional(required)}`;
|
|
131
|
+
}
|
|
132
|
+
return `z.union([${anyOfSchemas.join(", ")}])${appendOptional(required)}`;
|
|
133
|
+
}
|
|
134
|
+
oneOf(schemas, required) {
|
|
135
|
+
const oneOfSchemas = schemas.map((sub) => this.handle(sub, true));
|
|
136
|
+
if (oneOfSchemas.length === 1) {
|
|
137
|
+
return `${oneOfSchemas[0]}${appendOptional(required)}`;
|
|
138
|
+
}
|
|
139
|
+
return `z.union([${oneOfSchemas.join(", ")}])${appendOptional(required)}`;
|
|
140
|
+
}
|
|
141
|
+
enum(type, values) {
|
|
142
|
+
if (values.length === 1) {
|
|
143
|
+
return `z.literal(${values.join(", ")})`;
|
|
144
|
+
}
|
|
145
|
+
if (type === "integer") {
|
|
146
|
+
return `z.union([${values.map((val) => `z.literal(${val})`).join(", ")}])`;
|
|
147
|
+
}
|
|
148
|
+
return `z.enum([${values.join(", ")}])`;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Handle a `string` schema with possible format keywords (JSON Schema).
|
|
152
|
+
*/
|
|
153
|
+
string(schema) {
|
|
154
|
+
let base = "z.string()";
|
|
155
|
+
if (schema.contentEncoding === "binary") {
|
|
156
|
+
base = "z.instanceof(Blob)";
|
|
157
|
+
return base;
|
|
158
|
+
}
|
|
159
|
+
switch (schema.format) {
|
|
160
|
+
case "date-time":
|
|
161
|
+
case "datetime":
|
|
162
|
+
base = "z.coerce.date()";
|
|
163
|
+
break;
|
|
164
|
+
case "date":
|
|
165
|
+
base = "z.coerce.date() /* or z.string() if you want raw date strings */";
|
|
166
|
+
break;
|
|
167
|
+
case "time":
|
|
168
|
+
base = "z.string() /* optionally add .regex(...) for HH:MM:SS format */";
|
|
169
|
+
break;
|
|
170
|
+
case "email":
|
|
171
|
+
base = "z.string().email()";
|
|
172
|
+
break;
|
|
173
|
+
case "uuid":
|
|
174
|
+
base = "z.string().uuid()";
|
|
175
|
+
break;
|
|
176
|
+
case "url":
|
|
177
|
+
case "uri":
|
|
178
|
+
base = "z.string().url()";
|
|
179
|
+
break;
|
|
180
|
+
case "ipv4":
|
|
181
|
+
base = 'z.string().ip({version: "v4"})';
|
|
182
|
+
break;
|
|
183
|
+
case "ipv6":
|
|
184
|
+
base = 'z.string().ip({version: "v6"})';
|
|
185
|
+
break;
|
|
186
|
+
case "phone":
|
|
187
|
+
base = "z.string() /* or add .regex(...) for phone formats */";
|
|
188
|
+
break;
|
|
189
|
+
case "byte":
|
|
190
|
+
case "binary":
|
|
191
|
+
base = "z.instanceof(Blob)";
|
|
192
|
+
break;
|
|
193
|
+
case "int64":
|
|
194
|
+
base = "z.string() /* or z.bigint() if your app can handle it */";
|
|
195
|
+
break;
|
|
196
|
+
default:
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
return base;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Handle number/integer constraints from OpenAPI/JSON Schema.
|
|
203
|
+
* In 3.1, exclusiveMinimum/Maximum hold the actual numeric threshold,
|
|
204
|
+
* rather than a boolean toggling `minimum`/`maximum`.
|
|
205
|
+
*/
|
|
206
|
+
#number(schema) {
|
|
207
|
+
let defaultValue = schema.default;
|
|
208
|
+
let base = "z.number()";
|
|
209
|
+
if (schema.format === "int64") {
|
|
210
|
+
base = "z.bigint()";
|
|
211
|
+
if (schema.default !== void 0) {
|
|
212
|
+
defaultValue = `BigInt(${schema.default})`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (schema.format === "int32") {
|
|
216
|
+
base += ".int()";
|
|
217
|
+
}
|
|
218
|
+
if (typeof schema.exclusiveMinimum === "number") {
|
|
219
|
+
base += `.gt(${schema.exclusiveMinimum})`;
|
|
220
|
+
}
|
|
221
|
+
if (typeof schema.exclusiveMaximum === "number") {
|
|
222
|
+
base += `.lt(${schema.exclusiveMaximum})`;
|
|
223
|
+
}
|
|
224
|
+
if (typeof schema.minimum === "number") {
|
|
225
|
+
base += schema.format === "int64" ? `.min(BigInt(${schema.minimum}))` : `.min(${schema.minimum})`;
|
|
226
|
+
}
|
|
227
|
+
if (typeof schema.maximum === "number") {
|
|
228
|
+
base += schema.format === "int64" ? `.max(BigInt(${schema.maximum}))` : `.max(${schema.maximum})`;
|
|
229
|
+
}
|
|
230
|
+
if (typeof schema.multipleOf === "number") {
|
|
231
|
+
base += `.refine((val) => Number.isInteger(val / ${schema.multipleOf}), "Must be a multiple of ${schema.multipleOf}")`;
|
|
232
|
+
}
|
|
233
|
+
return { base, defaultValue };
|
|
234
|
+
}
|
|
235
|
+
handle(schema, required) {
|
|
236
|
+
if (isRef(schema)) {
|
|
237
|
+
return `${this.#ref(schema.$ref, true)}${appendOptional(required)}`;
|
|
238
|
+
}
|
|
239
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
240
|
+
return this.allOf(schema.allOf ?? [], required);
|
|
241
|
+
}
|
|
242
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
243
|
+
return this.anyOf(schema.anyOf ?? [], required);
|
|
244
|
+
}
|
|
245
|
+
if (schema.oneOf && Array.isArray(schema.oneOf) && schema.oneOf.length) {
|
|
246
|
+
return this.oneOf(schema.oneOf ?? [], required);
|
|
247
|
+
}
|
|
248
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
249
|
+
const enumVals = schema.enum.map((val) => JSON.stringify(val));
|
|
250
|
+
const defaultValue = enumVals.includes(JSON.stringify(schema.default)) ? JSON.stringify(schema.default) : void 0;
|
|
251
|
+
return `${this.enum(schema.type, enumVals)}${this.#suffixes(defaultValue, required, false)}`;
|
|
252
|
+
}
|
|
253
|
+
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
254
|
+
if (!types.length) {
|
|
255
|
+
return `z.unknown()${appendOptional(required)}`;
|
|
256
|
+
}
|
|
257
|
+
if ("nullable" in schema && schema.nullable) {
|
|
258
|
+
types.push("null");
|
|
259
|
+
} else if (schema.default === null) {
|
|
260
|
+
types.push("null");
|
|
261
|
+
}
|
|
262
|
+
if (types.length > 1) {
|
|
263
|
+
const realTypes = types.filter((t) => t !== "null");
|
|
264
|
+
if (realTypes.length === 1 && types.includes("null")) {
|
|
265
|
+
return this.normal(realTypes[0], schema, required, true);
|
|
266
|
+
}
|
|
267
|
+
const subSchemas = types.map((t) => this.normal(t, schema, false));
|
|
268
|
+
return `z.union([${subSchemas.join(", ")}])${appendOptional(required)}`;
|
|
269
|
+
}
|
|
270
|
+
return this.normal(types[0], schema, required, false);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
function appendOptional(isRequired) {
|
|
274
|
+
return isRequired ? "" : ".optional()";
|
|
275
|
+
}
|
|
276
|
+
function appendDefault(defaultValue) {
|
|
277
|
+
return defaultValue !== void 0 || typeof defaultValue !== "undefined" ? `.default(${defaultValue})` : "";
|
|
278
|
+
}
|
|
279
|
+
function toZod(schema, required) {
|
|
280
|
+
const emitter = new ZodEmitter({});
|
|
281
|
+
const schemaStr = emitter.handle(schema, required ?? false);
|
|
282
|
+
if (schema["x-prefix"]) {
|
|
283
|
+
const prefix = schema["x-prefix"];
|
|
284
|
+
if (required === false) {
|
|
285
|
+
return schemaStr + `.transform((val) => (val ? \`${prefix}\${val}\` : undefined))`;
|
|
286
|
+
} else {
|
|
287
|
+
return schemaStr + `.transform((val) => \`${prefix}\${val}\`)`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return schemaStr;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// packages/typescript/src/lib/client.ts
|
|
23
294
|
var client_default = (spec, style) => {
|
|
24
|
-
const
|
|
25
|
-
(
|
|
26
|
-
);
|
|
27
|
-
const defaultHeaders = `{${optionsEntries.filter(([, value]) => value.in === "header").map(
|
|
28
|
-
([key, value]) => `${key}: this.options[${value.optionName ? `'${value.optionName}'` : key}]`
|
|
295
|
+
const defaultHeaders = `{${spec.options.filter((value) => value.in === "header").map(
|
|
296
|
+
(value) => `'${value.name}': this.options['${value["x-optionName"] ?? value.name}']`
|
|
29
297
|
).join(",\n")}}`;
|
|
30
|
-
const defaultInputs = `{${
|
|
31
|
-
(
|
|
298
|
+
const defaultInputs = `{${spec.options.filter((value) => value.in === "input").map(
|
|
299
|
+
(value) => `'${value.name}': this.options['${value["x-optionName"] ?? value.name}']`
|
|
32
300
|
).join(",\n")}}`;
|
|
33
301
|
const specOptions = {
|
|
34
302
|
...Object.fromEntries(
|
|
35
|
-
|
|
303
|
+
spec.options.map((value) => [
|
|
304
|
+
`'${value["x-optionName"] ?? value.name}'`,
|
|
305
|
+
{ schema: toZod(value.schema, value.required) }
|
|
306
|
+
])
|
|
36
307
|
),
|
|
37
308
|
fetch: {
|
|
38
309
|
schema: "fetchType"
|
|
@@ -145,8 +416,8 @@ export class ${spec.name} {
|
|
|
145
416
|
};
|
|
146
417
|
|
|
147
418
|
// packages/typescript/src/lib/emitters/interface.ts
|
|
148
|
-
import { followRef, isRef, parseRef, pascalcase } from "@sdk-it/core";
|
|
149
|
-
import { isPrimitiveSchema, sanitizeTag } from "@sdk-it/spec";
|
|
419
|
+
import { followRef as followRef2, isRef as isRef2, parseRef as parseRef2, pascalcase as pascalcase2 } from "@sdk-it/core";
|
|
420
|
+
import { isPrimitiveSchema as isPrimitiveSchema2, sanitizeTag as sanitizeTag2 } from "@sdk-it/spec";
|
|
150
421
|
var TypeScriptEmitter = class {
|
|
151
422
|
#spec;
|
|
152
423
|
constructor(spec) {
|
|
@@ -198,381 +469,160 @@ var TypeScriptEmitter = class {
|
|
|
198
469
|
case "integer":
|
|
199
470
|
return this.number(schema, required);
|
|
200
471
|
case "boolean":
|
|
201
|
-
return
|
|
472
|
+
return appendOptional2("boolean", required);
|
|
202
473
|
case "object":
|
|
203
474
|
return this.object(schema, required);
|
|
204
475
|
case "array":
|
|
205
476
|
return this.#array(schema, required);
|
|
206
477
|
case "null":
|
|
207
478
|
return "null";
|
|
208
|
-
default:
|
|
209
|
-
console.warn(`Unknown type: ${type}`);
|
|
210
|
-
return
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
#ref($ref, required) {
|
|
214
|
-
const schemaName = pascalcase(sanitizeTag(parseRef($ref).model));
|
|
215
|
-
const schema = followRef(this.#spec, $ref);
|
|
216
|
-
if (isPrimitiveSchema(schema)) {
|
|
217
|
-
return this.handle(schema, required);
|
|
218
|
-
}
|
|
219
|
-
return `models.${appendOptional(schemaName, required)}`;
|
|
220
|
-
}
|
|
221
|
-
allOf(schemas) {
|
|
222
|
-
const allOfTypes = schemas.map((sub) => this.handle(sub, true));
|
|
223
|
-
return allOfTypes.length > 1 ? `${allOfTypes.join(" & ")}` : allOfTypes[0];
|
|
224
|
-
}
|
|
225
|
-
oneOf(schemas, required) {
|
|
226
|
-
const oneOfTypes = schemas.map((sub) => this.handle(sub, true));
|
|
227
|
-
return appendOptional(
|
|
228
|
-
oneOfTypes.length > 1 ? `${oneOfTypes.join(" | ")}` : oneOfTypes[0],
|
|
229
|
-
required
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
anyOf(schemas, required) {
|
|
233
|
-
return this.oneOf(schemas, required);
|
|
234
|
-
}
|
|
235
|
-
enum(values, required) {
|
|
236
|
-
const enumValues = values.map((val) => typeof val === "string" ? `'${val}'` : `${val}`).join(" | ");
|
|
237
|
-
return appendOptional(enumValues, required);
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Handle string type with formats
|
|
241
|
-
*/
|
|
242
|
-
string(schema, required) {
|
|
243
|
-
let type;
|
|
244
|
-
if (schema.contentEncoding === "binary") {
|
|
245
|
-
return appendOptional("Blob", required);
|
|
246
|
-
}
|
|
247
|
-
switch (schema.format) {
|
|
248
|
-
case "date-time":
|
|
249
|
-
case "datetime":
|
|
250
|
-
case "date":
|
|
251
|
-
type = "Date";
|
|
252
|
-
break;
|
|
253
|
-
case "binary":
|
|
254
|
-
case "byte":
|
|
255
|
-
type = "Blob";
|
|
256
|
-
break;
|
|
257
|
-
case "int64":
|
|
258
|
-
type = "bigint";
|
|
259
|
-
break;
|
|
260
|
-
default:
|
|
261
|
-
type = "string";
|
|
262
|
-
}
|
|
263
|
-
return appendOptional(type, required);
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Handle number/integer types with formats
|
|
267
|
-
*/
|
|
268
|
-
number(schema, required) {
|
|
269
|
-
const type = schema.format === "int64" ? "bigint" : "number";
|
|
270
|
-
return appendOptional(type, required);
|
|
271
|
-
}
|
|
272
|
-
handle(schema, required) {
|
|
273
|
-
if (isRef(schema)) {
|
|
274
|
-
return this.#ref(schema.$ref, required);
|
|
275
|
-
}
|
|
276
|
-
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
277
|
-
return this.allOf(schema.allOf);
|
|
278
|
-
}
|
|
279
|
-
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
280
|
-
return this.anyOf(schema.anyOf, required);
|
|
281
|
-
}
|
|
282
|
-
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
283
|
-
return this.oneOf(schema.oneOf, required);
|
|
284
|
-
}
|
|
285
|
-
if (schema.enum && Array.isArray(schema.enum)) {
|
|
286
|
-
return this.enum(schema.enum, required);
|
|
287
|
-
}
|
|
288
|
-
if (schema.const) {
|
|
289
|
-
return this.enum([schema.const], true);
|
|
290
|
-
}
|
|
291
|
-
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
292
|
-
if (!types.length) {
|
|
293
|
-
if ("properties" in schema) {
|
|
294
|
-
return this.object(schema, required);
|
|
295
|
-
}
|
|
296
|
-
return appendOptional("any", required);
|
|
297
|
-
}
|
|
298
|
-
if (types.length > 1) {
|
|
299
|
-
const realTypes = types.filter((t) => t !== "null");
|
|
300
|
-
if (realTypes.length === 1 && types.includes("null")) {
|
|
301
|
-
const tsType = this.normal(realTypes[0], schema, false);
|
|
302
|
-
return appendOptional(`${tsType} | null`, required);
|
|
303
|
-
}
|
|
304
|
-
const typeResults = types.map((t) => this.normal(t, schema, false));
|
|
305
|
-
return appendOptional(typeResults.join(" | "), required);
|
|
306
|
-
}
|
|
307
|
-
return this.normal(types[0], schema, required);
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
function appendOptional(type, isRequired) {
|
|
311
|
-
return isRequired ? type : `${type} | undefined`;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// packages/typescript/src/lib/generator.ts
|
|
315
|
-
import { merge, template } from "lodash-es";
|
|
316
|
-
import { join } from "node:path";
|
|
317
|
-
import { camelcase as camelcase2, spinalcase } from "stringcase";
|
|
318
|
-
import { followRef as followRef4, isEmpty as isEmpty2, isRef as isRef4, resolveRef } from "@sdk-it/core";
|
|
319
|
-
import { forEachOperation } from "@sdk-it/spec";
|
|
320
|
-
|
|
321
|
-
// packages/typescript/src/lib/emitters/zod.ts
|
|
322
|
-
import { followRef as followRef2, isRef as isRef2, parseRef as parseRef2, pascalcase as pascalcase2 } from "@sdk-it/core";
|
|
323
|
-
import { isPrimitiveSchema as isPrimitiveSchema2, sanitizeTag as sanitizeTag2 } from "@sdk-it/spec";
|
|
324
|
-
var ZodEmitter = class {
|
|
325
|
-
#generatedRefs = /* @__PURE__ */ new Set();
|
|
326
|
-
#spec;
|
|
327
|
-
#onRef;
|
|
328
|
-
constructor(spec, onRef) {
|
|
329
|
-
this.#spec = spec;
|
|
330
|
-
this.#onRef = onRef;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Handle objects (properties, additionalProperties).
|
|
334
|
-
*/
|
|
335
|
-
object(schema) {
|
|
336
|
-
const properties = schema.properties || {};
|
|
337
|
-
const propEntries = Object.entries(properties).map(([key, propSchema]) => {
|
|
338
|
-
const isRequired = (schema.required ?? []).includes(key);
|
|
339
|
-
return `'${key}': ${this.handle(propSchema, isRequired)}`;
|
|
340
|
-
});
|
|
341
|
-
let additionalProps = "";
|
|
342
|
-
if (schema.additionalProperties) {
|
|
343
|
-
if (typeof schema.additionalProperties === "object") {
|
|
344
|
-
const addPropZod = this.handle(schema.additionalProperties, true);
|
|
345
|
-
additionalProps = `.catchall(${addPropZod})`;
|
|
346
|
-
} else if (schema.additionalProperties === true) {
|
|
347
|
-
additionalProps = `.catchall(z.unknown())`;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return `z.object({${propEntries.join(", ")}})${additionalProps}`;
|
|
351
|
-
}
|
|
352
|
-
#array(schema, required = false) {
|
|
353
|
-
const { items } = schema;
|
|
354
|
-
if (!items) {
|
|
355
|
-
return `z.array(z.unknown())${appendOptional2(required)}`;
|
|
356
|
-
}
|
|
357
|
-
if (Array.isArray(items)) {
|
|
358
|
-
const tupleItems = items.map((sub) => this.handle(sub, true));
|
|
359
|
-
const base = `z.tuple([${tupleItems.join(", ")}])`;
|
|
360
|
-
return `${base}${appendOptional2(required)}`;
|
|
361
|
-
}
|
|
362
|
-
const itemsSchema = this.handle(items, true);
|
|
363
|
-
return `z.array(${itemsSchema})${this.#suffixes(JSON.stringify(schema.default), required, false)}`;
|
|
364
|
-
}
|
|
365
|
-
#suffixes = (defaultValue, required, nullable) => {
|
|
366
|
-
return `${nullable ? ".nullable()" : ""}${appendDefault(defaultValue)}${appendOptional2(required)}`;
|
|
367
|
-
};
|
|
368
|
-
/**
|
|
369
|
-
* Convert a basic type (string | number | boolean | object | array, etc.) to Zod.
|
|
370
|
-
* We'll also handle .optional() if needed.
|
|
371
|
-
*/
|
|
372
|
-
normal(type, schema, required = false, nullable = false) {
|
|
373
|
-
switch (type) {
|
|
374
|
-
case "string":
|
|
375
|
-
return `${this.string(schema)}${this.#suffixes(JSON.stringify(schema.default), required, nullable)}`;
|
|
376
|
-
case "number":
|
|
377
|
-
case "integer": {
|
|
378
|
-
const { base, defaultValue } = this.number(schema);
|
|
379
|
-
return `${base}${this.#suffixes(defaultValue, required, nullable)}`;
|
|
380
|
-
}
|
|
381
|
-
case "boolean":
|
|
382
|
-
return `z.boolean()${this.#suffixes(schema.default, required, nullable)}`;
|
|
383
|
-
case "object":
|
|
384
|
-
return `${this.object(schema)}${this.#suffixes(JSON.stringify(schema.default), required, nullable)}`;
|
|
385
|
-
case "array":
|
|
386
|
-
return this.#array(schema, required);
|
|
387
|
-
case "null":
|
|
388
|
-
return `z.null()${appendOptional2(required)}`;
|
|
389
|
-
default:
|
|
390
|
-
return `z.unknown()${appendOptional2(required)}`;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
#ref($ref, required) {
|
|
394
|
-
const schemaName = pascalcase2(sanitizeTag2(parseRef2($ref).model));
|
|
395
|
-
const schema = followRef2(this.#spec, $ref);
|
|
396
|
-
if (isPrimitiveSchema2(schema)) {
|
|
397
|
-
const result = this.handle(schema, required);
|
|
398
|
-
this.#onRef?.(schemaName, result);
|
|
399
|
-
return result;
|
|
400
|
-
}
|
|
401
|
-
if (this.#generatedRefs.has(schemaName)) {
|
|
402
|
-
return schemaName;
|
|
403
|
-
}
|
|
404
|
-
this.#generatedRefs.add(schemaName);
|
|
405
|
-
this.#onRef?.(schemaName, this.handle(schema, required));
|
|
406
|
-
return schemaName;
|
|
407
|
-
}
|
|
408
|
-
#toIntersection(schemas) {
|
|
409
|
-
const [left, ...right] = schemas;
|
|
410
|
-
if (!right.length) {
|
|
411
|
-
return left;
|
|
479
|
+
default:
|
|
480
|
+
console.warn(`Unknown type: ${type}`);
|
|
481
|
+
return appendOptional2("any", required);
|
|
412
482
|
}
|
|
413
|
-
return `z.intersection(${left}, ${this.#toIntersection(right)})`;
|
|
414
483
|
}
|
|
415
|
-
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
if (allOfSchemas.length === 1) {
|
|
421
|
-
return `${allOfSchemas[0]}${appendOptional2(required)}`;
|
|
484
|
+
#ref($ref, required) {
|
|
485
|
+
const schemaName = pascalcase2(sanitizeTag2(parseRef2($ref).model));
|
|
486
|
+
const schema = followRef2(this.#spec, $ref);
|
|
487
|
+
if (isPrimitiveSchema2(schema)) {
|
|
488
|
+
return this.handle(schema, required);
|
|
422
489
|
}
|
|
423
|
-
return
|
|
490
|
+
return `models.${appendOptional2(schemaName, required)}`;
|
|
424
491
|
}
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
return `${anyOfSchemas[0]}${appendOptional2(required)}`;
|
|
429
|
-
}
|
|
430
|
-
return `z.union([${anyOfSchemas.join(", ")}])${appendOptional2(required)}`;
|
|
492
|
+
allOf(schemas) {
|
|
493
|
+
const allOfTypes = schemas.map((sub) => this.handle(sub, true));
|
|
494
|
+
return allOfTypes.length > 1 ? `${allOfTypes.join(" & ")}` : allOfTypes[0];
|
|
431
495
|
}
|
|
432
496
|
oneOf(schemas, required) {
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
497
|
+
const oneOfTypes = schemas.map((sub) => this.handle(sub, true));
|
|
498
|
+
return appendOptional2(
|
|
499
|
+
oneOfTypes.length > 1 ? `${oneOfTypes.join(" | ")}` : oneOfTypes[0],
|
|
500
|
+
required
|
|
501
|
+
);
|
|
438
502
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
return `z.enum([${values.join(", ")}])`;
|
|
503
|
+
anyOf(schemas, required) {
|
|
504
|
+
return this.oneOf(schemas, required);
|
|
505
|
+
}
|
|
506
|
+
enum(values, required) {
|
|
507
|
+
const enumValues = values.map((val) => typeof val === "string" ? `'${val}'` : `${val}`).join(" | ");
|
|
508
|
+
return appendOptional2(enumValues, required);
|
|
447
509
|
}
|
|
448
510
|
/**
|
|
449
|
-
* Handle
|
|
511
|
+
* Handle string type with formats
|
|
450
512
|
*/
|
|
451
|
-
string(schema) {
|
|
452
|
-
let
|
|
513
|
+
string(schema, required) {
|
|
514
|
+
let type;
|
|
453
515
|
if (schema.contentEncoding === "binary") {
|
|
454
|
-
|
|
455
|
-
return base;
|
|
516
|
+
return appendOptional2("Blob", required);
|
|
456
517
|
}
|
|
457
518
|
switch (schema.format) {
|
|
458
519
|
case "date-time":
|
|
459
520
|
case "datetime":
|
|
460
|
-
base = "z.coerce.date()";
|
|
461
|
-
break;
|
|
462
521
|
case "date":
|
|
463
|
-
|
|
464
|
-
break;
|
|
465
|
-
case "time":
|
|
466
|
-
base = "z.string() /* optionally add .regex(...) for HH:MM:SS format */";
|
|
467
|
-
break;
|
|
468
|
-
case "email":
|
|
469
|
-
base = "z.string().email()";
|
|
470
|
-
break;
|
|
471
|
-
case "uuid":
|
|
472
|
-
base = "z.string().uuid()";
|
|
473
|
-
break;
|
|
474
|
-
case "url":
|
|
475
|
-
case "uri":
|
|
476
|
-
base = "z.string().url()";
|
|
477
|
-
break;
|
|
478
|
-
case "ipv4":
|
|
479
|
-
base = 'z.string().ip({version: "v4"})';
|
|
480
|
-
break;
|
|
481
|
-
case "ipv6":
|
|
482
|
-
base = 'z.string().ip({version: "v6"})';
|
|
483
|
-
break;
|
|
484
|
-
case "phone":
|
|
485
|
-
base = "z.string() /* or add .regex(...) for phone formats */";
|
|
522
|
+
type = "Date";
|
|
486
523
|
break;
|
|
487
|
-
case "byte":
|
|
488
524
|
case "binary":
|
|
489
|
-
|
|
525
|
+
case "byte":
|
|
526
|
+
type = "Blob";
|
|
490
527
|
break;
|
|
491
528
|
case "int64":
|
|
492
|
-
|
|
529
|
+
type = "bigint";
|
|
493
530
|
break;
|
|
494
531
|
default:
|
|
495
|
-
|
|
532
|
+
type = "string";
|
|
496
533
|
}
|
|
497
|
-
return
|
|
534
|
+
return appendOptional2(type, required);
|
|
498
535
|
}
|
|
499
536
|
/**
|
|
500
|
-
* Handle number/integer
|
|
501
|
-
* In 3.1, exclusiveMinimum/Maximum hold the actual numeric threshold,
|
|
502
|
-
* rather than a boolean toggling `minimum`/`maximum`.
|
|
537
|
+
* Handle number/integer types with formats
|
|
503
538
|
*/
|
|
504
|
-
number(schema) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (schema.format === "int64") {
|
|
508
|
-
base = "z.bigint()";
|
|
509
|
-
if (schema.default !== void 0) {
|
|
510
|
-
defaultValue = `BigInt(${schema.default})`;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
if (schema.format === "int32") {
|
|
514
|
-
base += ".int()";
|
|
515
|
-
}
|
|
516
|
-
if (typeof schema.exclusiveMinimum === "number") {
|
|
517
|
-
base += `.gt(${schema.exclusiveMinimum})`;
|
|
518
|
-
}
|
|
519
|
-
if (typeof schema.exclusiveMaximum === "number") {
|
|
520
|
-
base += `.lt(${schema.exclusiveMaximum})`;
|
|
521
|
-
}
|
|
522
|
-
if (typeof schema.minimum === "number") {
|
|
523
|
-
base += schema.format === "int64" ? `.min(BigInt(${schema.minimum}))` : `.min(${schema.minimum})`;
|
|
524
|
-
}
|
|
525
|
-
if (typeof schema.maximum === "number") {
|
|
526
|
-
base += schema.format === "int64" ? `.max(BigInt(${schema.maximum}))` : `.max(${schema.maximum})`;
|
|
527
|
-
}
|
|
528
|
-
if (typeof schema.multipleOf === "number") {
|
|
529
|
-
base += `.refine((val) => Number.isInteger(val / ${schema.multipleOf}), "Must be a multiple of ${schema.multipleOf}")`;
|
|
530
|
-
}
|
|
531
|
-
return { base, defaultValue };
|
|
539
|
+
number(schema, required) {
|
|
540
|
+
const type = schema.format === "int64" ? "bigint" : "number";
|
|
541
|
+
return appendOptional2(type, required);
|
|
532
542
|
}
|
|
533
543
|
handle(schema, required) {
|
|
534
544
|
if (isRef2(schema)) {
|
|
535
|
-
return
|
|
545
|
+
return this.#ref(schema.$ref, required);
|
|
536
546
|
}
|
|
537
547
|
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
538
|
-
return this.allOf(schema.allOf
|
|
548
|
+
return this.allOf(schema.allOf);
|
|
539
549
|
}
|
|
540
550
|
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
541
|
-
return this.anyOf(schema.anyOf
|
|
551
|
+
return this.anyOf(schema.anyOf, required);
|
|
542
552
|
}
|
|
543
|
-
if (schema.oneOf && Array.isArray(schema.oneOf)
|
|
544
|
-
return this.oneOf(schema.oneOf
|
|
553
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
554
|
+
return this.oneOf(schema.oneOf, required);
|
|
545
555
|
}
|
|
546
556
|
if (schema.enum && Array.isArray(schema.enum)) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
557
|
+
return this.enum(schema.enum, required);
|
|
558
|
+
}
|
|
559
|
+
if (schema.const) {
|
|
560
|
+
return this.enum([schema.const], true);
|
|
550
561
|
}
|
|
551
562
|
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
552
563
|
if (!types.length) {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
} else if (schema.default === null) {
|
|
558
|
-
types.push("null");
|
|
564
|
+
if ("properties" in schema) {
|
|
565
|
+
return this.object(schema, required);
|
|
566
|
+
}
|
|
567
|
+
return appendOptional2("any", required);
|
|
559
568
|
}
|
|
560
569
|
if (types.length > 1) {
|
|
561
570
|
const realTypes = types.filter((t) => t !== "null");
|
|
562
571
|
if (realTypes.length === 1 && types.includes("null")) {
|
|
563
|
-
|
|
572
|
+
const tsType = this.normal(realTypes[0], schema, false);
|
|
573
|
+
return appendOptional2(`${tsType} | null`, required);
|
|
564
574
|
}
|
|
565
|
-
const
|
|
566
|
-
return
|
|
575
|
+
const typeResults = types.map((t) => this.normal(t, schema, false));
|
|
576
|
+
return appendOptional2(typeResults.join(" | "), required);
|
|
567
577
|
}
|
|
568
|
-
return this.normal(types[0], schema, required
|
|
578
|
+
return this.normal(types[0], schema, required);
|
|
569
579
|
}
|
|
570
580
|
};
|
|
571
|
-
function appendOptional2(isRequired) {
|
|
572
|
-
return isRequired ?
|
|
581
|
+
function appendOptional2(type, isRequired) {
|
|
582
|
+
return isRequired ? type : `${type} | undefined`;
|
|
573
583
|
}
|
|
574
|
-
|
|
575
|
-
|
|
584
|
+
|
|
585
|
+
// packages/typescript/src/lib/generator.ts
|
|
586
|
+
import { merge, template } from "lodash-es";
|
|
587
|
+
import { join } from "node:path";
|
|
588
|
+
import { camelcase as camelcase2, spinalcase } from "stringcase";
|
|
589
|
+
import { followRef as followRef3, isEmpty as isEmpty2, isRef as isRef3, resolveRef } from "@sdk-it/core";
|
|
590
|
+
import { forEachOperation } from "@sdk-it/spec";
|
|
591
|
+
|
|
592
|
+
// packages/typescript/src/lib/import-utilities.ts
|
|
593
|
+
import { removeDuplicates } from "@sdk-it/core";
|
|
594
|
+
function mergeImports(...imports) {
|
|
595
|
+
const merged = {};
|
|
596
|
+
for (const it of imports) {
|
|
597
|
+
merged[it.moduleSpecifier] = merged[it.moduleSpecifier] ?? {
|
|
598
|
+
moduleSpecifier: it.moduleSpecifier,
|
|
599
|
+
defaultImport: it.defaultImport,
|
|
600
|
+
namespaceImport: it.namespaceImport,
|
|
601
|
+
namedImports: []
|
|
602
|
+
};
|
|
603
|
+
for (const named of it.namedImports) {
|
|
604
|
+
if (!merged[it.moduleSpecifier].namedImports.some(
|
|
605
|
+
(x) => x.name === named.name
|
|
606
|
+
)) {
|
|
607
|
+
merged[it.moduleSpecifier].namedImports.push(named);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return Object.values(merged);
|
|
612
|
+
}
|
|
613
|
+
function importsToString(...imports) {
|
|
614
|
+
return imports.map((it) => {
|
|
615
|
+
if (it.defaultImport) {
|
|
616
|
+
return `import ${it.defaultImport} from '${it.moduleSpecifier}'`;
|
|
617
|
+
}
|
|
618
|
+
if (it.namespaceImport) {
|
|
619
|
+
return `import * as ${it.namespaceImport} from '${it.moduleSpecifier}'`;
|
|
620
|
+
}
|
|
621
|
+
if (it.namedImports) {
|
|
622
|
+
return `import {${removeDuplicates(it.namedImports, (it2) => it2.name).map((n) => `${n.isTypeOnly ? "type" : ""} ${n.name}`).join(", ")}} from '${it.moduleSpecifier}'`;
|
|
623
|
+
}
|
|
624
|
+
throw new Error(`Invalid import ${JSON.stringify(it)}`);
|
|
625
|
+
});
|
|
576
626
|
}
|
|
577
627
|
|
|
578
628
|
// packages/typescript/src/lib/sdk.ts
|
|
@@ -613,75 +663,6 @@ var status_map_default = {
|
|
|
613
663
|
"504": "GatewayTimeout"
|
|
614
664
|
};
|
|
615
665
|
|
|
616
|
-
// packages/typescript/src/lib/utils.ts
|
|
617
|
-
import { followRef as followRef3, isRef as isRef3, removeDuplicates } from "@sdk-it/core";
|
|
618
|
-
function securityToOptions(spec, security2, securitySchemes, staticIn) {
|
|
619
|
-
securitySchemes ??= {};
|
|
620
|
-
const options = {};
|
|
621
|
-
for (const it of security2) {
|
|
622
|
-
const [name] = Object.keys(it);
|
|
623
|
-
if (!name) {
|
|
624
|
-
continue;
|
|
625
|
-
}
|
|
626
|
-
const schema = isRef3(securitySchemes[name]) ? followRef3(spec, securitySchemes[name].$ref) : securitySchemes[name];
|
|
627
|
-
if (schema.type === "http") {
|
|
628
|
-
options["authorization"] = {
|
|
629
|
-
in: staticIn ?? "header",
|
|
630
|
-
schema: "z.string().optional().transform((val) => (val ? `Bearer ${val}` : undefined))",
|
|
631
|
-
optionName: "token"
|
|
632
|
-
};
|
|
633
|
-
continue;
|
|
634
|
-
}
|
|
635
|
-
if (schema.type === "apiKey") {
|
|
636
|
-
if (!schema.in) {
|
|
637
|
-
throw new Error(`apiKey security schema must have an "in" field`);
|
|
638
|
-
}
|
|
639
|
-
if (!schema.name) {
|
|
640
|
-
throw new Error(`apiKey security schema must have a "name" field`);
|
|
641
|
-
}
|
|
642
|
-
options[schema.name] = {
|
|
643
|
-
in: staticIn ?? schema.in,
|
|
644
|
-
schema: "z.string().optional()"
|
|
645
|
-
};
|
|
646
|
-
continue;
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
return options;
|
|
650
|
-
}
|
|
651
|
-
function mergeImports(...imports) {
|
|
652
|
-
const merged = {};
|
|
653
|
-
for (const it of imports) {
|
|
654
|
-
merged[it.moduleSpecifier] = merged[it.moduleSpecifier] ?? {
|
|
655
|
-
moduleSpecifier: it.moduleSpecifier,
|
|
656
|
-
defaultImport: it.defaultImport,
|
|
657
|
-
namespaceImport: it.namespaceImport,
|
|
658
|
-
namedImports: []
|
|
659
|
-
};
|
|
660
|
-
for (const named of it.namedImports) {
|
|
661
|
-
if (!merged[it.moduleSpecifier].namedImports.some(
|
|
662
|
-
(x) => x.name === named.name
|
|
663
|
-
)) {
|
|
664
|
-
merged[it.moduleSpecifier].namedImports.push(named);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
return Object.values(merged);
|
|
669
|
-
}
|
|
670
|
-
function importsToString(...imports) {
|
|
671
|
-
return imports.map((it) => {
|
|
672
|
-
if (it.defaultImport) {
|
|
673
|
-
return `import ${it.defaultImport} from '${it.moduleSpecifier}'`;
|
|
674
|
-
}
|
|
675
|
-
if (it.namespaceImport) {
|
|
676
|
-
return `import * as ${it.namespaceImport} from '${it.moduleSpecifier}'`;
|
|
677
|
-
}
|
|
678
|
-
if (it.namedImports) {
|
|
679
|
-
return `import {${removeDuplicates(it.namedImports, (it2) => it2.name).map((n) => `${n.isTypeOnly ? "type" : ""} ${n.name}`).join(", ")}} from '${it.moduleSpecifier}'`;
|
|
680
|
-
}
|
|
681
|
-
throw new Error(`Invalid import ${JSON.stringify(it)}`);
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
|
|
685
666
|
// packages/typescript/src/lib/sdk.ts
|
|
686
667
|
function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
687
668
|
const schemaName = camelcase(`${operation.name} schema`);
|
|
@@ -953,36 +934,6 @@ function generateCode(config) {
|
|
|
953
934
|
groups[entry.groupName] ??= [];
|
|
954
935
|
endpoints[entry.groupName] ??= [];
|
|
955
936
|
const inputs = {};
|
|
956
|
-
const additionalProperties = {};
|
|
957
|
-
for (const param of operation.parameters) {
|
|
958
|
-
if (!param.schema) {
|
|
959
|
-
param.schema = {
|
|
960
|
-
type: "string"
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
inputs[param.name] = {
|
|
964
|
-
in: param.in,
|
|
965
|
-
schema: ""
|
|
966
|
-
};
|
|
967
|
-
additionalProperties[param.name] = param;
|
|
968
|
-
}
|
|
969
|
-
const securitySchemes = config.spec.components?.securitySchemes ?? {};
|
|
970
|
-
const securityOptions = securityToOptions(
|
|
971
|
-
config.spec,
|
|
972
|
-
operation.security ?? [],
|
|
973
|
-
securitySchemes
|
|
974
|
-
);
|
|
975
|
-
Object.assign(inputs, securityOptions);
|
|
976
|
-
Object.entries(securityOptions).forEach(([name, value]) => {
|
|
977
|
-
additionalProperties[name] = {
|
|
978
|
-
name,
|
|
979
|
-
required: false,
|
|
980
|
-
schema: {
|
|
981
|
-
type: "string"
|
|
982
|
-
},
|
|
983
|
-
in: value.in
|
|
984
|
-
};
|
|
985
|
-
});
|
|
986
937
|
const schemas = {};
|
|
987
938
|
const shortContenTypeMap = {
|
|
988
939
|
"application/json": "json",
|
|
@@ -1001,11 +952,12 @@ function generateCode(config) {
|
|
|
1001
952
|
config.spec,
|
|
1002
953
|
operation.requestBody.content[type].schema
|
|
1003
954
|
);
|
|
955
|
+
const xProperties = objectSchema["x-properties"] ?? {};
|
|
956
|
+
const xRequired = objectSchema["x-required"] ?? [];
|
|
1004
957
|
if (type === "application/empty") {
|
|
1005
958
|
objectSchema = {
|
|
1006
959
|
type: "object",
|
|
1007
|
-
|
|
1008
|
-
additionalProperties: isEmpty2(objectSchema["x-properties"])
|
|
960
|
+
additionalProperties: isEmpty2(xProperties)
|
|
1009
961
|
};
|
|
1010
962
|
} else {
|
|
1011
963
|
if (objectSchema.type !== "object") {
|
|
@@ -1014,14 +966,29 @@ function generateCode(config) {
|
|
|
1014
966
|
required: [operation.requestBody.required ? "$body" : ""],
|
|
1015
967
|
properties: {
|
|
1016
968
|
$body: objectSchema
|
|
1017
|
-
// ...objectSchema['x-properties'],
|
|
1018
969
|
}
|
|
1019
970
|
};
|
|
1020
971
|
}
|
|
1021
972
|
}
|
|
973
|
+
const additionalProperties = {};
|
|
974
|
+
for (const [name, prop] of Object.entries(xProperties)) {
|
|
975
|
+
additionalProperties[name] = {
|
|
976
|
+
name,
|
|
977
|
+
required: xRequired?.includes(name),
|
|
978
|
+
schema: prop,
|
|
979
|
+
in: prop["x-in"]
|
|
980
|
+
};
|
|
981
|
+
inputs[name] = {
|
|
982
|
+
in: prop["x-in"],
|
|
983
|
+
schema: ""
|
|
984
|
+
};
|
|
985
|
+
}
|
|
1022
986
|
const schema = merge({}, objectSchema, {
|
|
1023
987
|
required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
|
|
1024
|
-
properties: Object.entries(additionalProperties).reduce(
|
|
988
|
+
properties: Object.entries(additionalProperties).reduce(
|
|
989
|
+
(acc, [, p]) => ({ ...acc, [p.name]: p.schema }),
|
|
990
|
+
{}
|
|
991
|
+
)
|
|
1025
992
|
});
|
|
1026
993
|
Object.assign(inputs, bodyInputs(config.spec, objectSchema));
|
|
1027
994
|
schemas[shortContenTypeMap[type]] = zodDeserialzer.handle(schema, true);
|
|
@@ -1123,8 +1090,8 @@ ${endpoint.flatMap((it) => it.schemas).join(",\n")}
|
|
|
1123
1090
|
};
|
|
1124
1091
|
}
|
|
1125
1092
|
function toProps(spec, schemaOrRef, aggregator = []) {
|
|
1126
|
-
if (
|
|
1127
|
-
const schema =
|
|
1093
|
+
if (isRef3(schemaOrRef)) {
|
|
1094
|
+
const schema = followRef3(spec, schemaOrRef.$ref);
|
|
1128
1095
|
return toProps(spec, schema, aggregator);
|
|
1129
1096
|
} else if (schemaOrRef.type === "object") {
|
|
1130
1097
|
for (const [name] of Object.entries(schemaOrRef.properties ?? {})) {
|
|
@@ -1632,11 +1599,11 @@ import { camelcase as camelcase3, spinalcase as spinalcase2 } from "stringcase";
|
|
|
1632
1599
|
import { isEmpty as isEmpty3, pascalcase as pascalcase4, resolveRef as resolveRef2 } from "@sdk-it/core";
|
|
1633
1600
|
import {
|
|
1634
1601
|
patchParameters,
|
|
1635
|
-
securityToOptions
|
|
1602
|
+
securityToOptions
|
|
1636
1603
|
} from "@sdk-it/spec";
|
|
1637
1604
|
|
|
1638
1605
|
// packages/typescript/src/lib/emitters/snippet.ts
|
|
1639
|
-
import { followRef as
|
|
1606
|
+
import { followRef as followRef4, isRef as isRef4 } from "@sdk-it/core";
|
|
1640
1607
|
var SnippetEmitter = class {
|
|
1641
1608
|
spec;
|
|
1642
1609
|
generatedRefs = /* @__PURE__ */ new Set();
|
|
@@ -1645,12 +1612,12 @@ var SnippetEmitter = class {
|
|
|
1645
1612
|
this.spec = spec;
|
|
1646
1613
|
}
|
|
1647
1614
|
object(schema) {
|
|
1648
|
-
const schemaObj =
|
|
1615
|
+
const schemaObj = isRef4(schema) ? followRef4(this.spec, schema.$ref) : schema;
|
|
1649
1616
|
const result = {};
|
|
1650
1617
|
const properties = schemaObj.properties || {};
|
|
1651
1618
|
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
1652
1619
|
const isRequired = (schemaObj.required ?? []).includes(propName);
|
|
1653
|
-
const resolvedProp =
|
|
1620
|
+
const resolvedProp = isRef4(propSchema) ? followRef4(this.spec, propSchema.$ref) : propSchema;
|
|
1654
1621
|
if (isRequired || resolvedProp.example !== void 0 || resolvedProp.default !== void 0 || Math.random() > 0.5) {
|
|
1655
1622
|
result[propName] = this.handle(propSchema);
|
|
1656
1623
|
}
|
|
@@ -1663,7 +1630,7 @@ var SnippetEmitter = class {
|
|
|
1663
1630
|
return result;
|
|
1664
1631
|
}
|
|
1665
1632
|
array(schema) {
|
|
1666
|
-
const schemaObj =
|
|
1633
|
+
const schemaObj = isRef4(schema) ? followRef4(this.spec, schema.$ref) : schema;
|
|
1667
1634
|
const itemsSchema = schemaObj.items;
|
|
1668
1635
|
if (!itemsSchema) {
|
|
1669
1636
|
return [];
|
|
@@ -1676,10 +1643,8 @@ var SnippetEmitter = class {
|
|
|
1676
1643
|
return result;
|
|
1677
1644
|
}
|
|
1678
1645
|
string(schema) {
|
|
1679
|
-
if (schema.example !== void 0)
|
|
1680
|
-
|
|
1681
|
-
if (schema.default !== void 0)
|
|
1682
|
-
return String(schema.default);
|
|
1646
|
+
if (schema.example !== void 0) return String(schema.example);
|
|
1647
|
+
if (schema.default !== void 0) return String(schema.default);
|
|
1683
1648
|
switch (schema.format) {
|
|
1684
1649
|
case "date-time":
|
|
1685
1650
|
case "datetime":
|
|
@@ -1712,10 +1677,8 @@ var SnippetEmitter = class {
|
|
|
1712
1677
|
}
|
|
1713
1678
|
}
|
|
1714
1679
|
number(schema) {
|
|
1715
|
-
if (schema.example !== void 0)
|
|
1716
|
-
|
|
1717
|
-
if (schema.default !== void 0)
|
|
1718
|
-
return Number(schema.default);
|
|
1680
|
+
if (schema.example !== void 0) return Number(schema.example);
|
|
1681
|
+
if (schema.default !== void 0) return Number(schema.default);
|
|
1719
1682
|
let value;
|
|
1720
1683
|
if (typeof schema.exclusiveMinimum === "number") {
|
|
1721
1684
|
value = schema.exclusiveMinimum + 1;
|
|
@@ -1735,10 +1698,8 @@ var SnippetEmitter = class {
|
|
|
1735
1698
|
return schema.type === "integer" ? Math.floor(value) : value;
|
|
1736
1699
|
}
|
|
1737
1700
|
boolean(schema) {
|
|
1738
|
-
if (schema.example !== void 0)
|
|
1739
|
-
|
|
1740
|
-
if (schema.default !== void 0)
|
|
1741
|
-
return Boolean(schema.default);
|
|
1701
|
+
if (schema.example !== void 0) return Boolean(schema.example);
|
|
1702
|
+
if (schema.default !== void 0) return Boolean(schema.default);
|
|
1742
1703
|
return true;
|
|
1743
1704
|
}
|
|
1744
1705
|
null() {
|
|
@@ -1751,7 +1712,7 @@ var SnippetEmitter = class {
|
|
|
1751
1712
|
return this.cache.get($ref);
|
|
1752
1713
|
}
|
|
1753
1714
|
this.cache.set($ref, { _ref: refKey });
|
|
1754
|
-
const resolved =
|
|
1715
|
+
const resolved = followRef4(this.spec, $ref);
|
|
1755
1716
|
const result = this.handle(resolved);
|
|
1756
1717
|
this.cache.set($ref, result);
|
|
1757
1718
|
return result;
|
|
@@ -1767,23 +1728,21 @@ var SnippetEmitter = class {
|
|
|
1767
1728
|
}, initial);
|
|
1768
1729
|
}
|
|
1769
1730
|
anyOf(schemas) {
|
|
1770
|
-
if (schemas.length === 0)
|
|
1771
|
-
return {};
|
|
1731
|
+
if (schemas.length === 0) return {};
|
|
1772
1732
|
return this.handle(schemas[0]);
|
|
1773
1733
|
}
|
|
1774
1734
|
oneOf(schemas) {
|
|
1775
|
-
if (schemas.length === 0)
|
|
1776
|
-
return {};
|
|
1735
|
+
if (schemas.length === 0) return {};
|
|
1777
1736
|
return this.handle(schemas[0]);
|
|
1778
1737
|
}
|
|
1779
1738
|
enum(schema) {
|
|
1780
1739
|
return Array.isArray(schema.enum) && schema.enum.length > 0 ? schema.enum[0] : void 0;
|
|
1781
1740
|
}
|
|
1782
1741
|
handle(schemaOrRef) {
|
|
1783
|
-
if (
|
|
1742
|
+
if (isRef4(schemaOrRef)) {
|
|
1784
1743
|
return this.ref(schemaOrRef.$ref);
|
|
1785
1744
|
}
|
|
1786
|
-
const schema =
|
|
1745
|
+
const schema = isRef4(schemaOrRef) ? followRef4(this.spec, schemaOrRef.$ref) : schemaOrRef;
|
|
1787
1746
|
if (schema.example !== void 0) {
|
|
1788
1747
|
return schema.example;
|
|
1789
1748
|
}
|
|
@@ -1981,7 +1940,7 @@ var TypeScriptGenerator = class {
|
|
|
1981
1940
|
return content.join("\n");
|
|
1982
1941
|
}
|
|
1983
1942
|
#authentication() {
|
|
1984
|
-
return
|
|
1943
|
+
return securityToOptions(
|
|
1985
1944
|
this.#spec,
|
|
1986
1945
|
this.#spec.security ?? [],
|
|
1987
1946
|
this.#spec.components?.securitySchemes ?? {}
|
|
@@ -1994,7 +1953,9 @@ var TypeScriptGenerator = class {
|
|
|
1994
1953
|
const authOptions = this.#authentication();
|
|
1995
1954
|
if (!isEmpty3(authOptions)) {
|
|
1996
1955
|
const [firstAuth] = authOptions;
|
|
1997
|
-
inputs.push(
|
|
1956
|
+
inputs.push(
|
|
1957
|
+
`'${firstAuth["x-optionName"] ?? firstAuth.name}': ${firstAuth.example}`
|
|
1958
|
+
);
|
|
1998
1959
|
}
|
|
1999
1960
|
return `import { ${this.#clientName} } from '${this.#packageName}';
|
|
2000
1961
|
|
|
@@ -2014,7 +1975,7 @@ function security(spec) {
|
|
|
2014
1975
|
const components = spec.components || {};
|
|
2015
1976
|
const securitySchemes = components.securitySchemes || {};
|
|
2016
1977
|
const paths = Object.values(spec.paths ?? {});
|
|
2017
|
-
const options =
|
|
1978
|
+
const options = securityToOptions2(spec, security2, securitySchemes);
|
|
2018
1979
|
for (const it of paths) {
|
|
2019
1980
|
for (const method of methods) {
|
|
2020
1981
|
const operation = it[method];
|
|
@@ -2023,7 +1984,7 @@ function security(spec) {
|
|
|
2023
1984
|
}
|
|
2024
1985
|
Object.assign(
|
|
2025
1986
|
options,
|
|
2026
|
-
|
|
1987
|
+
securityToOptions2(
|
|
2027
1988
|
spec,
|
|
2028
1989
|
operation.security || [],
|
|
2029
1990
|
securitySchemes,
|
|
@@ -2072,7 +2033,6 @@ async function generate(openapi, settings) {
|
|
|
2072
2033
|
style,
|
|
2073
2034
|
makeImport
|
|
2074
2035
|
});
|
|
2075
|
-
const options = security(spec);
|
|
2076
2036
|
const clientName = pascalcase5((settings.name || "client").trim());
|
|
2077
2037
|
const packageName = settings.name ? `@${spinalcase3(settings.name.trim().toLowerCase())}/sdk` : "sdk";
|
|
2078
2038
|
const inputs = toInputs(groups, commonZod, makeImport);
|
|
@@ -2103,7 +2063,7 @@ ${template2(dispatcher_default, {})({ throwError: !style.errorAsValue, outputTyp
|
|
|
2103
2063
|
{
|
|
2104
2064
|
name: clientName,
|
|
2105
2065
|
servers: (spec.servers ?? []).map((server) => server.url) || [],
|
|
2106
|
-
options,
|
|
2066
|
+
options: security(spec),
|
|
2107
2067
|
makeImport
|
|
2108
2068
|
},
|
|
2109
2069
|
style
|
|
@@ -2261,6 +2221,9 @@ function serializeModels(spec) {
|
|
|
2261
2221
|
const isRequestBody = schema["x-requestbody"];
|
|
2262
2222
|
const responseGroup = schema["x-response-group"];
|
|
2263
2223
|
const stream = schema["x-stream"];
|
|
2224
|
+
if (isRequestBody) {
|
|
2225
|
+
continue;
|
|
2226
|
+
}
|
|
2264
2227
|
const folder = isResponseBody ? "outputs" : "models";
|
|
2265
2228
|
let typeContent = "ReadableStream";
|
|
2266
2229
|
if (!stream) {
|