@sdk-it/typescript 0.19.0 → 0.20.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.d.ts +2 -2
- package/dist/index.js +417 -252
- package/dist/index.js.map +4 -4
- package/dist/lib/emitters/interface.d.ts +1 -1
- package/dist/lib/emitters/interface.d.ts.map +1 -1
- package/dist/lib/emitters/zod.d.ts +1 -1
- package/dist/lib/emitters/zod.d.ts.map +1 -1
- package/dist/lib/generate.d.ts +1 -0
- package/dist/lib/generate.d.ts.map +1 -1
- package/dist/lib/generator.d.ts.map +1 -1
- package/dist/lib/readme.d.ts +19 -0
- package/dist/lib/readme.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,137 +1,16 @@
|
|
|
1
1
|
// packages/typescript/src/lib/generate.ts
|
|
2
2
|
import { template as template2 } from "lodash-es";
|
|
3
|
-
import { join as
|
|
3
|
+
import { join as join2 } from "node:path";
|
|
4
4
|
import { npmRunPathEnv } from "npm-run-path";
|
|
5
5
|
import { spinalcase as spinalcase3 } from "stringcase";
|
|
6
|
-
|
|
7
|
-
// packages/core/dist/index.js
|
|
6
|
+
import { methods, pascalcase as pascalcase3 } from "@sdk-it/core";
|
|
8
7
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "stringcase";
|
|
13
|
-
import ts, { TypeFlags, symbolName } from "typescript";
|
|
14
|
-
import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
15
|
-
import { dirname, extname, isAbsolute, join } from "node:path";
|
|
16
|
-
import debug from "debug";
|
|
17
|
-
import ts2 from "typescript";
|
|
18
|
-
import { get } from "lodash-es";
|
|
19
|
-
var deriveSymbol = Symbol.for("serialize");
|
|
20
|
-
var $types = Symbol.for("types");
|
|
21
|
-
async function exist(file) {
|
|
22
|
-
return stat(file).then(() => true).catch(() => false);
|
|
23
|
-
}
|
|
24
|
-
async function writeFiles(dir, contents) {
|
|
25
|
-
await Promise.all(
|
|
26
|
-
Object.entries(contents).map(async ([file, content]) => {
|
|
27
|
-
if (content === null) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const filePath = isAbsolute(file) ? file : join(dir, file);
|
|
31
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
32
|
-
if (typeof content === "string") {
|
|
33
|
-
await writeFile(filePath, content, "utf-8");
|
|
34
|
-
} else {
|
|
35
|
-
if (content.ignoreIfExists) {
|
|
36
|
-
if (!await exist(filePath)) {
|
|
37
|
-
await writeFile(filePath, content.content, "utf-8");
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
await writeFile(filePath, content.content, "utf-8");
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
async function getFolderExports(folder, includeExtension = true, extensions = ["ts"], ignore = () => false) {
|
|
47
|
-
const files = await readdir(folder, { withFileTypes: true });
|
|
48
|
-
const exports = [];
|
|
49
|
-
for (const file of files) {
|
|
50
|
-
if (ignore(file)) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (file.isDirectory()) {
|
|
54
|
-
if (await exist(`${file.parentPath}/${file.name}/index.ts`)) {
|
|
55
|
-
exports.push(
|
|
56
|
-
`export * from './${file.name}/index${includeExtension ? ".ts" : ""}';`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
} else if (file.name !== "index.ts" && extensions.includes(getExt(file.name))) {
|
|
60
|
-
exports.push(
|
|
61
|
-
`export * from './${includeExtension ? file.name : file.name.replace(extname(file.name), "")}';`
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return exports.join("\n");
|
|
66
|
-
}
|
|
67
|
-
var getExt = (fileName) => {
|
|
68
|
-
if (!fileName) {
|
|
69
|
-
return "";
|
|
70
|
-
}
|
|
71
|
-
const lastDot = fileName.lastIndexOf(".");
|
|
72
|
-
if (lastDot === -1) {
|
|
73
|
-
return "";
|
|
74
|
-
}
|
|
75
|
-
const ext = fileName.slice(lastDot + 1).split("/").filter(Boolean).join("");
|
|
76
|
-
if (ext === fileName) {
|
|
77
|
-
return "";
|
|
78
|
-
}
|
|
79
|
-
return ext || "txt";
|
|
80
|
-
};
|
|
81
|
-
var methods = [
|
|
82
|
-
"get",
|
|
83
|
-
"post",
|
|
84
|
-
"put",
|
|
85
|
-
"patch",
|
|
86
|
-
"delete",
|
|
87
|
-
"trace",
|
|
88
|
-
"head"
|
|
89
|
-
];
|
|
90
|
-
var logger = debug("january:client");
|
|
91
|
-
function isRef(obj) {
|
|
92
|
-
return obj && "$ref" in obj;
|
|
93
|
-
}
|
|
94
|
-
function cleanRef(ref) {
|
|
95
|
-
return ref.replace(/^#\//, "");
|
|
96
|
-
}
|
|
97
|
-
function parseRef(ref) {
|
|
98
|
-
const parts = ref.split("/");
|
|
99
|
-
const [model] = parts.splice(-1);
|
|
100
|
-
const [namespace] = parts.splice(-1);
|
|
101
|
-
return {
|
|
102
|
-
model,
|
|
103
|
-
namespace,
|
|
104
|
-
path: cleanRef(parts.join("/"))
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
function followRef(spec, ref) {
|
|
108
|
-
const pathParts = cleanRef(ref).split("/");
|
|
109
|
-
const entry = get(spec, pathParts);
|
|
110
|
-
if (entry && "$ref" in entry) {
|
|
111
|
-
return followRef(spec, entry.$ref);
|
|
112
|
-
}
|
|
113
|
-
return entry;
|
|
114
|
-
}
|
|
115
|
-
function removeDuplicates(data, accessor = (item) => item) {
|
|
116
|
-
return [...new Map(data.map((x) => [accessor(x), x])).values()];
|
|
117
|
-
}
|
|
118
|
-
function toLitObject(obj, accessor = (value) => value) {
|
|
119
|
-
return `{${Object.keys(obj).map((key) => `${key}: ${accessor(obj[key])}`).join(", ")}}`;
|
|
120
|
-
}
|
|
121
|
-
function isEmpty(value) {
|
|
122
|
-
if (value === null || value === void 0 || value === "") {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
if (Array.isArray(value) && value.length === 0) {
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
if (typeof value === "object" && Object.keys(value).length === 0) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
8
|
+
getFolderExports,
|
|
9
|
+
writeFiles
|
|
10
|
+
} from "@sdk-it/core/file-system.js";
|
|
133
11
|
|
|
134
12
|
// packages/typescript/src/lib/client.ts
|
|
13
|
+
import { toLitObject } from "@sdk-it/core";
|
|
135
14
|
var client_default = (spec, style) => {
|
|
136
15
|
const optionsEntries = Object.entries(spec.options).map(
|
|
137
16
|
([key, value]) => [`'${key}'`, value]
|
|
@@ -255,11 +134,13 @@ export class ${spec.name} {
|
|
|
255
134
|
|
|
256
135
|
// packages/typescript/src/lib/generator.ts
|
|
257
136
|
import { merge, template } from "lodash-es";
|
|
258
|
-
import { join
|
|
137
|
+
import { join } from "node:path";
|
|
259
138
|
import { camelcase as camelcase3, pascalcase as pascalcase2, spinalcase as spinalcase2 } from "stringcase";
|
|
139
|
+
import { followRef as followRef5, isEmpty, isRef as isRef6 } from "@sdk-it/core";
|
|
260
140
|
|
|
261
141
|
// packages/spec/dist/lib/operation.js
|
|
262
142
|
import { camelcase } from "stringcase";
|
|
143
|
+
import { followRef, isRef } from "@sdk-it/core";
|
|
263
144
|
var defaults = {
|
|
264
145
|
operationId: (operation, path, method) => {
|
|
265
146
|
if (operation.operationId) {
|
|
@@ -274,9 +155,18 @@ var defaults = {
|
|
|
274
155
|
);
|
|
275
156
|
},
|
|
276
157
|
tag: (operation, path) => {
|
|
277
|
-
return operation.tags?.[0]
|
|
158
|
+
return operation.tags?.[0] ? sanitizeTag(operation.tags?.[0]) : determineGenericTag(path, operation);
|
|
278
159
|
}
|
|
279
160
|
};
|
|
161
|
+
function resolveResponses(spec, operation) {
|
|
162
|
+
const responses = operation.responses ?? {};
|
|
163
|
+
const resolved = {};
|
|
164
|
+
for (const status in responses) {
|
|
165
|
+
const response = isRef(responses[status]) ? followRef(spec, responses[status].$ref) : responses[status];
|
|
166
|
+
resolved[status] = response;
|
|
167
|
+
}
|
|
168
|
+
return resolved;
|
|
169
|
+
}
|
|
280
170
|
function forEachOperation(config, callback) {
|
|
281
171
|
const result = [];
|
|
282
172
|
for (const [path, pathItem] of Object.entries(config.spec.paths ?? {})) {
|
|
@@ -300,7 +190,8 @@ function forEachOperation(config, callback) {
|
|
|
300
190
|
{
|
|
301
191
|
...operation,
|
|
302
192
|
parameters: [...parameters, ...operation.parameters ?? []],
|
|
303
|
-
operationId: operationName
|
|
193
|
+
operationId: operationName,
|
|
194
|
+
responses: resolveResponses(config.spec, operation)
|
|
304
195
|
}
|
|
305
196
|
)
|
|
306
197
|
);
|
|
@@ -309,15 +200,11 @@ function forEachOperation(config, callback) {
|
|
|
309
200
|
return result;
|
|
310
201
|
}
|
|
311
202
|
var reservedKeywords = /* @__PURE__ */ new Set([
|
|
312
|
-
"abstract",
|
|
313
|
-
"arguments",
|
|
314
203
|
"await",
|
|
315
|
-
|
|
204
|
+
// Reserved in async functions
|
|
316
205
|
"break",
|
|
317
|
-
"byte",
|
|
318
206
|
"case",
|
|
319
207
|
"catch",
|
|
320
|
-
"char",
|
|
321
208
|
"class",
|
|
322
209
|
"const",
|
|
323
210
|
"continue",
|
|
@@ -325,85 +212,59 @@ var reservedKeywords = /* @__PURE__ */ new Set([
|
|
|
325
212
|
"default",
|
|
326
213
|
"delete",
|
|
327
214
|
"do",
|
|
328
|
-
"double",
|
|
329
215
|
"else",
|
|
330
216
|
"enum",
|
|
331
|
-
"eval",
|
|
332
217
|
"export",
|
|
333
218
|
"extends",
|
|
334
219
|
"false",
|
|
335
|
-
"final",
|
|
336
220
|
"finally",
|
|
337
|
-
"float",
|
|
338
221
|
"for",
|
|
339
222
|
"function",
|
|
340
|
-
"goto",
|
|
341
223
|
"if",
|
|
342
224
|
"implements",
|
|
225
|
+
// Strict mode
|
|
343
226
|
"import",
|
|
344
227
|
"in",
|
|
345
228
|
"instanceof",
|
|
346
|
-
"int",
|
|
347
229
|
"interface",
|
|
230
|
+
// Strict mode
|
|
348
231
|
"let",
|
|
349
|
-
|
|
350
|
-
"native",
|
|
232
|
+
// Strict mode
|
|
351
233
|
"new",
|
|
352
234
|
"null",
|
|
353
235
|
"package",
|
|
236
|
+
// Strict mode
|
|
354
237
|
"private",
|
|
238
|
+
// Strict mode
|
|
355
239
|
"protected",
|
|
240
|
+
// Strict mode
|
|
356
241
|
"public",
|
|
242
|
+
// Strict mode
|
|
357
243
|
"return",
|
|
358
|
-
"short",
|
|
359
244
|
"static",
|
|
245
|
+
// Strict mode
|
|
360
246
|
"super",
|
|
361
247
|
"switch",
|
|
362
|
-
"synchronized",
|
|
363
248
|
"this",
|
|
364
249
|
"throw",
|
|
365
|
-
"throws",
|
|
366
|
-
"transient",
|
|
367
250
|
"true",
|
|
368
251
|
"try",
|
|
369
252
|
"typeof",
|
|
370
253
|
"var",
|
|
371
254
|
"void",
|
|
372
|
-
"volatile",
|
|
373
255
|
"while",
|
|
374
256
|
"with",
|
|
375
257
|
"yield",
|
|
376
|
-
//
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
"
|
|
380
|
-
"any",
|
|
381
|
-
"unknown",
|
|
382
|
-
"never",
|
|
383
|
-
"get",
|
|
384
|
-
"list",
|
|
385
|
-
"create",
|
|
386
|
-
"update",
|
|
387
|
-
"delete",
|
|
388
|
-
"post",
|
|
389
|
-
"put",
|
|
390
|
-
"patch",
|
|
391
|
-
"do",
|
|
392
|
-
"send",
|
|
393
|
-
"add",
|
|
394
|
-
"remove",
|
|
395
|
-
"set",
|
|
396
|
-
"find",
|
|
397
|
-
"search",
|
|
398
|
-
"check",
|
|
399
|
-
"make"
|
|
400
|
-
// Added make, check
|
|
258
|
+
// Strict mode / Generator functions
|
|
259
|
+
// 'arguments' is not technically a reserved word, but it's a special identifier within functions
|
|
260
|
+
// and assigning to it or declaring it can cause issues or unexpected behavior.
|
|
261
|
+
"arguments"
|
|
401
262
|
]);
|
|
402
263
|
function sanitizeTag(camelCasedTag) {
|
|
403
264
|
if (/^\d/.test(camelCasedTag)) {
|
|
404
265
|
return `_${camelCasedTag}`;
|
|
405
266
|
}
|
|
406
|
-
return reservedKeywords.has(camelCasedTag) ? `${camelCasedTag}_` : camelCasedTag;
|
|
267
|
+
return reservedKeywords.has(camelcase(camelCasedTag)) ? `${camelCasedTag}_` : camelCasedTag;
|
|
407
268
|
}
|
|
408
269
|
function determineGenericTag(pathString, operation) {
|
|
409
270
|
const operationId = operation.operationId || "";
|
|
@@ -427,7 +288,6 @@ function determineGenericTag(pathString, operation) {
|
|
|
427
288
|
"search",
|
|
428
289
|
"check",
|
|
429
290
|
"make"
|
|
430
|
-
// Added make
|
|
431
291
|
]);
|
|
432
292
|
const segments = pathString.split("/").filter(Boolean);
|
|
433
293
|
const potentialCandidates = segments.filter(
|
|
@@ -521,8 +381,9 @@ function determineGenericTag(pathString, operation) {
|
|
|
521
381
|
}
|
|
522
382
|
|
|
523
383
|
// packages/typescript/src/lib/emitters/zod.ts
|
|
384
|
+
import { cleanRef, followRef as followRef2, isRef as isRef2 } from "@sdk-it/core";
|
|
524
385
|
var ZodDeserialzer = class {
|
|
525
|
-
|
|
386
|
+
generatedRefs = /* @__PURE__ */ new Set();
|
|
526
387
|
#spec;
|
|
527
388
|
#onRef;
|
|
528
389
|
constructor(spec, onRef) {
|
|
@@ -596,15 +457,14 @@ var ZodDeserialzer = class {
|
|
|
596
457
|
}
|
|
597
458
|
ref($ref, required) {
|
|
598
459
|
const schemaName = cleanRef($ref).split("/").pop();
|
|
599
|
-
if (this.
|
|
460
|
+
if (this.generatedRefs.has(schemaName)) {
|
|
600
461
|
return schemaName;
|
|
601
462
|
}
|
|
602
|
-
this.
|
|
463
|
+
this.generatedRefs.add(schemaName);
|
|
603
464
|
this.#onRef?.(
|
|
604
465
|
schemaName,
|
|
605
|
-
this.handle(
|
|
466
|
+
this.handle(followRef2(this.#spec, $ref), required)
|
|
606
467
|
);
|
|
607
|
-
this.circularRefTracker.delete(schemaName);
|
|
608
468
|
return schemaName;
|
|
609
469
|
}
|
|
610
470
|
allOf(schemas, required) {
|
|
@@ -632,15 +492,7 @@ var ZodDeserialzer = class {
|
|
|
632
492
|
return `z.union([${anyOfSchemas.join(", ")}])${appendOptional(required)}`;
|
|
633
493
|
}
|
|
634
494
|
oneOf(schemas, required) {
|
|
635
|
-
const oneOfSchemas = schemas.map((sub) =>
|
|
636
|
-
if (isRef(sub)) {
|
|
637
|
-
const { model } = parseRef(sub.$ref);
|
|
638
|
-
if (this.circularRefTracker.has(model)) {
|
|
639
|
-
return `${model}${appendOptional(required)}`;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
return this.handle(sub, true);
|
|
643
|
-
});
|
|
495
|
+
const oneOfSchemas = schemas.map((sub) => this.handle(sub, true));
|
|
644
496
|
if (oneOfSchemas.length === 1) {
|
|
645
497
|
return `${oneOfSchemas[0]}${appendOptional(required)}`;
|
|
646
498
|
}
|
|
@@ -660,6 +512,10 @@ var ZodDeserialzer = class {
|
|
|
660
512
|
*/
|
|
661
513
|
string(schema) {
|
|
662
514
|
let base = "z.string()";
|
|
515
|
+
if (schema.contentEncoding === "binary") {
|
|
516
|
+
base = "z.instanceof(Blob)";
|
|
517
|
+
return base;
|
|
518
|
+
}
|
|
663
519
|
switch (schema.format) {
|
|
664
520
|
case "date-time":
|
|
665
521
|
case "datetime":
|
|
@@ -737,7 +593,7 @@ var ZodDeserialzer = class {
|
|
|
737
593
|
return { base, defaultValue };
|
|
738
594
|
}
|
|
739
595
|
handle(schema, required) {
|
|
740
|
-
if (
|
|
596
|
+
if (isRef2(schema)) {
|
|
741
597
|
return `${this.ref(schema.$ref, true)}${appendOptional(required)}`;
|
|
742
598
|
}
|
|
743
599
|
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
@@ -782,12 +638,14 @@ function appendDefault(defaultValue) {
|
|
|
782
638
|
}
|
|
783
639
|
|
|
784
640
|
// packages/typescript/src/lib/sdk.ts
|
|
785
|
-
import { get
|
|
641
|
+
import { get } from "lodash-es";
|
|
786
642
|
import { camelcase as camelcase2, pascalcase, spinalcase } from "stringcase";
|
|
643
|
+
import { followRef as followRef4, isRef as isRef5, toLitObject as toLitObject2 } from "@sdk-it/core";
|
|
787
644
|
|
|
788
645
|
// packages/typescript/src/lib/emitters/interface.ts
|
|
646
|
+
import { cleanRef as cleanRef2, followRef as followRef3, isRef as isRef3 } from "@sdk-it/core";
|
|
789
647
|
var TypeScriptDeserialzer = class {
|
|
790
|
-
|
|
648
|
+
generatedRefs = /* @__PURE__ */ new Set();
|
|
791
649
|
#spec;
|
|
792
650
|
#onRef;
|
|
793
651
|
constructor(spec, onRef) {
|
|
@@ -798,7 +656,7 @@ var TypeScriptDeserialzer = class {
|
|
|
798
656
|
return `'${value}'`;
|
|
799
657
|
};
|
|
800
658
|
#isInternal = (schema) => {
|
|
801
|
-
return
|
|
659
|
+
return isRef3(schema) ? false : !!schema["x-internal"];
|
|
802
660
|
};
|
|
803
661
|
/**
|
|
804
662
|
* Handle objects (properties)
|
|
@@ -859,16 +717,15 @@ var TypeScriptDeserialzer = class {
|
|
|
859
717
|
}
|
|
860
718
|
}
|
|
861
719
|
ref($ref, required) {
|
|
862
|
-
const schemaName =
|
|
863
|
-
if (this.
|
|
720
|
+
const schemaName = cleanRef2($ref).split("/").pop();
|
|
721
|
+
if (this.generatedRefs.has(schemaName)) {
|
|
864
722
|
return schemaName;
|
|
865
723
|
}
|
|
866
|
-
this.
|
|
724
|
+
this.generatedRefs.add(schemaName);
|
|
867
725
|
this.#onRef?.(
|
|
868
726
|
schemaName,
|
|
869
|
-
this.handle(
|
|
727
|
+
this.handle(followRef3(this.#spec, $ref), required)
|
|
870
728
|
);
|
|
871
|
-
this.circularRefTracker.delete(schemaName);
|
|
872
729
|
return appendOptional2(schemaName, required);
|
|
873
730
|
}
|
|
874
731
|
allOf(schemas) {
|
|
@@ -884,12 +741,6 @@ var TypeScriptDeserialzer = class {
|
|
|
884
741
|
}
|
|
885
742
|
oneOf(schemas, required) {
|
|
886
743
|
const oneOfTypes = schemas.map((sub) => {
|
|
887
|
-
if (isRef(sub)) {
|
|
888
|
-
const { model } = parseRef(sub.$ref);
|
|
889
|
-
if (this.circularRefTracker.has(model)) {
|
|
890
|
-
return model;
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
744
|
return this.handle(sub, false);
|
|
894
745
|
});
|
|
895
746
|
return appendOptional2(
|
|
@@ -906,6 +757,9 @@ var TypeScriptDeserialzer = class {
|
|
|
906
757
|
*/
|
|
907
758
|
string(schema, required) {
|
|
908
759
|
let type;
|
|
760
|
+
if (schema.contentEncoding === "binary") {
|
|
761
|
+
return appendOptional2("Blob", required);
|
|
762
|
+
}
|
|
909
763
|
switch (schema.format) {
|
|
910
764
|
case "date-time":
|
|
911
765
|
case "datetime":
|
|
@@ -932,7 +786,7 @@ var TypeScriptDeserialzer = class {
|
|
|
932
786
|
return appendOptional2(type, required);
|
|
933
787
|
}
|
|
934
788
|
handle(schema, required) {
|
|
935
|
-
if (
|
|
789
|
+
if (isRef3(schema)) {
|
|
936
790
|
return this.ref(schema.$ref, required);
|
|
937
791
|
}
|
|
938
792
|
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
@@ -977,6 +831,7 @@ function appendOptional2(type, isRequired) {
|
|
|
977
831
|
}
|
|
978
832
|
|
|
979
833
|
// packages/typescript/src/lib/utils.ts
|
|
834
|
+
import { isRef as isRef4, removeDuplicates } from "@sdk-it/core";
|
|
980
835
|
function securityToOptions(security2, securitySchemes, staticIn) {
|
|
981
836
|
securitySchemes ??= {};
|
|
982
837
|
const options = {};
|
|
@@ -986,7 +841,7 @@ function securityToOptions(security2, securitySchemes, staticIn) {
|
|
|
986
841
|
continue;
|
|
987
842
|
}
|
|
988
843
|
const schema = securitySchemes[name];
|
|
989
|
-
if (
|
|
844
|
+
if (isRef4(schema)) {
|
|
990
845
|
throw new Error(`Ref security schemas are not supported`);
|
|
991
846
|
}
|
|
992
847
|
if (schema.type === "http") {
|
|
@@ -1075,7 +930,7 @@ function generateInputs(operationsSet, commonZod, makeImport) {
|
|
|
1075
930
|
const imports = /* @__PURE__ */ new Set(['import { z } from "zod";']);
|
|
1076
931
|
for (const operation of operations) {
|
|
1077
932
|
const schemaName = camelcase2(`${operation.name} schema`);
|
|
1078
|
-
const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] :
|
|
933
|
+
const schema = `export const ${schemaName} = ${Object.keys(operation.schemas).length === 1 ? Object.values(operation.schemas)[0] : toLitObject2(operation.schemas)};`;
|
|
1079
934
|
const inputContent = schema;
|
|
1080
935
|
for (const schema2 of commonImports) {
|
|
1081
936
|
if (inputContent.includes(schema2)) {
|
|
@@ -1145,7 +1000,7 @@ function toEndpoint(groupName, spec, specOperation, operation, utils) {
|
|
|
1145
1000
|
return statusCode >= 200 && statusCode < 300;
|
|
1146
1001
|
}).length > 1;
|
|
1147
1002
|
for (const status in specOperation.responses) {
|
|
1148
|
-
const response =
|
|
1003
|
+
const response = isRef5(specOperation.responses[status]) ? followRef4(spec, specOperation.responses[status].$ref) : specOperation.responses[status];
|
|
1149
1004
|
const handled = handleResponse(
|
|
1150
1005
|
spec,
|
|
1151
1006
|
operation.name,
|
|
@@ -1250,12 +1105,12 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
|
|
|
1250
1105
|
);
|
|
1251
1106
|
}
|
|
1252
1107
|
}
|
|
1253
|
-
const responseContent =
|
|
1108
|
+
const responseContent = get(response, ["content"]);
|
|
1254
1109
|
const isJson = responseContent && responseContent["application/json"];
|
|
1255
1110
|
let responseSchema = parser === "chunked" ? "ReadableStream" : "void";
|
|
1256
1111
|
if (isJson) {
|
|
1257
1112
|
const schema = responseContent["application/json"].schema;
|
|
1258
|
-
const isObject = !
|
|
1113
|
+
const isObject = !isRef5(schema) && schema.type === "object";
|
|
1259
1114
|
if (isObject && schema.properties) {
|
|
1260
1115
|
schema.properties["[http.KIND]"] = {
|
|
1261
1116
|
"x-internal": true,
|
|
@@ -1263,7 +1118,7 @@ function handleResponse(spec, operationName, status, response, utils, numbered)
|
|
|
1263
1118
|
type: "string"
|
|
1264
1119
|
};
|
|
1265
1120
|
schema.required ??= [];
|
|
1266
|
-
schema.required.push("[KIND]");
|
|
1121
|
+
schema.required.push("[http.KIND]");
|
|
1267
1122
|
}
|
|
1268
1123
|
responseSchema = typeScriptDeserialzer.handle(schema, true);
|
|
1269
1124
|
}
|
|
@@ -1320,9 +1175,9 @@ function generateCode(config) {
|
|
|
1320
1175
|
groups[entry.groupName] ??= [];
|
|
1321
1176
|
endpoints[entry.groupName] ??= [];
|
|
1322
1177
|
const inputs = {};
|
|
1323
|
-
const additionalProperties =
|
|
1178
|
+
const additionalProperties = {};
|
|
1324
1179
|
for (const param of operation.parameters ?? []) {
|
|
1325
|
-
if (
|
|
1180
|
+
if (isRef6(param)) {
|
|
1326
1181
|
throw new Error(`Found reference in parameter ${param.$ref}`);
|
|
1327
1182
|
}
|
|
1328
1183
|
if (!param.schema) {
|
|
@@ -1332,24 +1187,22 @@ function generateCode(config) {
|
|
|
1332
1187
|
in: param.in,
|
|
1333
1188
|
schema: ""
|
|
1334
1189
|
};
|
|
1335
|
-
additionalProperties.
|
|
1190
|
+
additionalProperties[param.name] = param;
|
|
1336
1191
|
}
|
|
1337
1192
|
const security2 = operation.security ?? [];
|
|
1338
1193
|
const securitySchemes = config.spec.components?.securitySchemes ?? {};
|
|
1339
1194
|
const securityOptions = securityToOptions(security2, securitySchemes);
|
|
1340
1195
|
Object.assign(inputs, securityOptions);
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
)
|
|
1352
|
-
);
|
|
1196
|
+
Object.entries(securityOptions).forEach(([name, value]) => {
|
|
1197
|
+
additionalProperties[name] = {
|
|
1198
|
+
name,
|
|
1199
|
+
required: false,
|
|
1200
|
+
schema: {
|
|
1201
|
+
type: "string"
|
|
1202
|
+
},
|
|
1203
|
+
in: value.in
|
|
1204
|
+
};
|
|
1205
|
+
});
|
|
1353
1206
|
const schemas = {};
|
|
1354
1207
|
const shortContenTypeMap = {
|
|
1355
1208
|
"application/json": "json",
|
|
@@ -1364,9 +1217,9 @@ function generateCode(config) {
|
|
|
1364
1217
|
};
|
|
1365
1218
|
let outgoingContentType;
|
|
1366
1219
|
if (!isEmpty(operation.requestBody)) {
|
|
1367
|
-
const requestBody =
|
|
1220
|
+
const requestBody = isRef6(operation.requestBody) ? followRef5(config.spec, operation.requestBody.$ref) : operation.requestBody;
|
|
1368
1221
|
for (const type in requestBody.content) {
|
|
1369
|
-
const ctSchema =
|
|
1222
|
+
const ctSchema = isRef6(requestBody.content[type].schema) ? followRef5(config.spec, requestBody.content[type].schema.$ref) : requestBody.content[type].schema;
|
|
1370
1223
|
if (!ctSchema) {
|
|
1371
1224
|
console.warn(
|
|
1372
1225
|
`Schema not found for ${type} in ${entry.method} ${entry.path}`
|
|
@@ -1384,9 +1237,9 @@ function generateCode(config) {
|
|
|
1384
1237
|
};
|
|
1385
1238
|
}
|
|
1386
1239
|
const schema = merge({}, objectSchema, {
|
|
1387
|
-
required: additionalProperties.filter((p) => p.required).map((p) => p.name),
|
|
1388
|
-
properties: additionalProperties.reduce(
|
|
1389
|
-
(acc, p) => ({
|
|
1240
|
+
required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
|
|
1241
|
+
properties: Object.entries(additionalProperties).reduce(
|
|
1242
|
+
(acc, [, p]) => ({
|
|
1390
1243
|
...acc,
|
|
1391
1244
|
[p.name]: p.schema
|
|
1392
1245
|
}),
|
|
@@ -1406,8 +1259,8 @@ function generateCode(config) {
|
|
|
1406
1259
|
outgoingContentType = "json";
|
|
1407
1260
|
}
|
|
1408
1261
|
} else {
|
|
1409
|
-
const properties = additionalProperties.reduce(
|
|
1410
|
-
(acc, p) => ({
|
|
1262
|
+
const properties = Object.entries(additionalProperties).reduce(
|
|
1263
|
+
(acc, [, p]) => ({
|
|
1411
1264
|
...acc,
|
|
1412
1265
|
[p.name]: p.schema
|
|
1413
1266
|
}),
|
|
@@ -1416,7 +1269,7 @@ function generateCode(config) {
|
|
|
1416
1269
|
schemas[shortContenTypeMap["application/json"]] = zodDeserialzer.handle(
|
|
1417
1270
|
{
|
|
1418
1271
|
type: "object",
|
|
1419
|
-
required: additionalProperties.filter((p) => p.required).map((p) => p.name),
|
|
1272
|
+
required: Object.values(additionalProperties).filter((p) => p.required).map((p) => p.name),
|
|
1420
1273
|
properties
|
|
1421
1274
|
},
|
|
1422
1275
|
true
|
|
@@ -1503,7 +1356,7 @@ function generateCode(config) {
|
|
|
1503
1356
|
commonZod,
|
|
1504
1357
|
outputs,
|
|
1505
1358
|
endpoints: {
|
|
1506
|
-
[
|
|
1359
|
+
[join("api", "endpoints.ts")]: `
|
|
1507
1360
|
|
|
1508
1361
|
|
|
1509
1362
|
import type z from 'zod';
|
|
@@ -1518,7 +1371,7 @@ import type { OutputType, Parser, Type } from '${config.makeImport(
|
|
|
1518
1371
|
import schemas from '${config.makeImport("./schemas")}';
|
|
1519
1372
|
|
|
1520
1373
|
${template(endpoints_default)({ outputType: config.style?.outputType })}`,
|
|
1521
|
-
[`${
|
|
1374
|
+
[`${join("api", "schemas.ts")}`]: `${allSchemas.map((it) => it.import).join("\n")}
|
|
1522
1375
|
import { KIND } from "${config.makeImport("../http/index")}";
|
|
1523
1376
|
export default {
|
|
1524
1377
|
${allSchemas.map((it) => it.use).join(",\n")}
|
|
@@ -1538,10 +1391,9 @@ ${allSchemas.map((it) => it.use).join(",\n")}
|
|
|
1538
1391
|
);
|
|
1539
1392
|
return [
|
|
1540
1393
|
[
|
|
1541
|
-
|
|
1394
|
+
join("api", `${spinalcase2(name)}.ts`),
|
|
1542
1395
|
`${[
|
|
1543
1396
|
...imps,
|
|
1544
|
-
// ...imports,
|
|
1545
1397
|
`import z from 'zod';`,
|
|
1546
1398
|
`import * as http from '${config.makeImport("../http/response")}';`,
|
|
1547
1399
|
`import { toRequest, json, urlencoded, nobody, formdata, createUrl } from '${config.makeImport("../http/request")}';`,
|
|
@@ -1561,8 +1413,8 @@ ${endpoint.flatMap((it) => it.schemas).join(",\n")}
|
|
|
1561
1413
|
};
|
|
1562
1414
|
}
|
|
1563
1415
|
function toProps(spec, schemaOrRef, aggregator = []) {
|
|
1564
|
-
if (
|
|
1565
|
-
const schema =
|
|
1416
|
+
if (isRef6(schemaOrRef)) {
|
|
1417
|
+
const schema = followRef5(spec, schemaOrRef.$ref);
|
|
1566
1418
|
return toProps(spec, schema, aggregator);
|
|
1567
1419
|
} else if (schemaOrRef.type === "object") {
|
|
1568
1420
|
for (const [name] of Object.entries(schemaOrRef.properties ?? {})) {
|
|
@@ -2032,6 +1884,311 @@ export type SuccessfulResponse =
|
|
|
2032
1884
|
// packages/typescript/src/lib/http/send-request.txt
|
|
2033
1885
|
var send_request_default = "export interface Type<T> {\n new (...args: any[]): T;\n}\nexport type Parser = (\n response: Response,\n) => Promise<unknown> | ReadableStream<any>;\nexport type OutputType =\n | Type<APIResponse>\n | { parser: Parser; type: Type<APIResponse> };\n\nexport interface RequestSchema {\n schema: z.ZodType;\n toRequest: (input: any) => RequestConfig;\n output: OutputType[];\n}\n\nexport const fetchType = z\n .function()\n .args(z.instanceof(Request))\n .returns(z.promise(z.instanceof(Response)))\n .optional();\n\nexport async function dispatch(\n input: unknown,\n route: RequestSchema,\n options: {\n fetch?: z.infer<typeof fetchType>;\n interceptors?: Interceptor[];\n signal?: AbortSignal;\n },\n) {\n const { interceptors = [] } = options;\n const [parsedInput, parseError] = parseInput(route.schema, input);\n if (parseError) {\n <% if(throwError) { %>\n throw parseError;\n <% } else { %>\n return [null as never, parseError as never] as const;\n <% } %>\n }\n\n let config = route.toRequest(parsedInput as never);\n for (const interceptor of interceptors) {\n if (interceptor.before) {\n config = await interceptor.before(config);\n }\n }\n\n let response = await (options.fetch ?? fetch)(\n new Request(config.url, config.init),\n {\n ...config.init,\n signal: options.signal,\n },\n );\n\n for (let i = interceptors.length - 1; i >= 0; i--) {\n const interceptor = interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n return await parse(route, response);\n}\n\nexport async function parse(route: RequestSchema, response: Response) {\n let output: typeof APIResponse | null = null;\n let parser: Parser = buffered;\n for (const outputType of route.output) {\n if ('parser' in outputType) {\n parser = outputType.parser;\n if (isTypeOf(outputType.type, APIResponse)) {\n if (response.status === outputType.type.status) {\n output = outputType.type;\n break;\n }\n }\n } else if (isTypeOf(outputType, APIResponse)) {\n if (response.status === outputType.status) {\n output = outputType;\n break;\n }\n }\n }\n\n if (response.ok) {\n const apiresponse = (output || APIResponse).create(\n response.status,\n await parser(response),\n );\n <% if(throwError) { %>\n return <% if (outputType === 'default') { %>apiresponse.data<% } else { %>apiresponse<% } %>;\n <% } else { %>\n return [<% if (outputType === 'default') { %>apiresponse.data<% } else { %>apiresponse<% } %> , null] as const;\n <% } %>\n }\n<% if(throwError) { %>\n throw (output || APIError).create(\n response.status,\n await parser(response),\n );\n<% } else { %>\n const data = (output || APIError).create(\n response.status,\n await parser(response),\n );\n return [null as never, data as never] as const;\n<% } %>\n}\n\nexport function isTypeOf<T extends Type<APIResponse>>(\n instance: any,\n baseType: T,\n): instance is T {\n if (instance === baseType) {\n return true;\n }\n const prototype = Object.getPrototypeOf(instance);\n if (prototype === null) {\n return false;\n }\n return isTypeOf(prototype, baseType);\n}\n";
|
|
2034
1886
|
|
|
1887
|
+
// packages/typescript/src/lib/readme.ts
|
|
1888
|
+
import { followRef as followRef6, isRef as isRef7 } from "@sdk-it/core";
|
|
1889
|
+
var PropEmitter = class {
|
|
1890
|
+
#spec;
|
|
1891
|
+
constructor(spec) {
|
|
1892
|
+
this.#spec = spec;
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Handle objects (properties)
|
|
1896
|
+
*/
|
|
1897
|
+
#object(schema) {
|
|
1898
|
+
const lines = [];
|
|
1899
|
+
const properties = schema.properties || {};
|
|
1900
|
+
if (Object.keys(properties).length > 0) {
|
|
1901
|
+
lines.push(`**Properties:**`);
|
|
1902
|
+
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
1903
|
+
const isRequired = (schema.required ?? []).includes(propName);
|
|
1904
|
+
lines.push(...this.#property(propName, propSchema, isRequired));
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
if (schema.additionalProperties) {
|
|
1908
|
+
lines.push(`**Additional Properties:**`);
|
|
1909
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
1910
|
+
lines.push(`- Allowed: ${schema.additionalProperties}`);
|
|
1911
|
+
} else {
|
|
1912
|
+
lines.push(
|
|
1913
|
+
...this.handle(schema.additionalProperties).map((l) => ` ${l}`)
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
return lines;
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Format a property with its type and description
|
|
1921
|
+
*/
|
|
1922
|
+
#property(name, schema, required) {
|
|
1923
|
+
const requiredMark = required ? " (required)" : "";
|
|
1924
|
+
const propNameLine = `- \`${name}\`${requiredMark}:`;
|
|
1925
|
+
const lines = [propNameLine];
|
|
1926
|
+
const schemaDocs = this.handle(schema);
|
|
1927
|
+
lines.push(...schemaDocs.map((line) => ` ${line}`));
|
|
1928
|
+
return lines;
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Handle array schemas
|
|
1932
|
+
*/
|
|
1933
|
+
#array(schema) {
|
|
1934
|
+
const lines = [];
|
|
1935
|
+
lines.push(`**Array items:**`);
|
|
1936
|
+
if (schema.items) {
|
|
1937
|
+
const itemDocs = this.handle(schema.items);
|
|
1938
|
+
lines.push(...itemDocs.map((line) => ` ${line}`));
|
|
1939
|
+
} else {
|
|
1940
|
+
lines.push(` **Type:** \`unknown\``);
|
|
1941
|
+
}
|
|
1942
|
+
if (schema.minItems !== void 0)
|
|
1943
|
+
lines.push(`- Minimum items: ${schema.minItems}`);
|
|
1944
|
+
if (schema.maxItems !== void 0)
|
|
1945
|
+
lines.push(`- Maximum items: ${schema.maxItems}`);
|
|
1946
|
+
if (schema.uniqueItems)
|
|
1947
|
+
lines.push(`- Items must be unique.`);
|
|
1948
|
+
return lines;
|
|
1949
|
+
}
|
|
1950
|
+
#ref($ref) {
|
|
1951
|
+
const schemaName = $ref.split("/").pop() || "object";
|
|
1952
|
+
const resolved = followRef6(this.#spec, $ref);
|
|
1953
|
+
const lines = [
|
|
1954
|
+
`**Type:** [\`${schemaName}\`](#${schemaName.toLowerCase()})`
|
|
1955
|
+
];
|
|
1956
|
+
if (resolved.description) {
|
|
1957
|
+
lines.push(resolved.description);
|
|
1958
|
+
}
|
|
1959
|
+
return lines;
|
|
1960
|
+
}
|
|
1961
|
+
#allOf(schemas) {
|
|
1962
|
+
const lines = ["**All of (Intersection):**"];
|
|
1963
|
+
schemas.forEach((subSchema, index) => {
|
|
1964
|
+
lines.push(`- **Constraint ${index + 1}:**`);
|
|
1965
|
+
const subLines = this.handle(subSchema);
|
|
1966
|
+
lines.push(...subLines.map((l) => ` ${l}`));
|
|
1967
|
+
});
|
|
1968
|
+
return lines;
|
|
1969
|
+
}
|
|
1970
|
+
#anyOf(schemas) {
|
|
1971
|
+
const lines = ["**Any of (Union):**"];
|
|
1972
|
+
schemas.forEach((subSchema, index) => {
|
|
1973
|
+
lines.push(`- **Option ${index + 1}:**`);
|
|
1974
|
+
const subLines = this.handle(subSchema);
|
|
1975
|
+
lines.push(...subLines.map((l) => ` ${l}`));
|
|
1976
|
+
});
|
|
1977
|
+
return lines;
|
|
1978
|
+
}
|
|
1979
|
+
#oneOf(schemas) {
|
|
1980
|
+
const lines = ["**One of (Exclusive Union):**"];
|
|
1981
|
+
schemas.forEach((subSchema, index) => {
|
|
1982
|
+
lines.push(`- **Option ${index + 1}:**`);
|
|
1983
|
+
const subLines = this.handle(subSchema);
|
|
1984
|
+
lines.push(...subLines.map((l) => ` ${l}`));
|
|
1985
|
+
});
|
|
1986
|
+
return lines;
|
|
1987
|
+
}
|
|
1988
|
+
#enum(schema) {
|
|
1989
|
+
const lines = [`**Type:** \`${schema.type || "unknown"}\` (enum)`];
|
|
1990
|
+
if (schema.description)
|
|
1991
|
+
lines.push(schema.description);
|
|
1992
|
+
lines.push("**Allowed values:**");
|
|
1993
|
+
lines.push(
|
|
1994
|
+
...(schema.enum || []).map((val) => `- \`${JSON.stringify(val)}\``)
|
|
1995
|
+
);
|
|
1996
|
+
if (schema.default !== void 0) {
|
|
1997
|
+
lines.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
|
|
1998
|
+
}
|
|
1999
|
+
return lines;
|
|
2000
|
+
}
|
|
2001
|
+
#normal(type, schema, nullable) {
|
|
2002
|
+
const lines = [];
|
|
2003
|
+
const nullableSuffix = nullable ? " (nullable)" : "";
|
|
2004
|
+
const description = schema.description ? [schema.description] : [];
|
|
2005
|
+
switch (type) {
|
|
2006
|
+
case "string":
|
|
2007
|
+
lines.push(
|
|
2008
|
+
`**Type:** \`string\`${schema.format ? ` (format: ${schema.format})` : ""}${nullableSuffix}`
|
|
2009
|
+
);
|
|
2010
|
+
lines.push(...description);
|
|
2011
|
+
if (schema.minLength !== void 0)
|
|
2012
|
+
lines.push(`- Minimum length: ${schema.minLength}`);
|
|
2013
|
+
if (schema.maxLength !== void 0)
|
|
2014
|
+
lines.push(`- Maximum length: ${schema.maxLength}`);
|
|
2015
|
+
if (schema.pattern !== void 0)
|
|
2016
|
+
lines.push(`- Pattern: \`${schema.pattern}\``);
|
|
2017
|
+
break;
|
|
2018
|
+
case "number":
|
|
2019
|
+
case "integer":
|
|
2020
|
+
lines.push(
|
|
2021
|
+
`**Type:** \`${type}\`${schema.format ? ` (format: ${schema.format})` : ""}${nullableSuffix}`
|
|
2022
|
+
);
|
|
2023
|
+
lines.push(...description);
|
|
2024
|
+
if (schema.minimum !== void 0) {
|
|
2025
|
+
const exclusiveMin = typeof schema.exclusiveMinimum === "number";
|
|
2026
|
+
lines.push(
|
|
2027
|
+
`- Minimum: ${schema.minimum}${exclusiveMin ? " (exclusive)" : ""}`
|
|
2028
|
+
);
|
|
2029
|
+
if (exclusiveMin) {
|
|
2030
|
+
lines.push(
|
|
2031
|
+
`- Must be strictly greater than: ${schema.exclusiveMinimum}`
|
|
2032
|
+
);
|
|
2033
|
+
}
|
|
2034
|
+
} else if (typeof schema.exclusiveMinimum === "number") {
|
|
2035
|
+
lines.push(
|
|
2036
|
+
`- Must be strictly greater than: ${schema.exclusiveMinimum}`
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
if (schema.maximum !== void 0) {
|
|
2040
|
+
const exclusiveMax = typeof schema.exclusiveMaximum === "number";
|
|
2041
|
+
lines.push(
|
|
2042
|
+
`- Maximum: ${schema.maximum}${exclusiveMax ? " (exclusive)" : ""}`
|
|
2043
|
+
);
|
|
2044
|
+
if (exclusiveMax) {
|
|
2045
|
+
lines.push(
|
|
2046
|
+
`- Must be strictly less than: ${schema.exclusiveMaximum}`
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
} else if (typeof schema.exclusiveMaximum === "number") {
|
|
2050
|
+
lines.push(
|
|
2051
|
+
`- Must be strictly less than: ${schema.exclusiveMaximum}`
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
if (schema.multipleOf !== void 0)
|
|
2055
|
+
lines.push(`- Must be a multiple of: ${schema.multipleOf}`);
|
|
2056
|
+
break;
|
|
2057
|
+
case "boolean":
|
|
2058
|
+
lines.push(`**Type:** \`boolean\`${nullableSuffix}`);
|
|
2059
|
+
lines.push(...description);
|
|
2060
|
+
break;
|
|
2061
|
+
case "object":
|
|
2062
|
+
lines.push(`**Type:** \`object\`${nullableSuffix}`);
|
|
2063
|
+
lines.push(...description);
|
|
2064
|
+
lines.push(...this.#object(schema));
|
|
2065
|
+
break;
|
|
2066
|
+
case "array":
|
|
2067
|
+
lines.push(`**Type:** \`array\`${nullableSuffix}`);
|
|
2068
|
+
lines.push(...description);
|
|
2069
|
+
lines.push(...this.#array(schema));
|
|
2070
|
+
break;
|
|
2071
|
+
case "null":
|
|
2072
|
+
lines.push(`**Type:** \`null\``);
|
|
2073
|
+
lines.push(...description);
|
|
2074
|
+
break;
|
|
2075
|
+
default:
|
|
2076
|
+
lines.push(`**Type:** \`${type}\`${nullableSuffix}`);
|
|
2077
|
+
lines.push(...description);
|
|
2078
|
+
}
|
|
2079
|
+
if (schema.default !== void 0) {
|
|
2080
|
+
lines.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
|
|
2081
|
+
}
|
|
2082
|
+
return lines.filter((l) => l);
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Handle schemas by resolving references and delegating to appropriate handler
|
|
2086
|
+
*/
|
|
2087
|
+
handle(schemaOrRef) {
|
|
2088
|
+
if (isRef7(schemaOrRef)) {
|
|
2089
|
+
return this.#ref(schemaOrRef.$ref);
|
|
2090
|
+
}
|
|
2091
|
+
const schema = schemaOrRef;
|
|
2092
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
2093
|
+
return this.#allOf(schema.allOf);
|
|
2094
|
+
}
|
|
2095
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
2096
|
+
return this.#anyOf(schema.anyOf);
|
|
2097
|
+
}
|
|
2098
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
2099
|
+
return this.#oneOf(schema.oneOf);
|
|
2100
|
+
}
|
|
2101
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
2102
|
+
return this.#enum(schema);
|
|
2103
|
+
}
|
|
2104
|
+
let types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
2105
|
+
let nullable = false;
|
|
2106
|
+
if (types.includes("null")) {
|
|
2107
|
+
nullable = true;
|
|
2108
|
+
types = types.filter((t) => t !== "null");
|
|
2109
|
+
}
|
|
2110
|
+
if (types.length === 0) {
|
|
2111
|
+
if (schema.properties || schema.additionalProperties) {
|
|
2112
|
+
types = ["object"];
|
|
2113
|
+
} else if (schema.items) {
|
|
2114
|
+
types = ["array"];
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
if (types.length === 0) {
|
|
2118
|
+
const lines2 = ["**Type:** `unknown`"];
|
|
2119
|
+
if (schema.description)
|
|
2120
|
+
lines2.push(schema.description);
|
|
2121
|
+
if (schema.default !== void 0)
|
|
2122
|
+
lines2.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
|
|
2123
|
+
return lines2;
|
|
2124
|
+
}
|
|
2125
|
+
if (types.length === 1) {
|
|
2126
|
+
return this.#normal(types[0], schema, nullable);
|
|
2127
|
+
}
|
|
2128
|
+
const typeString = types.join(" | ");
|
|
2129
|
+
const nullableSuffix = nullable ? " (nullable)" : "";
|
|
2130
|
+
const lines = [`**Type:** \`${typeString}\`${nullableSuffix}`];
|
|
2131
|
+
if (schema.description)
|
|
2132
|
+
lines.push(schema.description);
|
|
2133
|
+
if (schema.default !== void 0)
|
|
2134
|
+
lines.push(`**Default:** \`${JSON.stringify(schema.default)}\``);
|
|
2135
|
+
return lines;
|
|
2136
|
+
}
|
|
2137
|
+
/**
|
|
2138
|
+
* Process a request body and return markdown documentation
|
|
2139
|
+
*/
|
|
2140
|
+
requestBody(requestBody) {
|
|
2141
|
+
if (!requestBody)
|
|
2142
|
+
return [];
|
|
2143
|
+
const resolvedBody = isRef7(requestBody) ? followRef6(this.#spec, requestBody.$ref) : requestBody;
|
|
2144
|
+
const lines = [];
|
|
2145
|
+
lines.push(`##### Request Body`);
|
|
2146
|
+
if (resolvedBody.description) {
|
|
2147
|
+
lines.push(resolvedBody.description);
|
|
2148
|
+
}
|
|
2149
|
+
if (resolvedBody.required) {
|
|
2150
|
+
lines.push(`*This request body is required.*`);
|
|
2151
|
+
}
|
|
2152
|
+
if (resolvedBody.content) {
|
|
2153
|
+
for (const [contentType, mediaType] of Object.entries(
|
|
2154
|
+
resolvedBody.content
|
|
2155
|
+
)) {
|
|
2156
|
+
lines.push(`**Content Type:** \`${contentType}\``);
|
|
2157
|
+
if (mediaType.schema) {
|
|
2158
|
+
const schemaDocs = this.handle(mediaType.schema);
|
|
2159
|
+
lines.push(...schemaDocs);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
return lines;
|
|
2164
|
+
}
|
|
2165
|
+
};
|
|
2166
|
+
function toReadme(spec) {
|
|
2167
|
+
const markdown = [];
|
|
2168
|
+
const propEmitter = new PropEmitter(spec);
|
|
2169
|
+
forEachOperation({ spec }, ({ method, path, name }, operation) => {
|
|
2170
|
+
spec.components ??= {};
|
|
2171
|
+
spec.components.schemas ??= {};
|
|
2172
|
+
const statuses = [];
|
|
2173
|
+
markdown.push(
|
|
2174
|
+
`#### ${name || operation.operationId} | ${`_${method.toUpperCase()} ${path}_`}`
|
|
2175
|
+
);
|
|
2176
|
+
markdown.push(operation.summary || "");
|
|
2177
|
+
const requestBodyContent = propEmitter.requestBody(operation.requestBody);
|
|
2178
|
+
if (requestBodyContent.length > 1) {
|
|
2179
|
+
markdown.push(requestBodyContent.join("\n\n"));
|
|
2180
|
+
}
|
|
2181
|
+
markdown.push(`##### Responses`);
|
|
2182
|
+
for (const status in operation.responses) {
|
|
2183
|
+
const response = operation.responses[status];
|
|
2184
|
+
const resolvedResponse = isRef7(response) ? followRef6(spec, response.$ref) : response;
|
|
2185
|
+
statuses.push(`**${status}** _${resolvedResponse.description}_`);
|
|
2186
|
+
}
|
|
2187
|
+
markdown.push(`<small>${statuses.join("\n\n")}</small>`);
|
|
2188
|
+
});
|
|
2189
|
+
return markdown.join("\n\n");
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2035
2192
|
// packages/typescript/src/lib/generate.ts
|
|
2036
2193
|
function security(spec) {
|
|
2037
2194
|
const security2 = spec.security || [];
|
|
@@ -2074,17 +2231,18 @@ async function generate(spec, settings) {
|
|
|
2074
2231
|
makeImport
|
|
2075
2232
|
}
|
|
2076
2233
|
);
|
|
2077
|
-
const output = settings.mode === "full" ?
|
|
2234
|
+
const output = settings.mode === "full" ? join2(settings.output, "src") : settings.output;
|
|
2078
2235
|
const options = security(spec);
|
|
2079
|
-
const clientName = settings.name
|
|
2236
|
+
const clientName = settings.name?.trim() ? pascalcase3(settings.name) : "Client";
|
|
2237
|
+
const readme = settings.readme ? toReadme(spec) : "";
|
|
2080
2238
|
const inputFiles = generateInputs(groups, commonZod, makeImport);
|
|
2239
|
+
console.log("Writing to", output);
|
|
2081
2240
|
await writeFiles(output, {
|
|
2082
2241
|
"outputs/.gitkeep": "",
|
|
2083
2242
|
"inputs/.gitkeep": "",
|
|
2084
2243
|
"models/.getkeep": ""
|
|
2085
|
-
// 'README.md': readme,
|
|
2086
2244
|
});
|
|
2087
|
-
await writeFiles(
|
|
2245
|
+
await writeFiles(join2(output, "http"), {
|
|
2088
2246
|
"interceptors.ts": `
|
|
2089
2247
|
import type { RequestConfig, HeadersInit } from './${makeImport("request")}';
|
|
2090
2248
|
${interceptors_default}`,
|
|
@@ -2101,7 +2259,7 @@ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputT
|
|
|
2101
2259
|
"parser.ts": parser_default,
|
|
2102
2260
|
"request.ts": request_default
|
|
2103
2261
|
});
|
|
2104
|
-
await writeFiles(
|
|
2262
|
+
await writeFiles(join2(output, "outputs"), outputs);
|
|
2105
2263
|
const modelsImports = Object.entries(commonSchemas).map(([name]) => name);
|
|
2106
2264
|
await writeFiles(output, {
|
|
2107
2265
|
"client.ts": client_default(
|
|
@@ -2129,16 +2287,16 @@ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputT
|
|
|
2129
2287
|
)
|
|
2130
2288
|
});
|
|
2131
2289
|
const folders = [
|
|
2132
|
-
getFolderExports(
|
|
2290
|
+
getFolderExports(join2(output, "outputs"), settings.useTsExtension),
|
|
2133
2291
|
getFolderExports(
|
|
2134
|
-
|
|
2292
|
+
join2(output, "inputs"),
|
|
2135
2293
|
settings.useTsExtension,
|
|
2136
2294
|
["ts"],
|
|
2137
2295
|
(dirent) => dirent.isDirectory() && ["schemas"].includes(dirent.name)
|
|
2138
2296
|
),
|
|
2139
|
-
getFolderExports(
|
|
2297
|
+
getFolderExports(join2(output, "api"), settings.useTsExtension),
|
|
2140
2298
|
getFolderExports(
|
|
2141
|
-
|
|
2299
|
+
join2(output, "http"),
|
|
2142
2300
|
settings.useTsExtension,
|
|
2143
2301
|
["ts"],
|
|
2144
2302
|
(dirent) => !["response.ts", "parser.ts"].includes(dirent.name)
|
|
@@ -2146,7 +2304,7 @@ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputT
|
|
|
2146
2304
|
];
|
|
2147
2305
|
if (modelsImports.length) {
|
|
2148
2306
|
folders.push(
|
|
2149
|
-
getFolderExports(
|
|
2307
|
+
getFolderExports(join2(output, "models"), settings.useTsExtension)
|
|
2150
2308
|
);
|
|
2151
2309
|
}
|
|
2152
2310
|
const [outputIndex, inputsIndex, apiIndex, httpIndex, modelsIndex] = await Promise.all(folders);
|
|
@@ -2161,7 +2319,7 @@ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputT
|
|
|
2161
2319
|
"index.ts": await getFolderExports(output, settings.useTsExtension, ["ts"])
|
|
2162
2320
|
});
|
|
2163
2321
|
if (settings.mode === "full") {
|
|
2164
|
-
|
|
2322
|
+
const configFiles = {
|
|
2165
2323
|
"package.json": {
|
|
2166
2324
|
ignoreIfExists: true,
|
|
2167
2325
|
content: JSON.stringify(
|
|
@@ -2200,7 +2358,14 @@ ${template2(send_request_default, {})({ throwError: !style.errorAsValue, outputT
|
|
|
2200
2358
|
2
|
|
2201
2359
|
)
|
|
2202
2360
|
}
|
|
2203
|
-
}
|
|
2361
|
+
};
|
|
2362
|
+
if (readme) {
|
|
2363
|
+
configFiles["README.md"] = {
|
|
2364
|
+
ignoreIfExists: true,
|
|
2365
|
+
content: readme
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
await writeFiles(settings.output, configFiles);
|
|
2204
2369
|
}
|
|
2205
2370
|
await settings.formatCode?.({
|
|
2206
2371
|
output,
|