apigen-ts 1.1.0 → 1.2.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/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{main-B2qgp9dv.cjs → main-C0qK6dZX.cjs} +73 -45
- package/dist/{main-BvB08bJo.js → main-l0LIDQ3K.js} +74 -46
- package/dist/{main-BvB08bJo.mjs → main-l0LIDQ3K.mjs} +74 -46
- package/dist/main.cjs +1 -1
- package/dist/main.d.cts +1 -0
- package/dist/main.d.mts +1 -0
- package/dist/main.js +1 -1
- package/dist/main.mjs +1 -1
- package/package.json +8 -8
- package/readme.md +16 -9
package/dist/cli.cjs
CHANGED
package/dist/cli.js
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -13,7 +13,7 @@ var path = require('node:path');
|
|
|
13
13
|
|
|
14
14
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
15
15
|
var name = "apigen-ts";
|
|
16
|
-
var version = "1.
|
|
16
|
+
var version = "1.2.0";
|
|
17
17
|
|
|
18
18
|
const initCtx = (config) => {
|
|
19
19
|
return {
|
|
@@ -23,11 +23,20 @@ const initCtx = (config) => {
|
|
|
23
23
|
doc: { openapi: "3.1.0" },
|
|
24
24
|
parseDates: false,
|
|
25
25
|
inlineEnums: false,
|
|
26
|
+
headers: {},
|
|
26
27
|
...config,
|
|
27
28
|
logTag: "",
|
|
28
29
|
usedNames: /* @__PURE__ */ new Set()
|
|
29
30
|
};
|
|
30
31
|
};
|
|
32
|
+
const parseHeaders = (items) => {
|
|
33
|
+
const headers = {};
|
|
34
|
+
for (const item of items) {
|
|
35
|
+
const [key, val] = item.split(":");
|
|
36
|
+
if (key && val) headers[key.trim()] = val.trim();
|
|
37
|
+
}
|
|
38
|
+
return headers;
|
|
39
|
+
};
|
|
31
40
|
const getCliConfig = () => {
|
|
32
41
|
const argv = cleye.cli({
|
|
33
42
|
name,
|
|
@@ -36,18 +45,24 @@ const getCliConfig = () => {
|
|
|
36
45
|
flags: {
|
|
37
46
|
name: {
|
|
38
47
|
type: String,
|
|
39
|
-
description: "
|
|
48
|
+
description: "API class name to export",
|
|
40
49
|
default: "ApiClient"
|
|
41
50
|
},
|
|
42
51
|
parseDates: {
|
|
43
52
|
type: Boolean,
|
|
44
|
-
description: "
|
|
53
|
+
description: "Parse dates as Date objects",
|
|
45
54
|
default: false
|
|
46
55
|
},
|
|
47
56
|
inlineEnums: {
|
|
48
57
|
type: Boolean,
|
|
49
|
-
description: "
|
|
58
|
+
description: "Use inline enums instead of enum types",
|
|
50
59
|
default: false
|
|
60
|
+
},
|
|
61
|
+
header: {
|
|
62
|
+
type: [String],
|
|
63
|
+
alias: "H",
|
|
64
|
+
description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
|
|
65
|
+
default: []
|
|
51
66
|
}
|
|
52
67
|
}
|
|
53
68
|
});
|
|
@@ -56,13 +71,14 @@ const getCliConfig = () => {
|
|
|
56
71
|
output: argv._.output ?? null,
|
|
57
72
|
name: argv.flags.name,
|
|
58
73
|
parseDates: argv.flags.parseDates,
|
|
59
|
-
inlineEnums: argv.flags.inlineEnums
|
|
74
|
+
inlineEnums: argv.flags.inlineEnums,
|
|
75
|
+
headers: parseHeaders(argv.flags.header)
|
|
60
76
|
};
|
|
61
77
|
return config;
|
|
62
78
|
};
|
|
63
79
|
|
|
64
80
|
const unref = (ctx, s) => {
|
|
65
|
-
if (!s) return
|
|
81
|
+
if (!s) return void 0;
|
|
66
82
|
if ("$ref" in s && s.$ref) {
|
|
67
83
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
68
84
|
const obj = parts.reduce(
|
|
@@ -72,15 +88,15 @@ const unref = (ctx, s) => {
|
|
|
72
88
|
);
|
|
73
89
|
if (obj) return obj;
|
|
74
90
|
console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
|
|
75
|
-
return
|
|
91
|
+
return void 0;
|
|
76
92
|
}
|
|
77
93
|
return s;
|
|
78
94
|
};
|
|
79
95
|
const getReqSchema = (ctx, config) => {
|
|
80
96
|
const req = unref(ctx, config.requestBody);
|
|
81
|
-
if (!req) return
|
|
97
|
+
if (!req) return void 0;
|
|
82
98
|
const cts = Object.entries(req.content ?? {}).map((x) => [x[0].split(";")[0], x[1].schema]).filter((x) => x[1]);
|
|
83
|
-
if (cts.length === 0) return
|
|
99
|
+
if (cts.length === 0) return void 0;
|
|
84
100
|
const pretenders = [
|
|
85
101
|
"application/json",
|
|
86
102
|
"text/",
|
|
@@ -92,18 +108,18 @@ const getReqSchema = (ctx, config) => {
|
|
|
92
108
|
if (ct) return ct;
|
|
93
109
|
}
|
|
94
110
|
cts.map((x) => x[0]);
|
|
95
|
-
return
|
|
111
|
+
return void 0;
|
|
96
112
|
};
|
|
97
113
|
const getRepSchema = (ctx, config) => {
|
|
98
114
|
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => lodashEs.get(config, ["responses", x, "content"]));
|
|
99
115
|
const cts = Object.entries(lodashEs.get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
|
|
100
|
-
if (cts.length === 0) return
|
|
116
|
+
if (cts.length === 0) return void 0;
|
|
101
117
|
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
102
118
|
if (ctJson) return ctJson[1].schema;
|
|
103
119
|
const ctText = cts.find((x) => x[0].startsWith("text/"));
|
|
104
120
|
if (ctText) return { type: "string" };
|
|
105
121
|
cts.map((x) => x[0]).join(", ");
|
|
106
|
-
return
|
|
122
|
+
return void 0;
|
|
107
123
|
};
|
|
108
124
|
|
|
109
125
|
const f$2 = ts.factory;
|
|
@@ -179,9 +195,9 @@ const normalizeIdentifier = (val, asVar = false) => {
|
|
|
179
195
|
return name;
|
|
180
196
|
};
|
|
181
197
|
const makeInlineEnum = (s) => {
|
|
182
|
-
if (!s.enum) return
|
|
198
|
+
if (!s.enum) return void 0;
|
|
183
199
|
const values = arrayUtilsTs.filterEmpty(s.enum);
|
|
184
|
-
if (!values.length) return
|
|
200
|
+
if (!values.length) return void 0;
|
|
185
201
|
if (!s.type) {
|
|
186
202
|
if (values.every((x) => typeof x === "string")) s.type = "string";
|
|
187
203
|
if (values.every((x) => typeof x === "number")) s.type = "number";
|
|
@@ -202,7 +218,7 @@ const makeInlineEnum = (s) => {
|
|
|
202
218
|
return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
|
|
203
219
|
}
|
|
204
220
|
console.warn(`enum with unknown type "${s.type}" in`, s);
|
|
205
|
-
return
|
|
221
|
+
return void 0;
|
|
206
222
|
};
|
|
207
223
|
const makeObject = (ctx, s) => {
|
|
208
224
|
if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
|
|
@@ -214,9 +230,15 @@ const makeObject = (ctx, s) => {
|
|
|
214
230
|
}
|
|
215
231
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
|
|
216
232
|
};
|
|
233
|
+
const makeLiteralUnion = (ctx, types) => {
|
|
234
|
+
const tokens = types.map((x) => makeType(ctx, { type: x }));
|
|
235
|
+
const hasUnknown = tokens.some((x) => x.kind === ts.SyntaxKind.UnknownKeyword);
|
|
236
|
+
if (hasUnknown) return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
237
|
+
return f$2.createUnionTypeNode(tokens);
|
|
238
|
+
};
|
|
217
239
|
const makeType = (ctx, s) => {
|
|
218
240
|
const mk = makeType.bind(null, ctx);
|
|
219
|
-
if (s ===
|
|
241
|
+
if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
220
242
|
if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
|
|
221
243
|
if ("$ref" in s && s.$ref) {
|
|
222
244
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
@@ -240,28 +262,20 @@ const makeType = (ctx, s) => {
|
|
|
240
262
|
return f$2.createTypeLiteralNode(
|
|
241
263
|
Object.entries(s.properties).map(([k, v]) => {
|
|
242
264
|
const r = s.required ?? [];
|
|
243
|
-
const q = r.includes(k) ?
|
|
244
|
-
return f$2.createPropertySignature(
|
|
265
|
+
const q = r.includes(k) ? void 0 : f$2.createToken(ts.SyntaxKind.QuestionToken);
|
|
266
|
+
return f$2.createPropertySignature(void 0, f$2.createStringLiteral(k), q, mk(v));
|
|
245
267
|
})
|
|
246
268
|
);
|
|
247
269
|
}
|
|
248
270
|
if ("type" in s) {
|
|
249
|
-
if (Array.isArray(s.type)) {
|
|
250
|
-
const types = [];
|
|
251
|
-
for (const type of s.type) {
|
|
252
|
-
if (type === "null") types.push({ type: "null" });
|
|
253
|
-
else types.push({ ...s, type });
|
|
254
|
-
}
|
|
255
|
-
return mk({ oneOf: types });
|
|
256
|
-
}
|
|
257
271
|
let t;
|
|
258
272
|
if (s.type === "object") t = makeObject(ctx, s);
|
|
259
273
|
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
260
274
|
else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
261
275
|
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
262
|
-
else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
|
|
263
276
|
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
264
|
-
else if (lodashEs.isArray(s.type)) t =
|
|
277
|
+
else if (lodashEs.isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
278
|
+
else if (s.type === "array" && !lodashEs.isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
|
|
265
279
|
else {
|
|
266
280
|
console.warn(`makeType: unknown type "${s.type}"`);
|
|
267
281
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
@@ -270,7 +284,10 @@ const makeType = (ctx, s) => {
|
|
|
270
284
|
if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
|
|
271
285
|
if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
|
|
272
286
|
}
|
|
273
|
-
|
|
287
|
+
if ("nullable" in s && s.nullable) {
|
|
288
|
+
return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
|
|
289
|
+
}
|
|
290
|
+
return t;
|
|
274
291
|
}
|
|
275
292
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
276
293
|
};
|
|
@@ -298,7 +315,7 @@ const makeTypeAlias = (ctx, name, s) => {
|
|
|
298
315
|
return f$2.createTypeAliasDeclaration(
|
|
299
316
|
[f$2.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
300
317
|
f$2.createIdentifier(normalizeIdentifier(name, true)),
|
|
301
|
-
|
|
318
|
+
void 0,
|
|
302
319
|
makeType(ctx, s)
|
|
303
320
|
);
|
|
304
321
|
};
|
|
@@ -327,7 +344,7 @@ const getOpName = (ctx, op) => {
|
|
|
327
344
|
if (ctx.resolveName) {
|
|
328
345
|
const res = ctx.resolveName(ctx, op, proposal);
|
|
329
346
|
if (Array.isArray(res) && res.length === 2) return res;
|
|
330
|
-
if (res !==
|
|
347
|
+
if (res !== void 0) {
|
|
331
348
|
console.warn(`${ctx.logTag} resolveName should return [ns, fn] or undefined (skipping)`);
|
|
332
349
|
}
|
|
333
350
|
}
|
|
@@ -369,10 +386,10 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
369
386
|
const name = normalizeIdentifier(x.name, true);
|
|
370
387
|
const type = makeType(ctx, x.schema);
|
|
371
388
|
urlReplacements[x.name] = name;
|
|
372
|
-
return f$1.createParameterDeclaration(
|
|
389
|
+
return f$1.createParameterDeclaration(void 0, void 0, name, void 0, type);
|
|
373
390
|
});
|
|
374
391
|
const cbArgs = arrayUtilsTs.filterNullable([
|
|
375
|
-
search.length ? f$1.createShorthandPropertyAssignment("search") :
|
|
392
|
+
search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
|
|
376
393
|
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
377
394
|
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
378
395
|
"headers",
|
|
@@ -382,16 +399,16 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
382
399
|
f$1.createStringLiteral(reqSchema[0])
|
|
383
400
|
)
|
|
384
401
|
])
|
|
385
|
-
) :
|
|
402
|
+
) : void 0
|
|
386
403
|
]);
|
|
387
404
|
return f$1.createPropertyAssignment(
|
|
388
405
|
f$1.createIdentifier(normalizeIdentifier(opName)),
|
|
389
406
|
f$1.createArrowFunction(
|
|
390
|
-
|
|
391
|
-
|
|
407
|
+
void 0,
|
|
408
|
+
void 0,
|
|
392
409
|
fnArgs,
|
|
393
|
-
|
|
394
|
-
|
|
410
|
+
void 0,
|
|
411
|
+
void 0,
|
|
395
412
|
f$1.createBlock([
|
|
396
413
|
f$1.createReturnStatement(
|
|
397
414
|
f$1.createCallExpression(
|
|
@@ -413,10 +430,10 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
413
430
|
};
|
|
414
431
|
const prepareNs = (ctx, name, handlers) => {
|
|
415
432
|
return f$1.createPropertyDeclaration(
|
|
416
|
-
|
|
433
|
+
void 0,
|
|
417
434
|
normalizeIdentifier(name),
|
|
418
|
-
|
|
419
|
-
|
|
435
|
+
void 0,
|
|
436
|
+
void 0,
|
|
420
437
|
f$1.createObjectLiteralExpression(handlers)
|
|
421
438
|
);
|
|
422
439
|
};
|
|
@@ -476,12 +493,23 @@ const generateAst = async (ctx) => {
|
|
|
476
493
|
}
|
|
477
494
|
return { modules, types };
|
|
478
495
|
};
|
|
479
|
-
const loadSchema = async (
|
|
496
|
+
const loadSchema = async ({
|
|
497
|
+
url,
|
|
498
|
+
upgrade = true,
|
|
499
|
+
headers = {}
|
|
500
|
+
}) => {
|
|
480
501
|
if (url.startsWith("file://")) url = url.substring(7);
|
|
481
502
|
const { bundle } = await redocly.bundle({
|
|
482
503
|
ref: url,
|
|
483
504
|
config: await redocly.createConfig({}),
|
|
484
|
-
removeUnusedComponents: false
|
|
505
|
+
removeUnusedComponents: false,
|
|
506
|
+
externalRefResolver: new redocly.BaseResolver({
|
|
507
|
+
http: {
|
|
508
|
+
headers: Object.entries(headers).map(([name, value]) => {
|
|
509
|
+
return { name, value, matches: "**" };
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
})
|
|
485
513
|
});
|
|
486
514
|
if (bundle.parsed.swagger && upgrade) {
|
|
487
515
|
const { openapi } = await swagger2openapi.convertObj(bundle.parsed, { patch: true });
|
|
@@ -519,10 +547,10 @@ const formatCode = async (code) => {
|
|
|
519
547
|
};
|
|
520
548
|
|
|
521
549
|
const apigen = async (config) => {
|
|
522
|
-
const doc = await loadSchema(config.source);
|
|
550
|
+
const doc = await loadSchema({ url: config.source, headers: config.headers });
|
|
523
551
|
const ctx = initCtx({ ...config, doc });
|
|
524
552
|
const { modules, types } = await generateAst(ctx);
|
|
525
|
-
const filepath = path$1.join(path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('main-
|
|
553
|
+
const filepath = path$1.join(path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('main-C0qK6dZX.cjs', document.baseURI).href)))), "_template.ts");
|
|
526
554
|
const file = await fs.readFile(filepath, "utf-8");
|
|
527
555
|
let code = [
|
|
528
556
|
`// Auto-generated by https://github.com/vladkens/apigen-ts`,
|
|
@@ -2,15 +2,15 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import { join, dirname } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { cli } from 'cleye';
|
|
5
|
-
import redocly from '@redocly/openapi-core';
|
|
5
|
+
import redocly, { BaseResolver } from '@redocly/openapi-core';
|
|
6
6
|
import { filterEmpty, filterNullable } from 'array-utils-ts';
|
|
7
|
-
import { get, uniq, upperFirst, isArray, isBoolean,
|
|
7
|
+
import { get, uniq, upperFirst, isArray, isBoolean, sortBy, isObject, lowerFirst, uniqBy } from 'lodash-es';
|
|
8
8
|
import { convertObj } from 'swagger2openapi';
|
|
9
9
|
import ts from 'typescript';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
|
|
12
12
|
var name = "apigen-ts";
|
|
13
|
-
var version = "1.
|
|
13
|
+
var version = "1.2.0";
|
|
14
14
|
|
|
15
15
|
const initCtx = (config) => {
|
|
16
16
|
return {
|
|
@@ -20,11 +20,20 @@ const initCtx = (config) => {
|
|
|
20
20
|
doc: { openapi: "3.1.0" },
|
|
21
21
|
parseDates: false,
|
|
22
22
|
inlineEnums: false,
|
|
23
|
+
headers: {},
|
|
23
24
|
...config,
|
|
24
25
|
logTag: "",
|
|
25
26
|
usedNames: /* @__PURE__ */ new Set()
|
|
26
27
|
};
|
|
27
28
|
};
|
|
29
|
+
const parseHeaders = (items) => {
|
|
30
|
+
const headers = {};
|
|
31
|
+
for (const item of items) {
|
|
32
|
+
const [key, val] = item.split(":");
|
|
33
|
+
if (key && val) headers[key.trim()] = val.trim();
|
|
34
|
+
}
|
|
35
|
+
return headers;
|
|
36
|
+
};
|
|
28
37
|
const getCliConfig = () => {
|
|
29
38
|
const argv = cli({
|
|
30
39
|
name,
|
|
@@ -33,18 +42,24 @@ const getCliConfig = () => {
|
|
|
33
42
|
flags: {
|
|
34
43
|
name: {
|
|
35
44
|
type: String,
|
|
36
|
-
description: "
|
|
45
|
+
description: "API class name to export",
|
|
37
46
|
default: "ApiClient"
|
|
38
47
|
},
|
|
39
48
|
parseDates: {
|
|
40
49
|
type: Boolean,
|
|
41
|
-
description: "
|
|
50
|
+
description: "Parse dates as Date objects",
|
|
42
51
|
default: false
|
|
43
52
|
},
|
|
44
53
|
inlineEnums: {
|
|
45
54
|
type: Boolean,
|
|
46
|
-
description: "
|
|
55
|
+
description: "Use inline enums instead of enum types",
|
|
47
56
|
default: false
|
|
57
|
+
},
|
|
58
|
+
header: {
|
|
59
|
+
type: [String],
|
|
60
|
+
alias: "H",
|
|
61
|
+
description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
|
|
62
|
+
default: []
|
|
48
63
|
}
|
|
49
64
|
}
|
|
50
65
|
});
|
|
@@ -53,13 +68,14 @@ const getCliConfig = () => {
|
|
|
53
68
|
output: argv._.output ?? null,
|
|
54
69
|
name: argv.flags.name,
|
|
55
70
|
parseDates: argv.flags.parseDates,
|
|
56
|
-
inlineEnums: argv.flags.inlineEnums
|
|
71
|
+
inlineEnums: argv.flags.inlineEnums,
|
|
72
|
+
headers: parseHeaders(argv.flags.header)
|
|
57
73
|
};
|
|
58
74
|
return config;
|
|
59
75
|
};
|
|
60
76
|
|
|
61
77
|
const unref = (ctx, s) => {
|
|
62
|
-
if (!s) return
|
|
78
|
+
if (!s) return void 0;
|
|
63
79
|
if ("$ref" in s && s.$ref) {
|
|
64
80
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
65
81
|
const obj = parts.reduce(
|
|
@@ -69,15 +85,15 @@ const unref = (ctx, s) => {
|
|
|
69
85
|
);
|
|
70
86
|
if (obj) return obj;
|
|
71
87
|
console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
|
|
72
|
-
return
|
|
88
|
+
return void 0;
|
|
73
89
|
}
|
|
74
90
|
return s;
|
|
75
91
|
};
|
|
76
92
|
const getReqSchema = (ctx, config) => {
|
|
77
93
|
const req = unref(ctx, config.requestBody);
|
|
78
|
-
if (!req) return
|
|
94
|
+
if (!req) return void 0;
|
|
79
95
|
const cts = Object.entries(req.content ?? {}).map((x) => [x[0].split(";")[0], x[1].schema]).filter((x) => x[1]);
|
|
80
|
-
if (cts.length === 0) return
|
|
96
|
+
if (cts.length === 0) return void 0;
|
|
81
97
|
const pretenders = [
|
|
82
98
|
"application/json",
|
|
83
99
|
"text/",
|
|
@@ -89,18 +105,18 @@ const getReqSchema = (ctx, config) => {
|
|
|
89
105
|
if (ct) return ct;
|
|
90
106
|
}
|
|
91
107
|
cts.map((x) => x[0]);
|
|
92
|
-
return
|
|
108
|
+
return void 0;
|
|
93
109
|
};
|
|
94
110
|
const getRepSchema = (ctx, config) => {
|
|
95
111
|
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x, "content"]));
|
|
96
112
|
const cts = Object.entries(get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
|
|
97
|
-
if (cts.length === 0) return
|
|
113
|
+
if (cts.length === 0) return void 0;
|
|
98
114
|
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
99
115
|
if (ctJson) return ctJson[1].schema;
|
|
100
116
|
const ctText = cts.find((x) => x[0].startsWith("text/"));
|
|
101
117
|
if (ctText) return { type: "string" };
|
|
102
118
|
cts.map((x) => x[0]).join(", ");
|
|
103
|
-
return
|
|
119
|
+
return void 0;
|
|
104
120
|
};
|
|
105
121
|
|
|
106
122
|
const f$2 = ts.factory;
|
|
@@ -176,9 +192,9 @@ const normalizeIdentifier = (val, asVar = false) => {
|
|
|
176
192
|
return name;
|
|
177
193
|
};
|
|
178
194
|
const makeInlineEnum = (s) => {
|
|
179
|
-
if (!s.enum) return
|
|
195
|
+
if (!s.enum) return void 0;
|
|
180
196
|
const values = filterEmpty(s.enum);
|
|
181
|
-
if (!values.length) return
|
|
197
|
+
if (!values.length) return void 0;
|
|
182
198
|
if (!s.type) {
|
|
183
199
|
if (values.every((x) => typeof x === "string")) s.type = "string";
|
|
184
200
|
if (values.every((x) => typeof x === "number")) s.type = "number";
|
|
@@ -199,7 +215,7 @@ const makeInlineEnum = (s) => {
|
|
|
199
215
|
return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
|
|
200
216
|
}
|
|
201
217
|
console.warn(`enum with unknown type "${s.type}" in`, s);
|
|
202
|
-
return
|
|
218
|
+
return void 0;
|
|
203
219
|
};
|
|
204
220
|
const makeObject = (ctx, s) => {
|
|
205
221
|
if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
|
|
@@ -211,9 +227,15 @@ const makeObject = (ctx, s) => {
|
|
|
211
227
|
}
|
|
212
228
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
|
|
213
229
|
};
|
|
230
|
+
const makeLiteralUnion = (ctx, types) => {
|
|
231
|
+
const tokens = types.map((x) => makeType(ctx, { type: x }));
|
|
232
|
+
const hasUnknown = tokens.some((x) => x.kind === ts.SyntaxKind.UnknownKeyword);
|
|
233
|
+
if (hasUnknown) return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
234
|
+
return f$2.createUnionTypeNode(tokens);
|
|
235
|
+
};
|
|
214
236
|
const makeType = (ctx, s) => {
|
|
215
237
|
const mk = makeType.bind(null, ctx);
|
|
216
|
-
if (s ===
|
|
238
|
+
if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
217
239
|
if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
|
|
218
240
|
if ("$ref" in s && s.$ref) {
|
|
219
241
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
@@ -237,28 +259,20 @@ const makeType = (ctx, s) => {
|
|
|
237
259
|
return f$2.createTypeLiteralNode(
|
|
238
260
|
Object.entries(s.properties).map(([k, v]) => {
|
|
239
261
|
const r = s.required ?? [];
|
|
240
|
-
const q = r.includes(k) ?
|
|
241
|
-
return f$2.createPropertySignature(
|
|
262
|
+
const q = r.includes(k) ? void 0 : f$2.createToken(ts.SyntaxKind.QuestionToken);
|
|
263
|
+
return f$2.createPropertySignature(void 0, f$2.createStringLiteral(k), q, mk(v));
|
|
242
264
|
})
|
|
243
265
|
);
|
|
244
266
|
}
|
|
245
267
|
if ("type" in s) {
|
|
246
|
-
if (Array.isArray(s.type)) {
|
|
247
|
-
const types = [];
|
|
248
|
-
for (const type of s.type) {
|
|
249
|
-
if (type === "null") types.push({ type: "null" });
|
|
250
|
-
else types.push({ ...s, type });
|
|
251
|
-
}
|
|
252
|
-
return mk({ oneOf: types });
|
|
253
|
-
}
|
|
254
268
|
let t;
|
|
255
269
|
if (s.type === "object") t = makeObject(ctx, s);
|
|
256
270
|
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
257
271
|
else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
258
272
|
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
259
|
-
else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
|
|
260
273
|
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
261
|
-
else if (isArray(s.type)) t =
|
|
274
|
+
else if (isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
275
|
+
else if (s.type === "array" && !isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
|
|
262
276
|
else {
|
|
263
277
|
console.warn(`makeType: unknown type "${s.type}"`);
|
|
264
278
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
@@ -267,7 +281,10 @@ const makeType = (ctx, s) => {
|
|
|
267
281
|
if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
|
|
268
282
|
if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
|
|
269
283
|
}
|
|
270
|
-
|
|
284
|
+
if ("nullable" in s && s.nullable) {
|
|
285
|
+
return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
|
|
286
|
+
}
|
|
287
|
+
return t;
|
|
271
288
|
}
|
|
272
289
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
273
290
|
};
|
|
@@ -295,7 +312,7 @@ const makeTypeAlias = (ctx, name, s) => {
|
|
|
295
312
|
return f$2.createTypeAliasDeclaration(
|
|
296
313
|
[f$2.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
297
314
|
f$2.createIdentifier(normalizeIdentifier(name, true)),
|
|
298
|
-
|
|
315
|
+
void 0,
|
|
299
316
|
makeType(ctx, s)
|
|
300
317
|
);
|
|
301
318
|
};
|
|
@@ -324,7 +341,7 @@ const getOpName = (ctx, op) => {
|
|
|
324
341
|
if (ctx.resolveName) {
|
|
325
342
|
const res = ctx.resolveName(ctx, op, proposal);
|
|
326
343
|
if (Array.isArray(res) && res.length === 2) return res;
|
|
327
|
-
if (res !==
|
|
344
|
+
if (res !== void 0) {
|
|
328
345
|
console.warn(`${ctx.logTag} resolveName should return [ns, fn] or undefined (skipping)`);
|
|
329
346
|
}
|
|
330
347
|
}
|
|
@@ -366,10 +383,10 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
366
383
|
const name = normalizeIdentifier(x.name, true);
|
|
367
384
|
const type = makeType(ctx, x.schema);
|
|
368
385
|
urlReplacements[x.name] = name;
|
|
369
|
-
return f$1.createParameterDeclaration(
|
|
386
|
+
return f$1.createParameterDeclaration(void 0, void 0, name, void 0, type);
|
|
370
387
|
});
|
|
371
388
|
const cbArgs = filterNullable([
|
|
372
|
-
search.length ? f$1.createShorthandPropertyAssignment("search") :
|
|
389
|
+
search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
|
|
373
390
|
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
374
391
|
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
375
392
|
"headers",
|
|
@@ -379,16 +396,16 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
379
396
|
f$1.createStringLiteral(reqSchema[0])
|
|
380
397
|
)
|
|
381
398
|
])
|
|
382
|
-
) :
|
|
399
|
+
) : void 0
|
|
383
400
|
]);
|
|
384
401
|
return f$1.createPropertyAssignment(
|
|
385
402
|
f$1.createIdentifier(normalizeIdentifier(opName)),
|
|
386
403
|
f$1.createArrowFunction(
|
|
387
|
-
|
|
388
|
-
|
|
404
|
+
void 0,
|
|
405
|
+
void 0,
|
|
389
406
|
fnArgs,
|
|
390
|
-
|
|
391
|
-
|
|
407
|
+
void 0,
|
|
408
|
+
void 0,
|
|
392
409
|
f$1.createBlock([
|
|
393
410
|
f$1.createReturnStatement(
|
|
394
411
|
f$1.createCallExpression(
|
|
@@ -410,10 +427,10 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
410
427
|
};
|
|
411
428
|
const prepareNs = (ctx, name, handlers) => {
|
|
412
429
|
return f$1.createPropertyDeclaration(
|
|
413
|
-
|
|
430
|
+
void 0,
|
|
414
431
|
normalizeIdentifier(name),
|
|
415
|
-
|
|
416
|
-
|
|
432
|
+
void 0,
|
|
433
|
+
void 0,
|
|
417
434
|
f$1.createObjectLiteralExpression(handlers)
|
|
418
435
|
);
|
|
419
436
|
};
|
|
@@ -473,12 +490,23 @@ const generateAst = async (ctx) => {
|
|
|
473
490
|
}
|
|
474
491
|
return { modules, types };
|
|
475
492
|
};
|
|
476
|
-
const loadSchema = async (
|
|
493
|
+
const loadSchema = async ({
|
|
494
|
+
url,
|
|
495
|
+
upgrade = true,
|
|
496
|
+
headers = {}
|
|
497
|
+
}) => {
|
|
477
498
|
if (url.startsWith("file://")) url = url.substring(7);
|
|
478
499
|
const { bundle } = await redocly.bundle({
|
|
479
500
|
ref: url,
|
|
480
501
|
config: await redocly.createConfig({}),
|
|
481
|
-
removeUnusedComponents: false
|
|
502
|
+
removeUnusedComponents: false,
|
|
503
|
+
externalRefResolver: new BaseResolver({
|
|
504
|
+
http: {
|
|
505
|
+
headers: Object.entries(headers).map(([name, value]) => {
|
|
506
|
+
return { name, value, matches: "**" };
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
})
|
|
482
510
|
});
|
|
483
511
|
if (bundle.parsed.swagger && upgrade) {
|
|
484
512
|
const { openapi } = await convertObj(bundle.parsed, { patch: true });
|
|
@@ -516,7 +544,7 @@ const formatCode = async (code) => {
|
|
|
516
544
|
};
|
|
517
545
|
|
|
518
546
|
const apigen = async (config) => {
|
|
519
|
-
const doc = await loadSchema(config.source);
|
|
547
|
+
const doc = await loadSchema({ url: config.source, headers: config.headers });
|
|
520
548
|
const ctx = initCtx({ ...config, doc });
|
|
521
549
|
const { modules, types } = await generateAst(ctx);
|
|
522
550
|
const filepath = join(dirname(fileURLToPath(import.meta.url)), "_template.ts");
|
|
@@ -2,15 +2,15 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import { join, dirname } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { cli } from 'cleye';
|
|
5
|
-
import redocly from '@redocly/openapi-core';
|
|
5
|
+
import redocly, { BaseResolver } from '@redocly/openapi-core';
|
|
6
6
|
import { filterEmpty, filterNullable } from 'array-utils-ts';
|
|
7
|
-
import { get, uniq, upperFirst, isArray, isBoolean,
|
|
7
|
+
import { get, uniq, upperFirst, isArray, isBoolean, sortBy, isObject, lowerFirst, uniqBy } from 'lodash-es';
|
|
8
8
|
import { convertObj } from 'swagger2openapi';
|
|
9
9
|
import ts from 'typescript';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
|
|
12
12
|
var name = "apigen-ts";
|
|
13
|
-
var version = "1.
|
|
13
|
+
var version = "1.2.0";
|
|
14
14
|
|
|
15
15
|
const initCtx = (config) => {
|
|
16
16
|
return {
|
|
@@ -20,11 +20,20 @@ const initCtx = (config) => {
|
|
|
20
20
|
doc: { openapi: "3.1.0" },
|
|
21
21
|
parseDates: false,
|
|
22
22
|
inlineEnums: false,
|
|
23
|
+
headers: {},
|
|
23
24
|
...config,
|
|
24
25
|
logTag: "",
|
|
25
26
|
usedNames: /* @__PURE__ */ new Set()
|
|
26
27
|
};
|
|
27
28
|
};
|
|
29
|
+
const parseHeaders = (items) => {
|
|
30
|
+
const headers = {};
|
|
31
|
+
for (const item of items) {
|
|
32
|
+
const [key, val] = item.split(":");
|
|
33
|
+
if (key && val) headers[key.trim()] = val.trim();
|
|
34
|
+
}
|
|
35
|
+
return headers;
|
|
36
|
+
};
|
|
28
37
|
const getCliConfig = () => {
|
|
29
38
|
const argv = cli({
|
|
30
39
|
name,
|
|
@@ -33,18 +42,24 @@ const getCliConfig = () => {
|
|
|
33
42
|
flags: {
|
|
34
43
|
name: {
|
|
35
44
|
type: String,
|
|
36
|
-
description: "
|
|
45
|
+
description: "API class name to export",
|
|
37
46
|
default: "ApiClient"
|
|
38
47
|
},
|
|
39
48
|
parseDates: {
|
|
40
49
|
type: Boolean,
|
|
41
|
-
description: "
|
|
50
|
+
description: "Parse dates as Date objects",
|
|
42
51
|
default: false
|
|
43
52
|
},
|
|
44
53
|
inlineEnums: {
|
|
45
54
|
type: Boolean,
|
|
46
|
-
description: "
|
|
55
|
+
description: "Use inline enums instead of enum types",
|
|
47
56
|
default: false
|
|
57
|
+
},
|
|
58
|
+
header: {
|
|
59
|
+
type: [String],
|
|
60
|
+
alias: "H",
|
|
61
|
+
description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
|
|
62
|
+
default: []
|
|
48
63
|
}
|
|
49
64
|
}
|
|
50
65
|
});
|
|
@@ -53,13 +68,14 @@ const getCliConfig = () => {
|
|
|
53
68
|
output: argv._.output ?? null,
|
|
54
69
|
name: argv.flags.name,
|
|
55
70
|
parseDates: argv.flags.parseDates,
|
|
56
|
-
inlineEnums: argv.flags.inlineEnums
|
|
71
|
+
inlineEnums: argv.flags.inlineEnums,
|
|
72
|
+
headers: parseHeaders(argv.flags.header)
|
|
57
73
|
};
|
|
58
74
|
return config;
|
|
59
75
|
};
|
|
60
76
|
|
|
61
77
|
const unref = (ctx, s) => {
|
|
62
|
-
if (!s) return
|
|
78
|
+
if (!s) return void 0;
|
|
63
79
|
if ("$ref" in s && s.$ref) {
|
|
64
80
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
65
81
|
const obj = parts.reduce(
|
|
@@ -69,15 +85,15 @@ const unref = (ctx, s) => {
|
|
|
69
85
|
);
|
|
70
86
|
if (obj) return obj;
|
|
71
87
|
console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
|
|
72
|
-
return
|
|
88
|
+
return void 0;
|
|
73
89
|
}
|
|
74
90
|
return s;
|
|
75
91
|
};
|
|
76
92
|
const getReqSchema = (ctx, config) => {
|
|
77
93
|
const req = unref(ctx, config.requestBody);
|
|
78
|
-
if (!req) return
|
|
94
|
+
if (!req) return void 0;
|
|
79
95
|
const cts = Object.entries(req.content ?? {}).map((x) => [x[0].split(";")[0], x[1].schema]).filter((x) => x[1]);
|
|
80
|
-
if (cts.length === 0) return
|
|
96
|
+
if (cts.length === 0) return void 0;
|
|
81
97
|
const pretenders = [
|
|
82
98
|
"application/json",
|
|
83
99
|
"text/",
|
|
@@ -89,18 +105,18 @@ const getReqSchema = (ctx, config) => {
|
|
|
89
105
|
if (ct) return ct;
|
|
90
106
|
}
|
|
91
107
|
cts.map((x) => x[0]);
|
|
92
|
-
return
|
|
108
|
+
return void 0;
|
|
93
109
|
};
|
|
94
110
|
const getRepSchema = (ctx, config) => {
|
|
95
111
|
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x, "content"]));
|
|
96
112
|
const cts = Object.entries(get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
|
|
97
|
-
if (cts.length === 0) return
|
|
113
|
+
if (cts.length === 0) return void 0;
|
|
98
114
|
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
99
115
|
if (ctJson) return ctJson[1].schema;
|
|
100
116
|
const ctText = cts.find((x) => x[0].startsWith("text/"));
|
|
101
117
|
if (ctText) return { type: "string" };
|
|
102
118
|
cts.map((x) => x[0]).join(", ");
|
|
103
|
-
return
|
|
119
|
+
return void 0;
|
|
104
120
|
};
|
|
105
121
|
|
|
106
122
|
const f$2 = ts.factory;
|
|
@@ -176,9 +192,9 @@ const normalizeIdentifier = (val, asVar = false) => {
|
|
|
176
192
|
return name;
|
|
177
193
|
};
|
|
178
194
|
const makeInlineEnum = (s) => {
|
|
179
|
-
if (!s.enum) return
|
|
195
|
+
if (!s.enum) return void 0;
|
|
180
196
|
const values = filterEmpty(s.enum);
|
|
181
|
-
if (!values.length) return
|
|
197
|
+
if (!values.length) return void 0;
|
|
182
198
|
if (!s.type) {
|
|
183
199
|
if (values.every((x) => typeof x === "string")) s.type = "string";
|
|
184
200
|
if (values.every((x) => typeof x === "number")) s.type = "number";
|
|
@@ -199,7 +215,7 @@ const makeInlineEnum = (s) => {
|
|
|
199
215
|
return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
|
|
200
216
|
}
|
|
201
217
|
console.warn(`enum with unknown type "${s.type}" in`, s);
|
|
202
|
-
return
|
|
218
|
+
return void 0;
|
|
203
219
|
};
|
|
204
220
|
const makeObject = (ctx, s) => {
|
|
205
221
|
if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
|
|
@@ -211,9 +227,15 @@ const makeObject = (ctx, s) => {
|
|
|
211
227
|
}
|
|
212
228
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
|
|
213
229
|
};
|
|
230
|
+
const makeLiteralUnion = (ctx, types) => {
|
|
231
|
+
const tokens = types.map((x) => makeType(ctx, { type: x }));
|
|
232
|
+
const hasUnknown = tokens.some((x) => x.kind === ts.SyntaxKind.UnknownKeyword);
|
|
233
|
+
if (hasUnknown) return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
234
|
+
return f$2.createUnionTypeNode(tokens);
|
|
235
|
+
};
|
|
214
236
|
const makeType = (ctx, s) => {
|
|
215
237
|
const mk = makeType.bind(null, ctx);
|
|
216
|
-
if (s ===
|
|
238
|
+
if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
217
239
|
if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
|
|
218
240
|
if ("$ref" in s && s.$ref) {
|
|
219
241
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
@@ -237,28 +259,20 @@ const makeType = (ctx, s) => {
|
|
|
237
259
|
return f$2.createTypeLiteralNode(
|
|
238
260
|
Object.entries(s.properties).map(([k, v]) => {
|
|
239
261
|
const r = s.required ?? [];
|
|
240
|
-
const q = r.includes(k) ?
|
|
241
|
-
return f$2.createPropertySignature(
|
|
262
|
+
const q = r.includes(k) ? void 0 : f$2.createToken(ts.SyntaxKind.QuestionToken);
|
|
263
|
+
return f$2.createPropertySignature(void 0, f$2.createStringLiteral(k), q, mk(v));
|
|
242
264
|
})
|
|
243
265
|
);
|
|
244
266
|
}
|
|
245
267
|
if ("type" in s) {
|
|
246
|
-
if (Array.isArray(s.type)) {
|
|
247
|
-
const types = [];
|
|
248
|
-
for (const type of s.type) {
|
|
249
|
-
if (type === "null") types.push({ type: "null" });
|
|
250
|
-
else types.push({ ...s, type });
|
|
251
|
-
}
|
|
252
|
-
return mk({ oneOf: types });
|
|
253
|
-
}
|
|
254
268
|
let t;
|
|
255
269
|
if (s.type === "object") t = makeObject(ctx, s);
|
|
256
270
|
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
257
271
|
else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
258
272
|
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
259
|
-
else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
|
|
260
273
|
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
261
|
-
else if (isArray(s.type)) t =
|
|
274
|
+
else if (isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
275
|
+
else if (s.type === "array" && !isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
|
|
262
276
|
else {
|
|
263
277
|
console.warn(`makeType: unknown type "${s.type}"`);
|
|
264
278
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
@@ -267,7 +281,10 @@ const makeType = (ctx, s) => {
|
|
|
267
281
|
if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
|
|
268
282
|
if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
|
|
269
283
|
}
|
|
270
|
-
|
|
284
|
+
if ("nullable" in s && s.nullable) {
|
|
285
|
+
return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
|
|
286
|
+
}
|
|
287
|
+
return t;
|
|
271
288
|
}
|
|
272
289
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
273
290
|
};
|
|
@@ -295,7 +312,7 @@ const makeTypeAlias = (ctx, name, s) => {
|
|
|
295
312
|
return f$2.createTypeAliasDeclaration(
|
|
296
313
|
[f$2.createToken(ts.SyntaxKind.ExportKeyword)],
|
|
297
314
|
f$2.createIdentifier(normalizeIdentifier(name, true)),
|
|
298
|
-
|
|
315
|
+
void 0,
|
|
299
316
|
makeType(ctx, s)
|
|
300
317
|
);
|
|
301
318
|
};
|
|
@@ -324,7 +341,7 @@ const getOpName = (ctx, op) => {
|
|
|
324
341
|
if (ctx.resolveName) {
|
|
325
342
|
const res = ctx.resolveName(ctx, op, proposal);
|
|
326
343
|
if (Array.isArray(res) && res.length === 2) return res;
|
|
327
|
-
if (res !==
|
|
344
|
+
if (res !== void 0) {
|
|
328
345
|
console.warn(`${ctx.logTag} resolveName should return [ns, fn] or undefined (skipping)`);
|
|
329
346
|
}
|
|
330
347
|
}
|
|
@@ -366,10 +383,10 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
366
383
|
const name = normalizeIdentifier(x.name, true);
|
|
367
384
|
const type = makeType(ctx, x.schema);
|
|
368
385
|
urlReplacements[x.name] = name;
|
|
369
|
-
return f$1.createParameterDeclaration(
|
|
386
|
+
return f$1.createParameterDeclaration(void 0, void 0, name, void 0, type);
|
|
370
387
|
});
|
|
371
388
|
const cbArgs = filterNullable([
|
|
372
|
-
search.length ? f$1.createShorthandPropertyAssignment("search") :
|
|
389
|
+
search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
|
|
373
390
|
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
374
391
|
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
375
392
|
"headers",
|
|
@@ -379,16 +396,16 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
379
396
|
f$1.createStringLiteral(reqSchema[0])
|
|
380
397
|
)
|
|
381
398
|
])
|
|
382
|
-
) :
|
|
399
|
+
) : void 0
|
|
383
400
|
]);
|
|
384
401
|
return f$1.createPropertyAssignment(
|
|
385
402
|
f$1.createIdentifier(normalizeIdentifier(opName)),
|
|
386
403
|
f$1.createArrowFunction(
|
|
387
|
-
|
|
388
|
-
|
|
404
|
+
void 0,
|
|
405
|
+
void 0,
|
|
389
406
|
fnArgs,
|
|
390
|
-
|
|
391
|
-
|
|
407
|
+
void 0,
|
|
408
|
+
void 0,
|
|
392
409
|
f$1.createBlock([
|
|
393
410
|
f$1.createReturnStatement(
|
|
394
411
|
f$1.createCallExpression(
|
|
@@ -410,10 +427,10 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
410
427
|
};
|
|
411
428
|
const prepareNs = (ctx, name, handlers) => {
|
|
412
429
|
return f$1.createPropertyDeclaration(
|
|
413
|
-
|
|
430
|
+
void 0,
|
|
414
431
|
normalizeIdentifier(name),
|
|
415
|
-
|
|
416
|
-
|
|
432
|
+
void 0,
|
|
433
|
+
void 0,
|
|
417
434
|
f$1.createObjectLiteralExpression(handlers)
|
|
418
435
|
);
|
|
419
436
|
};
|
|
@@ -473,12 +490,23 @@ const generateAst = async (ctx) => {
|
|
|
473
490
|
}
|
|
474
491
|
return { modules, types };
|
|
475
492
|
};
|
|
476
|
-
const loadSchema = async (
|
|
493
|
+
const loadSchema = async ({
|
|
494
|
+
url,
|
|
495
|
+
upgrade = true,
|
|
496
|
+
headers = {}
|
|
497
|
+
}) => {
|
|
477
498
|
if (url.startsWith("file://")) url = url.substring(7);
|
|
478
499
|
const { bundle } = await redocly.bundle({
|
|
479
500
|
ref: url,
|
|
480
501
|
config: await redocly.createConfig({}),
|
|
481
|
-
removeUnusedComponents: false
|
|
502
|
+
removeUnusedComponents: false,
|
|
503
|
+
externalRefResolver: new BaseResolver({
|
|
504
|
+
http: {
|
|
505
|
+
headers: Object.entries(headers).map(([name, value]) => {
|
|
506
|
+
return { name, value, matches: "**" };
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
})
|
|
482
510
|
});
|
|
483
511
|
if (bundle.parsed.swagger && upgrade) {
|
|
484
512
|
const { openapi } = await convertObj(bundle.parsed, { patch: true });
|
|
@@ -516,7 +544,7 @@ const formatCode = async (code) => {
|
|
|
516
544
|
};
|
|
517
545
|
|
|
518
546
|
const apigen = async (config) => {
|
|
519
|
-
const doc = await loadSchema(config.source);
|
|
547
|
+
const doc = await loadSchema({ url: config.source, headers: config.headers });
|
|
520
548
|
const ctx = initCtx({ ...config, doc });
|
|
521
549
|
const { modules, types } = await generateAst(ctx);
|
|
522
550
|
const filepath = join(dirname(fileURLToPath(import.meta.url)), "_template.ts");
|
package/dist/main.cjs
CHANGED
package/dist/main.d.cts
CHANGED
package/dist/main.d.mts
CHANGED
package/dist/main.js
CHANGED
package/dist/main.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "apigen-ts",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vladkens <v.pronsky@gmail.com>",
|
|
7
7
|
"repository": "vladkens/apigen-ts",
|
|
@@ -22,23 +22,23 @@
|
|
|
22
22
|
"ci": "tsc --noEmit && yarn test-cov && yarn build"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@redocly/openapi-core": "1.
|
|
25
|
+
"@redocly/openapi-core": "1.34.1",
|
|
26
26
|
"@types/lodash-es": "4.17.12",
|
|
27
27
|
"@types/swagger2openapi": "7.0.4",
|
|
28
28
|
"array-utils-ts": "1.0.2",
|
|
29
|
-
"cleye": "1.3.
|
|
29
|
+
"cleye": "1.3.4",
|
|
30
30
|
"lodash-es": "4.17.21",
|
|
31
31
|
"swagger2openapi": "7.0.8"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@types/node": "22.
|
|
34
|
+
"@types/node": "22.14.0",
|
|
35
35
|
"c8": "10.1.3",
|
|
36
|
-
"fetch-mock": "12.2
|
|
37
|
-
"pkgroll": "2.
|
|
38
|
-
"prettier": "3.
|
|
36
|
+
"fetch-mock": "12.5.2",
|
|
37
|
+
"pkgroll": "2.12.1",
|
|
38
|
+
"prettier": "3.5.3",
|
|
39
39
|
"prettier-plugin-organize-imports": "4.1.0",
|
|
40
40
|
"tsm": "2.3.0",
|
|
41
|
-
"typescript": "5.
|
|
41
|
+
"typescript": "5.8.3",
|
|
42
42
|
"uvu": "0.5.6"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
package/readme.md
CHANGED
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
[<img src="https://
|
|
6
|
-
[<img src="https://
|
|
7
|
-
[<img src="https://
|
|
8
|
-
[<img src="https://
|
|
9
|
-
[<img src="https://
|
|
5
|
+
[<img src="https://badges.ws/npm/v/apigen-ts" alt="version" />](https://npmjs.org/package/apigen-ts)
|
|
6
|
+
[<img src="https://badges.ws/packagephobia/publish/apigen-ts" alt="size" />](https://packagephobia.now.sh/result?p=apigen-ts)
|
|
7
|
+
[<img src="https://badges.ws/npm/dm/apigen-ts" alt="downloads" />](https://npmjs.org/package/apigen-ts)
|
|
8
|
+
[<img src="https://badges.ws/github/license/vladkens/apigen-ts" alt="license" />](https://github.com/vladkens/apigen-ts/blob/main/LICENSE)
|
|
9
|
+
[<img src="https://badges.ws/badge/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee&label" alt="donate" />](https://buymeacoffee.com/vladkens)
|
|
10
10
|
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
13
|
<div align="center">
|
|
14
14
|
<img src="./logo.svg" alt="apigen-ts logo" height="80" />
|
|
15
|
-
<div>Simple typed OpenAPI client generator</div>
|
|
16
15
|
</div>
|
|
17
16
|
|
|
18
17
|
## Features
|
|
@@ -29,7 +28,11 @@
|
|
|
29
28
|
## Install
|
|
30
29
|
|
|
31
30
|
```sh
|
|
32
|
-
|
|
31
|
+
npm install apigen-ts --save-dev
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
yarn add -D apigen-ts
|
|
33
36
|
```
|
|
34
37
|
|
|
35
38
|
## Usage
|
|
@@ -37,11 +40,14 @@ yarn install -D apigen-ts
|
|
|
37
40
|
### 1. Generate
|
|
38
41
|
|
|
39
42
|
```sh
|
|
43
|
+
# From file
|
|
44
|
+
yarn apigen-ts ./openapi.json ./api-client.ts
|
|
45
|
+
|
|
40
46
|
# From url
|
|
41
47
|
yarn apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts
|
|
42
48
|
|
|
43
|
-
# From
|
|
44
|
-
yarn apigen-ts
|
|
49
|
+
# From protected url
|
|
50
|
+
yarn apigen-ts https://secret-api.example.com ./api-client.ts -H "x-api-key: secret-key"
|
|
45
51
|
```
|
|
46
52
|
|
|
47
53
|
Run `yarn apigen-ts --help` for more options. Examples of generated clients [here](./examples/).
|
|
@@ -177,6 +183,7 @@ await apigen({
|
|
|
177
183
|
name: "MyApiClient", // default "ApiClient"
|
|
178
184
|
parseDates: true, // default false
|
|
179
185
|
inlineEnums: false, // default false, use string literal union instead of enum
|
|
186
|
+
headers: { "x-api-key": "secret-key" }, // Custom HTTP headers to use when fetching schema
|
|
180
187
|
resolveName(ctx, op, proposal) {
|
|
181
188
|
// proposal is [string, string] which represents module.funcName
|
|
182
189
|
if (proposal[0] === "users") return // will use default proposal
|