apigen-ts 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_template.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/{main-l0LIDQ3K.mjs → main-B8jUVaQq.js} +91 -14
- package/dist/main.cjs +570 -11
- package/dist/main.d.cts +4 -0
- package/dist/main.d.mts +4 -0
- package/dist/main.js +1 -1
- package/dist/main.mjs +572 -10
- package/package.json +16 -18
- package/readme.md +103 -83
- package/dist/cli.cjs +0 -18
- package/dist/cli.mjs +0 -16
- package/dist/main-C0qK6dZX.cjs +0 -580
- package/dist/main-l0LIDQ3K.js +0 -576
package/dist/_template.ts
CHANGED
|
@@ -69,7 +69,7 @@ export class ApiClient {
|
|
|
69
69
|
const headers = new Headers({ ...configHeaders, ...opts.headers })
|
|
70
70
|
const ct = headers.get("content-type") ?? "application/json"
|
|
71
71
|
|
|
72
|
-
let body: FormData | URLSearchParams | string |
|
|
72
|
+
let body: FormData | URLSearchParams | string | null = null
|
|
73
73
|
|
|
74
74
|
if (ct === "multipart/form-data" || ct === "application/x-www-form-urlencoded") {
|
|
75
75
|
// https://stackoverflow.com/a/61053359/3664464
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ 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
|
|
5
|
+
import { bundle, BaseResolver, createConfig } from '@redocly/openapi-core';
|
|
6
6
|
import { filterEmpty, filterNullable } from 'array-utils-ts';
|
|
7
7
|
import { get, uniq, upperFirst, isArray, isBoolean, sortBy, isObject, lowerFirst, uniqBy } from 'lodash-es';
|
|
8
8
|
import { convertObj } from 'swagger2openapi';
|
|
@@ -10,7 +10,7 @@ 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.3.0";
|
|
14
14
|
|
|
15
15
|
const initCtx = (config) => {
|
|
16
16
|
return {
|
|
@@ -20,6 +20,7 @@ const initCtx = (config) => {
|
|
|
20
20
|
doc: { openapi: "3.1.0" },
|
|
21
21
|
parseDates: false,
|
|
22
22
|
inlineEnums: false,
|
|
23
|
+
fetchOptions: false,
|
|
23
24
|
headers: {},
|
|
24
25
|
...config,
|
|
25
26
|
logTag: "",
|
|
@@ -55,21 +56,51 @@ const getCliConfig = () => {
|
|
|
55
56
|
description: "Use inline enums instead of enum types",
|
|
56
57
|
default: false
|
|
57
58
|
},
|
|
59
|
+
fetchOptions: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
description: "Add fetch options (e.g. AbortSignal) as last argument to each method",
|
|
62
|
+
default: false
|
|
63
|
+
},
|
|
58
64
|
header: {
|
|
59
65
|
type: [String],
|
|
60
66
|
alias: "H",
|
|
61
67
|
description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
|
|
62
68
|
default: []
|
|
69
|
+
},
|
|
70
|
+
filterPaths: {
|
|
71
|
+
type: String,
|
|
72
|
+
description: "Filter endpoints by path regex (e.g., --filter-paths '^/accounts')"
|
|
73
|
+
},
|
|
74
|
+
includeTags: {
|
|
75
|
+
type: [String],
|
|
76
|
+
description: "Only include operations with these tags (comma-separated or repeated flag)",
|
|
77
|
+
default: []
|
|
78
|
+
},
|
|
79
|
+
excludeTags: {
|
|
80
|
+
type: [String],
|
|
81
|
+
description: "Exclude operations with these tags (comma-separated or repeated flag)",
|
|
82
|
+
default: []
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
85
|
});
|
|
86
|
+
const parseTags = (items) => {
|
|
87
|
+
const tags = items.flatMap((x) => x.split(",")).map((x) => x.trim()).filter(Boolean);
|
|
88
|
+
return tags.length ? tags : void 0;
|
|
89
|
+
};
|
|
90
|
+
const filterPaths = argv.flags.filterPaths ? new RegExp(argv.flags.filterPaths) : void 0;
|
|
91
|
+
const includeTags = parseTags(argv.flags.includeTags);
|
|
92
|
+
const excludeTags = parseTags(argv.flags.excludeTags);
|
|
66
93
|
const config = {
|
|
67
94
|
source: argv._.source,
|
|
68
95
|
output: argv._.output ?? null,
|
|
69
96
|
name: argv.flags.name,
|
|
70
97
|
parseDates: argv.flags.parseDates,
|
|
71
98
|
inlineEnums: argv.flags.inlineEnums,
|
|
72
|
-
|
|
99
|
+
fetchOptions: argv.flags.fetchOptions,
|
|
100
|
+
headers: parseHeaders(argv.flags.header),
|
|
101
|
+
...filterPaths ? { filterPaths } : {},
|
|
102
|
+
...includeTags ? { includeTags } : {},
|
|
103
|
+
...excludeTags ? { excludeTags } : {}
|
|
73
104
|
};
|
|
74
105
|
return config;
|
|
75
106
|
};
|
|
@@ -108,8 +139,10 @@ const getReqSchema = (ctx, config) => {
|
|
|
108
139
|
return void 0;
|
|
109
140
|
};
|
|
110
141
|
const getRepSchema = (ctx, config) => {
|
|
111
|
-
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x
|
|
112
|
-
const
|
|
142
|
+
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x]));
|
|
143
|
+
const firstSuccess = unref(ctx, get(config, ["responses", successCodes[0]], {}));
|
|
144
|
+
if (!firstSuccess || !("content" in firstSuccess)) return void 0;
|
|
145
|
+
const cts = Object.entries(firstSuccess.content ?? {}).filter((x) => x[1].schema);
|
|
113
146
|
if (cts.length === 0) return void 0;
|
|
114
147
|
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
115
148
|
if (ctJson) return ctJson[1].schema;
|
|
@@ -205,8 +238,16 @@ const makeInlineEnum = (s) => {
|
|
|
205
238
|
return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
|
|
206
239
|
}
|
|
207
240
|
if (s.type === "number") {
|
|
208
|
-
const tokens = uniq(values).map((x) =>
|
|
209
|
-
|
|
241
|
+
const tokens = uniq(values).map((x) => {
|
|
242
|
+
const n = x;
|
|
243
|
+
if (n < 0) {
|
|
244
|
+
return f$2.createLiteralTypeNode(
|
|
245
|
+
f$2.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, f$2.createNumericLiteral(-n))
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
return f$2.createLiteralTypeNode(f$2.createNumericLiteral(n));
|
|
249
|
+
});
|
|
250
|
+
return f$2.createUnionTypeNode(tokens);
|
|
210
251
|
}
|
|
211
252
|
if (s.type === "boolean") {
|
|
212
253
|
const tokens = [];
|
|
@@ -256,22 +297,29 @@ const makeType = (ctx, s) => {
|
|
|
256
297
|
if (t) return isArray2 ? f$2.createArrayTypeNode(t) : t;
|
|
257
298
|
}
|
|
258
299
|
if ("properties" in s && s.properties) {
|
|
259
|
-
|
|
300
|
+
let t = f$2.createTypeLiteralNode(
|
|
260
301
|
Object.entries(s.properties).map(([k, v]) => {
|
|
261
302
|
const r = s.required ?? [];
|
|
262
303
|
const q = r.includes(k) ? void 0 : f$2.createToken(ts.SyntaxKind.QuestionToken);
|
|
263
304
|
return f$2.createPropertySignature(void 0, f$2.createStringLiteral(k), q, mk(v));
|
|
264
305
|
})
|
|
265
306
|
);
|
|
307
|
+
if ("nullable" in s && s.nullable) {
|
|
308
|
+
return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
|
|
309
|
+
}
|
|
310
|
+
return t;
|
|
266
311
|
}
|
|
267
312
|
if ("type" in s) {
|
|
268
313
|
let t;
|
|
269
314
|
if (s.type === "object") t = makeObject(ctx, s);
|
|
270
315
|
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
271
316
|
else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
317
|
+
else if (isConstantString(s)) t = f$2.createLiteralTypeNode(f$2.createStringLiteral(s.const));
|
|
272
318
|
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
273
319
|
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
274
320
|
else if (isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
321
|
+
else if (s.type === "array" && isPrefixItems(s) && s.prefixItems && !s.items)
|
|
322
|
+
t = f$2.createTupleTypeNode(s.prefixItems.map(mk));
|
|
275
323
|
else if (s.type === "array" && !isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
|
|
276
324
|
else {
|
|
277
325
|
console.warn(`makeType: unknown type "${s.type}"`);
|
|
@@ -294,6 +342,12 @@ const isStringEnum = (s) => {
|
|
|
294
342
|
}
|
|
295
343
|
return false;
|
|
296
344
|
};
|
|
345
|
+
const isPrefixItems = (s) => {
|
|
346
|
+
return s.prefixItems !== void 0;
|
|
347
|
+
};
|
|
348
|
+
const isConstantString = (s) => {
|
|
349
|
+
return s.type === "string" && s.const !== void 0;
|
|
350
|
+
};
|
|
297
351
|
const makeTypeAlias = (ctx, name, s) => {
|
|
298
352
|
if (isStringEnum(s) && !ctx.inlineEnums) {
|
|
299
353
|
const tokens1 = uniq(s.enum);
|
|
@@ -385,7 +439,19 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
385
439
|
urlReplacements[x.name] = name;
|
|
386
440
|
return f$1.createParameterDeclaration(void 0, void 0, name, void 0, type);
|
|
387
441
|
});
|
|
442
|
+
if (ctx.fetchOptions) {
|
|
443
|
+
fnArgs.push(
|
|
444
|
+
f$1.createParameterDeclaration(
|
|
445
|
+
void 0,
|
|
446
|
+
void 0,
|
|
447
|
+
"opts",
|
|
448
|
+
f$1.createToken(ts.SyntaxKind.QuestionToken),
|
|
449
|
+
f$1.createTypeReferenceNode(f$1.createIdentifier("ApigenRequest"), void 0)
|
|
450
|
+
)
|
|
451
|
+
);
|
|
452
|
+
}
|
|
388
453
|
const cbArgs = filterNullable([
|
|
454
|
+
ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
|
|
389
455
|
search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
|
|
390
456
|
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
391
457
|
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
@@ -439,6 +505,10 @@ const prepareRoutes = async (ctx) => {
|
|
|
439
505
|
for (const [path, pathConfig] of Object.entries(ctx.doc.paths ?? {})) {
|
|
440
506
|
ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
|
|
441
507
|
if (!isObject(pathConfig)) continue;
|
|
508
|
+
if (ctx.filterPaths) {
|
|
509
|
+
const match = ctx.filterPaths instanceof RegExp ? ctx.filterPaths.test(path) : ctx.filterPaths(path);
|
|
510
|
+
if (!match) continue;
|
|
511
|
+
}
|
|
442
512
|
if ("$ref" in pathConfig) {
|
|
443
513
|
console.warn(`${ctx.logTag} $ref should be resolved before (skipping)`);
|
|
444
514
|
continue;
|
|
@@ -450,6 +520,9 @@ const prepareRoutes = async (ctx) => {
|
|
|
450
520
|
if (pathConfig.parameters) {
|
|
451
521
|
config.parameters = [...config.parameters ?? [], ...pathConfig.parameters];
|
|
452
522
|
}
|
|
523
|
+
const opTags = config.tags ?? [];
|
|
524
|
+
if (ctx.includeTags?.length && !opTags.some((t) => ctx.includeTags.includes(t))) continue;
|
|
525
|
+
if (ctx.excludeTags?.length && opTags.some((t) => ctx.excludeTags.includes(t))) continue;
|
|
453
526
|
const [ns, op] = getOpName(ctx, { ...config, method, path });
|
|
454
527
|
if (!routes[ns]) routes[ns] = [];
|
|
455
528
|
const joined = [ns, op].join(".");
|
|
@@ -496,9 +569,9 @@ const loadSchema = async ({
|
|
|
496
569
|
headers = {}
|
|
497
570
|
}) => {
|
|
498
571
|
if (url.startsWith("file://")) url = url.substring(7);
|
|
499
|
-
const { bundle } = await
|
|
572
|
+
const { bundle: bundle$1 } = await bundle({
|
|
500
573
|
ref: url,
|
|
501
|
-
config: await
|
|
574
|
+
config: await createConfig({}),
|
|
502
575
|
removeUnusedComponents: false,
|
|
503
576
|
externalRefResolver: new BaseResolver({
|
|
504
577
|
http: {
|
|
@@ -508,11 +581,12 @@ const loadSchema = async ({
|
|
|
508
581
|
}
|
|
509
582
|
})
|
|
510
583
|
});
|
|
511
|
-
|
|
512
|
-
|
|
584
|
+
const parsed = bundle$1.parsed;
|
|
585
|
+
if ("swagger" in parsed && upgrade) {
|
|
586
|
+
const { openapi } = await convertObj(parsed, { patch: true });
|
|
513
587
|
return openapi;
|
|
514
588
|
}
|
|
515
|
-
return
|
|
589
|
+
return parsed;
|
|
516
590
|
};
|
|
517
591
|
|
|
518
592
|
const f = ts.factory;
|
|
@@ -544,7 +618,10 @@ const formatCode = async (code) => {
|
|
|
544
618
|
};
|
|
545
619
|
|
|
546
620
|
const apigen = async (config) => {
|
|
547
|
-
const doc = await loadSchema({
|
|
621
|
+
const doc = await loadSchema({
|
|
622
|
+
url: config.source,
|
|
623
|
+
...config.headers ? { headers: config.headers } : {}
|
|
624
|
+
});
|
|
548
625
|
const ctx = initCtx({ ...config, doc });
|
|
549
626
|
const { modules, types } = await generateAst(ctx);
|
|
550
627
|
const filepath = join(dirname(fileURLToPath(import.meta.url)), "_template.ts");
|