apigen-ts 1.3.1 → 1.5.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 +15 -2
- package/dist/cli.js +1 -1
- package/dist/{main-BiX2OIVV.js → main-Cn0HtcUt.js} +122 -54
- package/dist/main.cjs +120 -52
- package/dist/main.js +1 -1
- package/dist/main.mjs +121 -53
- package/package.json +15 -9
- package/readme.md +13 -13
package/dist/_template.ts
CHANGED
|
@@ -54,11 +54,24 @@ export class ApiClient {
|
|
|
54
54
|
return new URL(path, base)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// same as "${v}" in path template, but keeps "/", "?", "#" inside the path segment
|
|
58
|
+
EncodePathParam(v: unknown) {
|
|
59
|
+
return encodeURIComponent(String(v))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// query params with explode: false, eg. Swagger 2 `collectionFormat: csv`
|
|
63
|
+
JoinQueryParam(v: unknown, sep: string) {
|
|
64
|
+
return Array.isArray(v) ? v.join(sep) : v
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
async Fetch<T>(method: string, path: string, opts: ApigenRequest = {}): Promise<T> {
|
|
58
68
|
const url = this.PrepareFetchUrl(path)
|
|
59
69
|
|
|
70
|
+
// skip omitted values; arrays repeat the key per item (OpenAPI default: form + explode)
|
|
60
71
|
for (const [k, v] of Object.entries(opts?.search ?? {})) {
|
|
61
|
-
|
|
72
|
+
for (const x of Array.isArray(v) ? v : [v]) {
|
|
73
|
+
if (x !== undefined && x !== null) url.searchParams.append(k, x as string)
|
|
74
|
+
}
|
|
62
75
|
}
|
|
63
76
|
|
|
64
77
|
const configHeaders =
|
|
@@ -76,7 +89,7 @@ export class ApiClient {
|
|
|
76
89
|
headers.delete("content-type")
|
|
77
90
|
body = ct === "multipart/form-data" ? new FormData() : new URLSearchParams()
|
|
78
91
|
for (const [k, v] of Object.entries(opts.body as Record<string, string>)) {
|
|
79
|
-
body.append(k, v)
|
|
92
|
+
if (v !== undefined && v !== null) body.append(k, v)
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
|
package/dist/cli.js
CHANGED
|
@@ -4,13 +4,13 @@ import { fileURLToPath } from 'url';
|
|
|
4
4
|
import { cli } from 'cleye';
|
|
5
5
|
import { bundle, BaseResolver, createConfig } from '@redocly/openapi-core';
|
|
6
6
|
import { filterEmpty, filterNullable } from 'array-utils-ts';
|
|
7
|
-
import { get, uniq, upperFirst,
|
|
7
|
+
import { get, uniq, upperFirst, isBoolean, isArray, 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.5.0";
|
|
14
14
|
|
|
15
15
|
const initCtx = (config) => {
|
|
16
16
|
return {
|
|
@@ -106,7 +106,8 @@ const getCliConfig = () => {
|
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
const unref = (ctx, s) => {
|
|
109
|
-
if (
|
|
109
|
+
if (s === void 0) return void 0;
|
|
110
|
+
if (typeof s !== "object" || s === null) return s;
|
|
110
111
|
if ("$ref" in s && s.$ref) {
|
|
111
112
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
112
113
|
const obj = parts.reduce(
|
|
@@ -114,16 +115,24 @@ const unref = (ctx, s) => {
|
|
|
114
115
|
(acc, x) => get(acc, x, get(acc, decodeURIComponent(x).replaceAll("~1", "/"))),
|
|
115
116
|
ctx.doc
|
|
116
117
|
);
|
|
117
|
-
if (obj) return obj;
|
|
118
|
+
if (obj !== void 0) return obj;
|
|
118
119
|
console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
|
|
119
120
|
return void 0;
|
|
120
121
|
}
|
|
121
122
|
return s;
|
|
122
123
|
};
|
|
124
|
+
const getContent = (ctx, content) => {
|
|
125
|
+
const result = [];
|
|
126
|
+
for (const [cType, value] of Object.entries(content ?? {})) {
|
|
127
|
+
const mType = unref(ctx, value);
|
|
128
|
+
if (mType) result.push([cType.split(";")[0], mType]);
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
};
|
|
123
132
|
const getReqSchema = (ctx, config) => {
|
|
124
133
|
const req = unref(ctx, config.requestBody);
|
|
125
134
|
if (!req) return void 0;
|
|
126
|
-
const cts =
|
|
135
|
+
const cts = getContent(ctx, req.content).map((x) => [x[0], x[1].schema]).filter((x) => x[1] !== void 0);
|
|
127
136
|
if (cts.length === 0) return void 0;
|
|
128
137
|
const pretenders = [
|
|
129
138
|
"application/json",
|
|
@@ -138,12 +147,15 @@ const getReqSchema = (ctx, config) => {
|
|
|
138
147
|
cts.map((x) => x[0]);
|
|
139
148
|
return void 0;
|
|
140
149
|
};
|
|
141
|
-
const
|
|
150
|
+
const getRepContent = (ctx, config) => {
|
|
142
151
|
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x]));
|
|
143
152
|
const firstSuccess = unref(ctx, get(config, ["responses", successCodes[0]], {}));
|
|
144
|
-
if (!firstSuccess || !("content" in firstSuccess)) return
|
|
145
|
-
|
|
146
|
-
|
|
153
|
+
if (!firstSuccess || !("content" in firstSuccess)) return [];
|
|
154
|
+
return getContent(ctx, firstSuccess.content);
|
|
155
|
+
};
|
|
156
|
+
const getRepSchema = (ctx, config) => {
|
|
157
|
+
const cts = getRepContent(ctx, config).filter((x) => x[1].schema !== void 0);
|
|
158
|
+
if (!cts.length) return void 0;
|
|
147
159
|
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
148
160
|
if (ctJson) return ctJson[1].schema;
|
|
149
161
|
const ctText = cts.find((x) => x[0].startsWith("text/"));
|
|
@@ -213,10 +225,22 @@ const Keywords = /* @__PURE__ */ new Set([
|
|
|
213
225
|
"Exclude",
|
|
214
226
|
"Extract",
|
|
215
227
|
// ts keywords
|
|
228
|
+
"Array",
|
|
216
229
|
"Date",
|
|
217
|
-
"
|
|
218
|
-
"
|
|
219
|
-
|
|
230
|
+
"File",
|
|
231
|
+
"FormData",
|
|
232
|
+
"Headers",
|
|
233
|
+
"JSON",
|
|
234
|
+
"Object",
|
|
235
|
+
"Promise",
|
|
236
|
+
"RequestInit",
|
|
237
|
+
"Response",
|
|
238
|
+
"URL",
|
|
239
|
+
"URLSearchParams",
|
|
240
|
+
"fetch",
|
|
241
|
+
"globalThis",
|
|
242
|
+
"object"
|
|
243
|
+
// runtime names
|
|
220
244
|
]);
|
|
221
245
|
const normalizeIdentifier = (val, asVar = false) => {
|
|
222
246
|
let name = val.replace("#/components/schemas/", "").replaceAll("'", "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
@@ -225,7 +249,7 @@ const normalizeIdentifier = (val, asVar = false) => {
|
|
|
225
249
|
return name;
|
|
226
250
|
};
|
|
227
251
|
const makeInlineEnum = (s) => {
|
|
228
|
-
if (!s.enum) return void 0;
|
|
252
|
+
if (isBoolean(s) || !s.enum) return void 0;
|
|
229
253
|
const values = filterEmpty(s.enum);
|
|
230
254
|
if (!values.length) return void 0;
|
|
231
255
|
if (!s.type) {
|
|
@@ -258,8 +282,16 @@ const makeInlineEnum = (s) => {
|
|
|
258
282
|
console.warn(`enum with unknown type "${s.type}" in`, s);
|
|
259
283
|
return void 0;
|
|
260
284
|
};
|
|
285
|
+
const makeConst = (s) => {
|
|
286
|
+
if (isBoolean(s) || !("const" in s)) return void 0;
|
|
287
|
+
const value = s.const;
|
|
288
|
+
if (!["string", "number", "boolean"].includes(typeof value)) return void 0;
|
|
289
|
+
const t = makeInlineEnum({ ...s, enum: [value] });
|
|
290
|
+
return t && ts.isUnionTypeNode(t) ? t.types[0] : t;
|
|
291
|
+
};
|
|
261
292
|
const makeObject = (ctx, s) => {
|
|
262
|
-
if (s.type !== "object")
|
|
293
|
+
if (isBoolean(s) || s.type !== "object")
|
|
294
|
+
throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
|
|
263
295
|
if (s.additionalProperties && !isBoolean(s.additionalProperties)) {
|
|
264
296
|
return f$2.createTypeReferenceNode("Record", [
|
|
265
297
|
f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
@@ -278,19 +310,24 @@ const makeType = (ctx, s) => {
|
|
|
278
310
|
const mk = makeType.bind(null, ctx);
|
|
279
311
|
if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
280
312
|
if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
|
|
313
|
+
if (isBoolean(s)) {
|
|
314
|
+
return f$2.createKeywordTypeNode(s ? ts.SyntaxKind.UnknownKeyword : ts.SyntaxKind.NeverKeyword);
|
|
315
|
+
}
|
|
281
316
|
if ("$ref" in s && s.$ref) {
|
|
282
317
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
283
318
|
if (parts.length === 3 && parts[0] === "components" && parts[1] === "schemas") {
|
|
284
319
|
return f$2.createTypeReferenceNode(normalizeIdentifier(parts[2], true));
|
|
285
320
|
}
|
|
286
321
|
const t = unref(ctx, s);
|
|
287
|
-
if (
|
|
322
|
+
if (t === void 0) throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
|
|
288
323
|
return makeType(ctx, t);
|
|
289
324
|
}
|
|
290
325
|
if ("oneOf" in s && s.oneOf) return f$2.createUnionTypeNode(s.oneOf.map(mk));
|
|
291
326
|
if ("anyOf" in s && s.anyOf) return f$2.createUnionTypeNode(s.anyOf.map(mk));
|
|
292
327
|
if ("allOf" in s && s.allOf) return f$2.createIntersectionTypeNode(s.allOf.map(mk));
|
|
293
328
|
if ("type" in s && s.type === "integer") s.type = "number";
|
|
329
|
+
const constant = makeConst(s);
|
|
330
|
+
if (constant) return constant;
|
|
294
331
|
if ("enum" in s && s.enum && !Array.isArray(s.type)) {
|
|
295
332
|
const isArray2 = s.type === "array";
|
|
296
333
|
const t = makeInlineEnum(isArray2 ? { ...s, type: s.items?.type } : s);
|
|
@@ -314,7 +351,6 @@ const makeType = (ctx, s) => {
|
|
|
314
351
|
if (s.type === "object") t = makeObject(ctx, s);
|
|
315
352
|
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
316
353
|
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));
|
|
318
354
|
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
319
355
|
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
320
356
|
else if (isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
@@ -337,16 +373,14 @@ const makeType = (ctx, s) => {
|
|
|
337
373
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
338
374
|
};
|
|
339
375
|
const isStringEnum = (s) => {
|
|
376
|
+
if (isBoolean(s)) return false;
|
|
340
377
|
if ("enum" in s && s.enum) {
|
|
341
378
|
return s.enum.every((x) => typeof x === "string");
|
|
342
379
|
}
|
|
343
380
|
return false;
|
|
344
381
|
};
|
|
345
382
|
const isPrefixItems = (s) => {
|
|
346
|
-
return s.prefixItems !== void 0;
|
|
347
|
-
};
|
|
348
|
-
const isConstantString = (s) => {
|
|
349
|
-
return s.type === "string" && s.const !== void 0;
|
|
383
|
+
return !isBoolean(s) && s.prefixItems !== void 0;
|
|
350
384
|
};
|
|
351
385
|
const makeTypeAlias = (ctx, name, s) => {
|
|
352
386
|
if (isStringEnum(s) && !ctx.inlineEnums) {
|
|
@@ -372,7 +406,17 @@ const makeTypeAlias = (ctx, name, s) => {
|
|
|
372
406
|
};
|
|
373
407
|
|
|
374
408
|
const f$1 = ts.factory;
|
|
375
|
-
const HttpMethods = [
|
|
409
|
+
const HttpMethods = [
|
|
410
|
+
"get",
|
|
411
|
+
"post",
|
|
412
|
+
"put",
|
|
413
|
+
"patch",
|
|
414
|
+
"delete",
|
|
415
|
+
"head",
|
|
416
|
+
"options",
|
|
417
|
+
"trace",
|
|
418
|
+
"query"
|
|
419
|
+
];
|
|
376
420
|
const normalizeOpName = (val) => {
|
|
377
421
|
const articles = /* @__PURE__ */ new Set(["a", "an", "the"]);
|
|
378
422
|
const tmp = val.replace(/'/, "").replace(/[^a-zA-Z0-9]/g, "_").split("_").filter((x) => x !== "" && !articles.has(x)).map((x) => upperFirst(x));
|
|
@@ -411,21 +455,39 @@ const prepareUrl = (url, rename) => {
|
|
|
411
455
|
const [name, ...rest] = x.split("}");
|
|
412
456
|
const right = rest.join("}");
|
|
413
457
|
return f$1.createTemplateSpan(
|
|
414
|
-
f$1.createIdentifier(
|
|
415
|
-
|
|
458
|
+
f$1.createCallExpression(f$1.createIdentifier("this.EncodePathParam"), void 0, [
|
|
459
|
+
f$1.createIdentifier(name)
|
|
460
|
+
// no normalization required
|
|
461
|
+
]),
|
|
416
462
|
i === parts.length - 2 ? f$1.createTemplateTail(right) : f$1.createTemplateMiddle(right)
|
|
417
463
|
);
|
|
418
464
|
})
|
|
419
465
|
);
|
|
420
466
|
};
|
|
421
|
-
const
|
|
467
|
+
const prepareSearch = (params) => {
|
|
468
|
+
const separators = { form: ",", spaceDelimited: " ", pipeDelimited: "|" };
|
|
469
|
+
const joined = params.flatMap((x) => {
|
|
470
|
+
const style = x.style ?? "form";
|
|
471
|
+
if ((x.explode ?? style === "form") || !(style in separators)) return [];
|
|
472
|
+
const key = /^[a-zA-Z_$][\w$]*$/.test(x.name) ? f$1.createPropertyAccessExpression(f$1.createIdentifier("search"), x.name) : f$1.createElementAccessExpression(f$1.createIdentifier("search"), f$1.createStringLiteral(x.name));
|
|
473
|
+
const value = f$1.createCallExpression(f$1.createIdentifier("this.JoinQueryParam"), void 0, [
|
|
474
|
+
key,
|
|
475
|
+
f$1.createStringLiteral(separators[style])
|
|
476
|
+
]);
|
|
477
|
+
return [f$1.createPropertyAssignment(f$1.createStringLiteral(x.name), value)];
|
|
478
|
+
});
|
|
479
|
+
if (!joined.length) return f$1.createShorthandPropertyAssignment("search");
|
|
480
|
+
const search = [f$1.createSpreadAssignment(f$1.createIdentifier("search")), ...joined];
|
|
481
|
+
return f$1.createPropertyAssignment("search", f$1.createObjectLiteralExpression(search));
|
|
482
|
+
};
|
|
483
|
+
const prepareOp = (ctx, cfg, opName, httpMethod) => {
|
|
422
484
|
cfg.parameters = cfg.parameters ?? [];
|
|
423
485
|
const reqSchema = getReqSchema(ctx, cfg);
|
|
424
486
|
const repSchema = getRepSchema(ctx, cfg);
|
|
425
487
|
const allParams = filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
|
|
426
488
|
const params = uniqBy(allParams.filter((x) => x.in === "path"), "name");
|
|
427
489
|
if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
|
|
428
|
-
const search = allParams.filter((x) => x.in === "query");
|
|
490
|
+
const search = uniqBy(allParams.filter((x) => x.in === "query"), "name");
|
|
429
491
|
allParams.filter((x) => x.in === "header");
|
|
430
492
|
for (const [name, v] of Object.entries({ search })) {
|
|
431
493
|
if (!v.length) continue;
|
|
@@ -452,7 +514,7 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
452
514
|
}
|
|
453
515
|
const cbArgs = filterNullable([
|
|
454
516
|
ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
|
|
455
|
-
search.length ?
|
|
517
|
+
search.length ? prepareSearch(search) : void 0,
|
|
456
518
|
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
457
519
|
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
458
520
|
"headers",
|
|
@@ -478,7 +540,7 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
478
540
|
f$1.createIdentifier("this.Fetch"),
|
|
479
541
|
[makeType(ctx, repSchema)],
|
|
480
542
|
[
|
|
481
|
-
f$1.createStringLiteral(
|
|
543
|
+
f$1.createStringLiteral(httpMethod),
|
|
482
544
|
// method
|
|
483
545
|
prepareUrl(cfg.path, urlReplacements),
|
|
484
546
|
// path
|
|
@@ -500,43 +562,49 @@ const prepareNs = (ctx, name, handlers) => {
|
|
|
500
562
|
f$1.createObjectLiteralExpression(handlers)
|
|
501
563
|
);
|
|
502
564
|
};
|
|
565
|
+
const addOp = (ctx, routes, pathConfig, cfg, httpMethod) => {
|
|
566
|
+
ctx.logTag = `${httpMethod.padEnd(6, " ")} ${cfg.path}`;
|
|
567
|
+
if (pathConfig.parameters) {
|
|
568
|
+
cfg.parameters = [...cfg.parameters ?? [], ...pathConfig.parameters];
|
|
569
|
+
}
|
|
570
|
+
const opTags = cfg.tags ?? [];
|
|
571
|
+
if (ctx.includeTags?.length && !opTags.some((t) => ctx.includeTags.includes(t))) return;
|
|
572
|
+
if (ctx.excludeTags?.length && opTags.some((t) => ctx.excludeTags.includes(t))) return;
|
|
573
|
+
const [ns, op] = getOpName(ctx, cfg);
|
|
574
|
+
if (!routes[ns]) routes[ns] = [];
|
|
575
|
+
const joined = [ns, op].join(".");
|
|
576
|
+
if (ctx.usedNames.has(joined)) {
|
|
577
|
+
return;
|
|
578
|
+
} else {
|
|
579
|
+
ctx.usedNames.add(joined);
|
|
580
|
+
}
|
|
581
|
+
try {
|
|
582
|
+
routes[ns].push(prepareOp(ctx, cfg, op, httpMethod));
|
|
583
|
+
} catch (e) {
|
|
584
|
+
console.error(`${ctx.logTag} - ${e}`, cfg);
|
|
585
|
+
throw e;
|
|
586
|
+
}
|
|
587
|
+
};
|
|
503
588
|
const prepareRoutes = async (ctx) => {
|
|
504
589
|
const routes = {};
|
|
505
|
-
for (const [path,
|
|
590
|
+
for (const [path, value] of Object.entries(ctx.doc.paths ?? {})) {
|
|
506
591
|
ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
|
|
507
|
-
if (!isObject(
|
|
592
|
+
if (!isObject(value)) continue;
|
|
508
593
|
if (ctx.filterPaths) {
|
|
509
594
|
const match = ctx.filterPaths instanceof RegExp ? ctx.filterPaths.test(path) : ctx.filterPaths(path);
|
|
510
595
|
if (!match) continue;
|
|
511
596
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
597
|
+
const pathConfig = unref(ctx, value);
|
|
598
|
+
if (!pathConfig) continue;
|
|
516
599
|
for (const method of HttpMethods) {
|
|
517
|
-
ctx.logTag = `${method.toUpperCase().padEnd(6, " ")} ${path}`;
|
|
518
600
|
const config = pathConfig[method];
|
|
519
601
|
if (!config) continue;
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
if (
|
|
525
|
-
|
|
526
|
-
const [ns, op] = getOpName(ctx, { ...config, method, path });
|
|
527
|
-
if (!routes[ns]) routes[ns] = [];
|
|
528
|
-
const joined = [ns, op].join(".");
|
|
529
|
-
if (ctx.usedNames.has(joined)) {
|
|
530
|
-
continue;
|
|
531
|
-
} else {
|
|
532
|
-
ctx.usedNames.add(joined);
|
|
533
|
-
}
|
|
534
|
-
try {
|
|
535
|
-
routes[ns].push(prepareOp(ctx, { ...config, method, path }, op));
|
|
536
|
-
} catch (e) {
|
|
537
|
-
console.error(`${ctx.logTag} - ${e}`, config);
|
|
538
|
-
throw e;
|
|
539
|
-
}
|
|
602
|
+
addOp(ctx, routes, pathConfig, { ...config, method, path }, method.toUpperCase());
|
|
603
|
+
}
|
|
604
|
+
for (const [method, value2] of Object.entries(pathConfig.additionalOperations ?? {})) {
|
|
605
|
+
const config = unref(ctx, value2);
|
|
606
|
+
if (!config) continue;
|
|
607
|
+
addOp(ctx, routes, pathConfig, { ...config, method, path }, method);
|
|
540
608
|
}
|
|
541
609
|
}
|
|
542
610
|
return routes;
|
package/dist/main.cjs
CHANGED
|
@@ -29,7 +29,8 @@ const initCtx = (config) => {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
const unref = (ctx, s) => {
|
|
32
|
-
if (
|
|
32
|
+
if (s === void 0) return void 0;
|
|
33
|
+
if (typeof s !== "object" || s === null) return s;
|
|
33
34
|
if ("$ref" in s && s.$ref) {
|
|
34
35
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
35
36
|
const obj = parts.reduce(
|
|
@@ -37,16 +38,24 @@ const unref = (ctx, s) => {
|
|
|
37
38
|
(acc, x) => lodashEs.get(acc, x, lodashEs.get(acc, decodeURIComponent(x).replaceAll("~1", "/"))),
|
|
38
39
|
ctx.doc
|
|
39
40
|
);
|
|
40
|
-
if (obj) return obj;
|
|
41
|
+
if (obj !== void 0) return obj;
|
|
41
42
|
console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
|
|
42
43
|
return void 0;
|
|
43
44
|
}
|
|
44
45
|
return s;
|
|
45
46
|
};
|
|
47
|
+
const getContent = (ctx, content) => {
|
|
48
|
+
const result = [];
|
|
49
|
+
for (const [cType, value] of Object.entries(content ?? {})) {
|
|
50
|
+
const mType = unref(ctx, value);
|
|
51
|
+
if (mType) result.push([cType.split(";")[0], mType]);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
46
55
|
const getReqSchema = (ctx, config) => {
|
|
47
56
|
const req = unref(ctx, config.requestBody);
|
|
48
57
|
if (!req) return void 0;
|
|
49
|
-
const cts =
|
|
58
|
+
const cts = getContent(ctx, req.content).map((x) => [x[0], x[1].schema]).filter((x) => x[1] !== void 0);
|
|
50
59
|
if (cts.length === 0) return void 0;
|
|
51
60
|
const pretenders = [
|
|
52
61
|
"application/json",
|
|
@@ -61,12 +70,15 @@ const getReqSchema = (ctx, config) => {
|
|
|
61
70
|
cts.map((x) => x[0]);
|
|
62
71
|
return void 0;
|
|
63
72
|
};
|
|
64
|
-
const
|
|
73
|
+
const getRepContent = (ctx, config) => {
|
|
65
74
|
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => lodashEs.get(config, ["responses", x]));
|
|
66
75
|
const firstSuccess = unref(ctx, lodashEs.get(config, ["responses", successCodes[0]], {}));
|
|
67
|
-
if (!firstSuccess || !("content" in firstSuccess)) return
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
if (!firstSuccess || !("content" in firstSuccess)) return [];
|
|
77
|
+
return getContent(ctx, firstSuccess.content);
|
|
78
|
+
};
|
|
79
|
+
const getRepSchema = (ctx, config) => {
|
|
80
|
+
const cts = getRepContent(ctx, config).filter((x) => x[1].schema !== void 0);
|
|
81
|
+
if (!cts.length) return void 0;
|
|
70
82
|
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
71
83
|
if (ctJson) return ctJson[1].schema;
|
|
72
84
|
const ctText = cts.find((x) => x[0].startsWith("text/"));
|
|
@@ -136,10 +148,22 @@ const Keywords = /* @__PURE__ */ new Set([
|
|
|
136
148
|
"Exclude",
|
|
137
149
|
"Extract",
|
|
138
150
|
// ts keywords
|
|
151
|
+
"Array",
|
|
139
152
|
"Date",
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
|
|
153
|
+
"File",
|
|
154
|
+
"FormData",
|
|
155
|
+
"Headers",
|
|
156
|
+
"JSON",
|
|
157
|
+
"Object",
|
|
158
|
+
"Promise",
|
|
159
|
+
"RequestInit",
|
|
160
|
+
"Response",
|
|
161
|
+
"URL",
|
|
162
|
+
"URLSearchParams",
|
|
163
|
+
"fetch",
|
|
164
|
+
"globalThis",
|
|
165
|
+
"object"
|
|
166
|
+
// runtime names
|
|
143
167
|
]);
|
|
144
168
|
const normalizeIdentifier = (val, asVar = false) => {
|
|
145
169
|
let name = val.replace("#/components/schemas/", "").replaceAll("'", "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
@@ -148,7 +172,7 @@ const normalizeIdentifier = (val, asVar = false) => {
|
|
|
148
172
|
return name;
|
|
149
173
|
};
|
|
150
174
|
const makeInlineEnum = (s) => {
|
|
151
|
-
if (!s.enum) return void 0;
|
|
175
|
+
if (lodashEs.isBoolean(s) || !s.enum) return void 0;
|
|
152
176
|
const values = arrayUtilsTs.filterEmpty(s.enum);
|
|
153
177
|
if (!values.length) return void 0;
|
|
154
178
|
if (!s.type) {
|
|
@@ -181,8 +205,16 @@ const makeInlineEnum = (s) => {
|
|
|
181
205
|
console.warn(`enum with unknown type "${s.type}" in`, s);
|
|
182
206
|
return void 0;
|
|
183
207
|
};
|
|
208
|
+
const makeConst = (s) => {
|
|
209
|
+
if (lodashEs.isBoolean(s) || !("const" in s)) return void 0;
|
|
210
|
+
const value = s.const;
|
|
211
|
+
if (!["string", "number", "boolean"].includes(typeof value)) return void 0;
|
|
212
|
+
const t = makeInlineEnum({ ...s, enum: [value] });
|
|
213
|
+
return t && ts.isUnionTypeNode(t) ? t.types[0] : t;
|
|
214
|
+
};
|
|
184
215
|
const makeObject = (ctx, s) => {
|
|
185
|
-
if (s.type !== "object")
|
|
216
|
+
if (lodashEs.isBoolean(s) || s.type !== "object")
|
|
217
|
+
throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
|
|
186
218
|
if (s.additionalProperties && !lodashEs.isBoolean(s.additionalProperties)) {
|
|
187
219
|
return f$2.createTypeReferenceNode("Record", [
|
|
188
220
|
f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
@@ -201,19 +233,24 @@ const makeType = (ctx, s) => {
|
|
|
201
233
|
const mk = makeType.bind(null, ctx);
|
|
202
234
|
if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
203
235
|
if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
|
|
236
|
+
if (lodashEs.isBoolean(s)) {
|
|
237
|
+
return f$2.createKeywordTypeNode(s ? ts.SyntaxKind.UnknownKeyword : ts.SyntaxKind.NeverKeyword);
|
|
238
|
+
}
|
|
204
239
|
if ("$ref" in s && s.$ref) {
|
|
205
240
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
206
241
|
if (parts.length === 3 && parts[0] === "components" && parts[1] === "schemas") {
|
|
207
242
|
return f$2.createTypeReferenceNode(normalizeIdentifier(parts[2], true));
|
|
208
243
|
}
|
|
209
244
|
const t = unref(ctx, s);
|
|
210
|
-
if (
|
|
245
|
+
if (t === void 0) throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
|
|
211
246
|
return makeType(ctx, t);
|
|
212
247
|
}
|
|
213
248
|
if ("oneOf" in s && s.oneOf) return f$2.createUnionTypeNode(s.oneOf.map(mk));
|
|
214
249
|
if ("anyOf" in s && s.anyOf) return f$2.createUnionTypeNode(s.anyOf.map(mk));
|
|
215
250
|
if ("allOf" in s && s.allOf) return f$2.createIntersectionTypeNode(s.allOf.map(mk));
|
|
216
251
|
if ("type" in s && s.type === "integer") s.type = "number";
|
|
252
|
+
const constant = makeConst(s);
|
|
253
|
+
if (constant) return constant;
|
|
217
254
|
if ("enum" in s && s.enum && !Array.isArray(s.type)) {
|
|
218
255
|
const isArray2 = s.type === "array";
|
|
219
256
|
const t = makeInlineEnum(isArray2 ? { ...s, type: s.items?.type } : s);
|
|
@@ -237,7 +274,6 @@ const makeType = (ctx, s) => {
|
|
|
237
274
|
if (s.type === "object") t = makeObject(ctx, s);
|
|
238
275
|
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
239
276
|
else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
240
|
-
else if (isConstantString(s)) t = f$2.createLiteralTypeNode(f$2.createStringLiteral(s.const));
|
|
241
277
|
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
242
278
|
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
243
279
|
else if (lodashEs.isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
@@ -260,16 +296,14 @@ const makeType = (ctx, s) => {
|
|
|
260
296
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
261
297
|
};
|
|
262
298
|
const isStringEnum = (s) => {
|
|
299
|
+
if (lodashEs.isBoolean(s)) return false;
|
|
263
300
|
if ("enum" in s && s.enum) {
|
|
264
301
|
return s.enum.every((x) => typeof x === "string");
|
|
265
302
|
}
|
|
266
303
|
return false;
|
|
267
304
|
};
|
|
268
305
|
const isPrefixItems = (s) => {
|
|
269
|
-
return s.prefixItems !== void 0;
|
|
270
|
-
};
|
|
271
|
-
const isConstantString = (s) => {
|
|
272
|
-
return s.type === "string" && s.const !== void 0;
|
|
306
|
+
return !lodashEs.isBoolean(s) && s.prefixItems !== void 0;
|
|
273
307
|
};
|
|
274
308
|
const makeTypeAlias = (ctx, name, s) => {
|
|
275
309
|
if (isStringEnum(s) && !ctx.inlineEnums) {
|
|
@@ -295,7 +329,17 @@ const makeTypeAlias = (ctx, name, s) => {
|
|
|
295
329
|
};
|
|
296
330
|
|
|
297
331
|
const f$1 = ts.factory;
|
|
298
|
-
const HttpMethods = [
|
|
332
|
+
const HttpMethods = [
|
|
333
|
+
"get",
|
|
334
|
+
"post",
|
|
335
|
+
"put",
|
|
336
|
+
"patch",
|
|
337
|
+
"delete",
|
|
338
|
+
"head",
|
|
339
|
+
"options",
|
|
340
|
+
"trace",
|
|
341
|
+
"query"
|
|
342
|
+
];
|
|
299
343
|
const normalizeOpName = (val) => {
|
|
300
344
|
const articles = /* @__PURE__ */ new Set(["a", "an", "the"]);
|
|
301
345
|
const tmp = val.replace(/'/, "").replace(/[^a-zA-Z0-9]/g, "_").split("_").filter((x) => x !== "" && !articles.has(x)).map((x) => lodashEs.upperFirst(x));
|
|
@@ -334,21 +378,39 @@ const prepareUrl = (url, rename) => {
|
|
|
334
378
|
const [name, ...rest] = x.split("}");
|
|
335
379
|
const right = rest.join("}");
|
|
336
380
|
return f$1.createTemplateSpan(
|
|
337
|
-
f$1.createIdentifier(
|
|
338
|
-
|
|
381
|
+
f$1.createCallExpression(f$1.createIdentifier("this.EncodePathParam"), void 0, [
|
|
382
|
+
f$1.createIdentifier(name)
|
|
383
|
+
// no normalization required
|
|
384
|
+
]),
|
|
339
385
|
i === parts.length - 2 ? f$1.createTemplateTail(right) : f$1.createTemplateMiddle(right)
|
|
340
386
|
);
|
|
341
387
|
})
|
|
342
388
|
);
|
|
343
389
|
};
|
|
344
|
-
const
|
|
390
|
+
const prepareSearch = (params) => {
|
|
391
|
+
const separators = { form: ",", spaceDelimited: " ", pipeDelimited: "|" };
|
|
392
|
+
const joined = params.flatMap((x) => {
|
|
393
|
+
const style = x.style ?? "form";
|
|
394
|
+
if ((x.explode ?? style === "form") || !(style in separators)) return [];
|
|
395
|
+
const key = /^[a-zA-Z_$][\w$]*$/.test(x.name) ? f$1.createPropertyAccessExpression(f$1.createIdentifier("search"), x.name) : f$1.createElementAccessExpression(f$1.createIdentifier("search"), f$1.createStringLiteral(x.name));
|
|
396
|
+
const value = f$1.createCallExpression(f$1.createIdentifier("this.JoinQueryParam"), void 0, [
|
|
397
|
+
key,
|
|
398
|
+
f$1.createStringLiteral(separators[style])
|
|
399
|
+
]);
|
|
400
|
+
return [f$1.createPropertyAssignment(f$1.createStringLiteral(x.name), value)];
|
|
401
|
+
});
|
|
402
|
+
if (!joined.length) return f$1.createShorthandPropertyAssignment("search");
|
|
403
|
+
const search = [f$1.createSpreadAssignment(f$1.createIdentifier("search")), ...joined];
|
|
404
|
+
return f$1.createPropertyAssignment("search", f$1.createObjectLiteralExpression(search));
|
|
405
|
+
};
|
|
406
|
+
const prepareOp = (ctx, cfg, opName, httpMethod) => {
|
|
345
407
|
cfg.parameters = cfg.parameters ?? [];
|
|
346
408
|
const reqSchema = getReqSchema(ctx, cfg);
|
|
347
409
|
const repSchema = getRepSchema(ctx, cfg);
|
|
348
410
|
const allParams = arrayUtilsTs.filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
|
|
349
411
|
const params = lodashEs.uniqBy(allParams.filter((x) => x.in === "path"), "name");
|
|
350
412
|
if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
|
|
351
|
-
const search = allParams.filter((x) => x.in === "query");
|
|
413
|
+
const search = lodashEs.uniqBy(allParams.filter((x) => x.in === "query"), "name");
|
|
352
414
|
allParams.filter((x) => x.in === "header");
|
|
353
415
|
for (const [name, v] of Object.entries({ search })) {
|
|
354
416
|
if (!v.length) continue;
|
|
@@ -375,7 +437,7 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
375
437
|
}
|
|
376
438
|
const cbArgs = arrayUtilsTs.filterNullable([
|
|
377
439
|
ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
|
|
378
|
-
search.length ?
|
|
440
|
+
search.length ? prepareSearch(search) : void 0,
|
|
379
441
|
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
380
442
|
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
381
443
|
"headers",
|
|
@@ -401,7 +463,7 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
401
463
|
f$1.createIdentifier("this.Fetch"),
|
|
402
464
|
[makeType(ctx, repSchema)],
|
|
403
465
|
[
|
|
404
|
-
f$1.createStringLiteral(
|
|
466
|
+
f$1.createStringLiteral(httpMethod),
|
|
405
467
|
// method
|
|
406
468
|
prepareUrl(cfg.path, urlReplacements),
|
|
407
469
|
// path
|
|
@@ -423,43 +485,49 @@ const prepareNs = (ctx, name, handlers) => {
|
|
|
423
485
|
f$1.createObjectLiteralExpression(handlers)
|
|
424
486
|
);
|
|
425
487
|
};
|
|
488
|
+
const addOp = (ctx, routes, pathConfig, cfg, httpMethod) => {
|
|
489
|
+
ctx.logTag = `${httpMethod.padEnd(6, " ")} ${cfg.path}`;
|
|
490
|
+
if (pathConfig.parameters) {
|
|
491
|
+
cfg.parameters = [...cfg.parameters ?? [], ...pathConfig.parameters];
|
|
492
|
+
}
|
|
493
|
+
const opTags = cfg.tags ?? [];
|
|
494
|
+
if (ctx.includeTags?.length && !opTags.some((t) => ctx.includeTags.includes(t))) return;
|
|
495
|
+
if (ctx.excludeTags?.length && opTags.some((t) => ctx.excludeTags.includes(t))) return;
|
|
496
|
+
const [ns, op] = getOpName(ctx, cfg);
|
|
497
|
+
if (!routes[ns]) routes[ns] = [];
|
|
498
|
+
const joined = [ns, op].join(".");
|
|
499
|
+
if (ctx.usedNames.has(joined)) {
|
|
500
|
+
return;
|
|
501
|
+
} else {
|
|
502
|
+
ctx.usedNames.add(joined);
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
routes[ns].push(prepareOp(ctx, cfg, op, httpMethod));
|
|
506
|
+
} catch (e) {
|
|
507
|
+
console.error(`${ctx.logTag} - ${e}`, cfg);
|
|
508
|
+
throw e;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
426
511
|
const prepareRoutes = async (ctx) => {
|
|
427
512
|
const routes = {};
|
|
428
|
-
for (const [path,
|
|
513
|
+
for (const [path, value] of Object.entries(ctx.doc.paths ?? {})) {
|
|
429
514
|
ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
|
|
430
|
-
if (!lodashEs.isObject(
|
|
515
|
+
if (!lodashEs.isObject(value)) continue;
|
|
431
516
|
if (ctx.filterPaths) {
|
|
432
517
|
const match = ctx.filterPaths instanceof RegExp ? ctx.filterPaths.test(path) : ctx.filterPaths(path);
|
|
433
518
|
if (!match) continue;
|
|
434
519
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
520
|
+
const pathConfig = unref(ctx, value);
|
|
521
|
+
if (!pathConfig) continue;
|
|
439
522
|
for (const method of HttpMethods) {
|
|
440
|
-
ctx.logTag = `${method.toUpperCase().padEnd(6, " ")} ${path}`;
|
|
441
523
|
const config = pathConfig[method];
|
|
442
524
|
if (!config) continue;
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
const [ns, op] = getOpName(ctx, { ...config, method, path });
|
|
450
|
-
if (!routes[ns]) routes[ns] = [];
|
|
451
|
-
const joined = [ns, op].join(".");
|
|
452
|
-
if (ctx.usedNames.has(joined)) {
|
|
453
|
-
continue;
|
|
454
|
-
} else {
|
|
455
|
-
ctx.usedNames.add(joined);
|
|
456
|
-
}
|
|
457
|
-
try {
|
|
458
|
-
routes[ns].push(prepareOp(ctx, { ...config, method, path }, op));
|
|
459
|
-
} catch (e) {
|
|
460
|
-
console.error(`${ctx.logTag} - ${e}`, config);
|
|
461
|
-
throw e;
|
|
462
|
-
}
|
|
525
|
+
addOp(ctx, routes, pathConfig, { ...config, method, path }, method.toUpperCase());
|
|
526
|
+
}
|
|
527
|
+
for (const [method, value2] of Object.entries(pathConfig.additionalOperations ?? {})) {
|
|
528
|
+
const config = unref(ctx, value2);
|
|
529
|
+
if (!config) continue;
|
|
530
|
+
addOp(ctx, routes, pathConfig, { ...config, method, path }, method);
|
|
463
531
|
}
|
|
464
532
|
}
|
|
465
533
|
return routes;
|
package/dist/main.js
CHANGED
package/dist/main.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
|
|
|
4
4
|
import 'cleye';
|
|
5
5
|
import { bundle, BaseResolver, createConfig } from '@redocly/openapi-core';
|
|
6
6
|
import { filterEmpty, filterNullable } from 'array-utils-ts';
|
|
7
|
-
import { get, uniq, upperFirst,
|
|
7
|
+
import { get, uniq, upperFirst, isBoolean, isArray, 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';
|
|
@@ -26,7 +26,8 @@ const initCtx = (config) => {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const unref = (ctx, s) => {
|
|
29
|
-
if (
|
|
29
|
+
if (s === void 0) return void 0;
|
|
30
|
+
if (typeof s !== "object" || s === null) return s;
|
|
30
31
|
if ("$ref" in s && s.$ref) {
|
|
31
32
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
32
33
|
const obj = parts.reduce(
|
|
@@ -34,16 +35,24 @@ const unref = (ctx, s) => {
|
|
|
34
35
|
(acc, x) => get(acc, x, get(acc, decodeURIComponent(x).replaceAll("~1", "/"))),
|
|
35
36
|
ctx.doc
|
|
36
37
|
);
|
|
37
|
-
if (obj) return obj;
|
|
38
|
+
if (obj !== void 0) return obj;
|
|
38
39
|
console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
|
|
39
40
|
return void 0;
|
|
40
41
|
}
|
|
41
42
|
return s;
|
|
42
43
|
};
|
|
44
|
+
const getContent = (ctx, content) => {
|
|
45
|
+
const result = [];
|
|
46
|
+
for (const [cType, value] of Object.entries(content ?? {})) {
|
|
47
|
+
const mType = unref(ctx, value);
|
|
48
|
+
if (mType) result.push([cType.split(";")[0], mType]);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
43
52
|
const getReqSchema = (ctx, config) => {
|
|
44
53
|
const req = unref(ctx, config.requestBody);
|
|
45
54
|
if (!req) return void 0;
|
|
46
|
-
const cts =
|
|
55
|
+
const cts = getContent(ctx, req.content).map((x) => [x[0], x[1].schema]).filter((x) => x[1] !== void 0);
|
|
47
56
|
if (cts.length === 0) return void 0;
|
|
48
57
|
const pretenders = [
|
|
49
58
|
"application/json",
|
|
@@ -58,12 +67,15 @@ const getReqSchema = (ctx, config) => {
|
|
|
58
67
|
cts.map((x) => x[0]);
|
|
59
68
|
return void 0;
|
|
60
69
|
};
|
|
61
|
-
const
|
|
70
|
+
const getRepContent = (ctx, config) => {
|
|
62
71
|
const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x]));
|
|
63
72
|
const firstSuccess = unref(ctx, get(config, ["responses", successCodes[0]], {}));
|
|
64
|
-
if (!firstSuccess || !("content" in firstSuccess)) return
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
if (!firstSuccess || !("content" in firstSuccess)) return [];
|
|
74
|
+
return getContent(ctx, firstSuccess.content);
|
|
75
|
+
};
|
|
76
|
+
const getRepSchema = (ctx, config) => {
|
|
77
|
+
const cts = getRepContent(ctx, config).filter((x) => x[1].schema !== void 0);
|
|
78
|
+
if (!cts.length) return void 0;
|
|
67
79
|
const ctJson = cts.find((x) => x[0].startsWith("application/json"));
|
|
68
80
|
if (ctJson) return ctJson[1].schema;
|
|
69
81
|
const ctText = cts.find((x) => x[0].startsWith("text/"));
|
|
@@ -133,10 +145,22 @@ const Keywords = /* @__PURE__ */ new Set([
|
|
|
133
145
|
"Exclude",
|
|
134
146
|
"Extract",
|
|
135
147
|
// ts keywords
|
|
148
|
+
"Array",
|
|
136
149
|
"Date",
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
|
|
150
|
+
"File",
|
|
151
|
+
"FormData",
|
|
152
|
+
"Headers",
|
|
153
|
+
"JSON",
|
|
154
|
+
"Object",
|
|
155
|
+
"Promise",
|
|
156
|
+
"RequestInit",
|
|
157
|
+
"Response",
|
|
158
|
+
"URL",
|
|
159
|
+
"URLSearchParams",
|
|
160
|
+
"fetch",
|
|
161
|
+
"globalThis",
|
|
162
|
+
"object"
|
|
163
|
+
// runtime names
|
|
140
164
|
]);
|
|
141
165
|
const normalizeIdentifier = (val, asVar = false) => {
|
|
142
166
|
let name = val.replace("#/components/schemas/", "").replaceAll("'", "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
@@ -145,7 +169,7 @@ const normalizeIdentifier = (val, asVar = false) => {
|
|
|
145
169
|
return name;
|
|
146
170
|
};
|
|
147
171
|
const makeInlineEnum = (s) => {
|
|
148
|
-
if (!s.enum) return void 0;
|
|
172
|
+
if (isBoolean(s) || !s.enum) return void 0;
|
|
149
173
|
const values = filterEmpty(s.enum);
|
|
150
174
|
if (!values.length) return void 0;
|
|
151
175
|
if (!s.type) {
|
|
@@ -178,8 +202,16 @@ const makeInlineEnum = (s) => {
|
|
|
178
202
|
console.warn(`enum with unknown type "${s.type}" in`, s);
|
|
179
203
|
return void 0;
|
|
180
204
|
};
|
|
205
|
+
const makeConst = (s) => {
|
|
206
|
+
if (isBoolean(s) || !("const" in s)) return void 0;
|
|
207
|
+
const value = s.const;
|
|
208
|
+
if (!["string", "number", "boolean"].includes(typeof value)) return void 0;
|
|
209
|
+
const t = makeInlineEnum({ ...s, enum: [value] });
|
|
210
|
+
return t && ts.isUnionTypeNode(t) ? t.types[0] : t;
|
|
211
|
+
};
|
|
181
212
|
const makeObject = (ctx, s) => {
|
|
182
|
-
if (s.type !== "object")
|
|
213
|
+
if (isBoolean(s) || s.type !== "object")
|
|
214
|
+
throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
|
|
183
215
|
if (s.additionalProperties && !isBoolean(s.additionalProperties)) {
|
|
184
216
|
return f$2.createTypeReferenceNode("Record", [
|
|
185
217
|
f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
|
|
@@ -198,19 +230,24 @@ const makeType = (ctx, s) => {
|
|
|
198
230
|
const mk = makeType.bind(null, ctx);
|
|
199
231
|
if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
|
|
200
232
|
if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
|
|
233
|
+
if (isBoolean(s)) {
|
|
234
|
+
return f$2.createKeywordTypeNode(s ? ts.SyntaxKind.UnknownKeyword : ts.SyntaxKind.NeverKeyword);
|
|
235
|
+
}
|
|
201
236
|
if ("$ref" in s && s.$ref) {
|
|
202
237
|
const parts = s.$ref.replace("#/", "").split("/");
|
|
203
238
|
if (parts.length === 3 && parts[0] === "components" && parts[1] === "schemas") {
|
|
204
239
|
return f$2.createTypeReferenceNode(normalizeIdentifier(parts[2], true));
|
|
205
240
|
}
|
|
206
241
|
const t = unref(ctx, s);
|
|
207
|
-
if (
|
|
242
|
+
if (t === void 0) throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
|
|
208
243
|
return makeType(ctx, t);
|
|
209
244
|
}
|
|
210
245
|
if ("oneOf" in s && s.oneOf) return f$2.createUnionTypeNode(s.oneOf.map(mk));
|
|
211
246
|
if ("anyOf" in s && s.anyOf) return f$2.createUnionTypeNode(s.anyOf.map(mk));
|
|
212
247
|
if ("allOf" in s && s.allOf) return f$2.createIntersectionTypeNode(s.allOf.map(mk));
|
|
213
248
|
if ("type" in s && s.type === "integer") s.type = "number";
|
|
249
|
+
const constant = makeConst(s);
|
|
250
|
+
if (constant) return constant;
|
|
214
251
|
if ("enum" in s && s.enum && !Array.isArray(s.type)) {
|
|
215
252
|
const isArray2 = s.type === "array";
|
|
216
253
|
const t = makeInlineEnum(isArray2 ? { ...s, type: s.items?.type } : s);
|
|
@@ -234,7 +271,6 @@ const makeType = (ctx, s) => {
|
|
|
234
271
|
if (s.type === "object") t = makeObject(ctx, s);
|
|
235
272
|
else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
|
|
236
273
|
else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
237
|
-
else if (isConstantString(s)) t = f$2.createLiteralTypeNode(f$2.createStringLiteral(s.const));
|
|
238
274
|
else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
|
|
239
275
|
else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
|
|
240
276
|
else if (isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
|
|
@@ -257,16 +293,14 @@ const makeType = (ctx, s) => {
|
|
|
257
293
|
return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
258
294
|
};
|
|
259
295
|
const isStringEnum = (s) => {
|
|
296
|
+
if (isBoolean(s)) return false;
|
|
260
297
|
if ("enum" in s && s.enum) {
|
|
261
298
|
return s.enum.every((x) => typeof x === "string");
|
|
262
299
|
}
|
|
263
300
|
return false;
|
|
264
301
|
};
|
|
265
302
|
const isPrefixItems = (s) => {
|
|
266
|
-
return s.prefixItems !== void 0;
|
|
267
|
-
};
|
|
268
|
-
const isConstantString = (s) => {
|
|
269
|
-
return s.type === "string" && s.const !== void 0;
|
|
303
|
+
return !isBoolean(s) && s.prefixItems !== void 0;
|
|
270
304
|
};
|
|
271
305
|
const makeTypeAlias = (ctx, name, s) => {
|
|
272
306
|
if (isStringEnum(s) && !ctx.inlineEnums) {
|
|
@@ -292,7 +326,17 @@ const makeTypeAlias = (ctx, name, s) => {
|
|
|
292
326
|
};
|
|
293
327
|
|
|
294
328
|
const f$1 = ts.factory;
|
|
295
|
-
const HttpMethods = [
|
|
329
|
+
const HttpMethods = [
|
|
330
|
+
"get",
|
|
331
|
+
"post",
|
|
332
|
+
"put",
|
|
333
|
+
"patch",
|
|
334
|
+
"delete",
|
|
335
|
+
"head",
|
|
336
|
+
"options",
|
|
337
|
+
"trace",
|
|
338
|
+
"query"
|
|
339
|
+
];
|
|
296
340
|
const normalizeOpName = (val) => {
|
|
297
341
|
const articles = /* @__PURE__ */ new Set(["a", "an", "the"]);
|
|
298
342
|
const tmp = val.replace(/'/, "").replace(/[^a-zA-Z0-9]/g, "_").split("_").filter((x) => x !== "" && !articles.has(x)).map((x) => upperFirst(x));
|
|
@@ -331,21 +375,39 @@ const prepareUrl = (url, rename) => {
|
|
|
331
375
|
const [name, ...rest] = x.split("}");
|
|
332
376
|
const right = rest.join("}");
|
|
333
377
|
return f$1.createTemplateSpan(
|
|
334
|
-
f$1.createIdentifier(
|
|
335
|
-
|
|
378
|
+
f$1.createCallExpression(f$1.createIdentifier("this.EncodePathParam"), void 0, [
|
|
379
|
+
f$1.createIdentifier(name)
|
|
380
|
+
// no normalization required
|
|
381
|
+
]),
|
|
336
382
|
i === parts.length - 2 ? f$1.createTemplateTail(right) : f$1.createTemplateMiddle(right)
|
|
337
383
|
);
|
|
338
384
|
})
|
|
339
385
|
);
|
|
340
386
|
};
|
|
341
|
-
const
|
|
387
|
+
const prepareSearch = (params) => {
|
|
388
|
+
const separators = { form: ",", spaceDelimited: " ", pipeDelimited: "|" };
|
|
389
|
+
const joined = params.flatMap((x) => {
|
|
390
|
+
const style = x.style ?? "form";
|
|
391
|
+
if ((x.explode ?? style === "form") || !(style in separators)) return [];
|
|
392
|
+
const key = /^[a-zA-Z_$][\w$]*$/.test(x.name) ? f$1.createPropertyAccessExpression(f$1.createIdentifier("search"), x.name) : f$1.createElementAccessExpression(f$1.createIdentifier("search"), f$1.createStringLiteral(x.name));
|
|
393
|
+
const value = f$1.createCallExpression(f$1.createIdentifier("this.JoinQueryParam"), void 0, [
|
|
394
|
+
key,
|
|
395
|
+
f$1.createStringLiteral(separators[style])
|
|
396
|
+
]);
|
|
397
|
+
return [f$1.createPropertyAssignment(f$1.createStringLiteral(x.name), value)];
|
|
398
|
+
});
|
|
399
|
+
if (!joined.length) return f$1.createShorthandPropertyAssignment("search");
|
|
400
|
+
const search = [f$1.createSpreadAssignment(f$1.createIdentifier("search")), ...joined];
|
|
401
|
+
return f$1.createPropertyAssignment("search", f$1.createObjectLiteralExpression(search));
|
|
402
|
+
};
|
|
403
|
+
const prepareOp = (ctx, cfg, opName, httpMethod) => {
|
|
342
404
|
cfg.parameters = cfg.parameters ?? [];
|
|
343
405
|
const reqSchema = getReqSchema(ctx, cfg);
|
|
344
406
|
const repSchema = getRepSchema(ctx, cfg);
|
|
345
407
|
const allParams = filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
|
|
346
408
|
const params = uniqBy(allParams.filter((x) => x.in === "path"), "name");
|
|
347
409
|
if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
|
|
348
|
-
const search = allParams.filter((x) => x.in === "query");
|
|
410
|
+
const search = uniqBy(allParams.filter((x) => x.in === "query"), "name");
|
|
349
411
|
allParams.filter((x) => x.in === "header");
|
|
350
412
|
for (const [name, v] of Object.entries({ search })) {
|
|
351
413
|
if (!v.length) continue;
|
|
@@ -372,7 +434,7 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
372
434
|
}
|
|
373
435
|
const cbArgs = filterNullable([
|
|
374
436
|
ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
|
|
375
|
-
search.length ?
|
|
437
|
+
search.length ? prepareSearch(search) : void 0,
|
|
376
438
|
reqSchema && f$1.createShorthandPropertyAssignment("body"),
|
|
377
439
|
reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
|
|
378
440
|
"headers",
|
|
@@ -398,7 +460,7 @@ const prepareOp = (ctx, cfg, opName) => {
|
|
|
398
460
|
f$1.createIdentifier("this.Fetch"),
|
|
399
461
|
[makeType(ctx, repSchema)],
|
|
400
462
|
[
|
|
401
|
-
f$1.createStringLiteral(
|
|
463
|
+
f$1.createStringLiteral(httpMethod),
|
|
402
464
|
// method
|
|
403
465
|
prepareUrl(cfg.path, urlReplacements),
|
|
404
466
|
// path
|
|
@@ -420,43 +482,49 @@ const prepareNs = (ctx, name, handlers) => {
|
|
|
420
482
|
f$1.createObjectLiteralExpression(handlers)
|
|
421
483
|
);
|
|
422
484
|
};
|
|
485
|
+
const addOp = (ctx, routes, pathConfig, cfg, httpMethod) => {
|
|
486
|
+
ctx.logTag = `${httpMethod.padEnd(6, " ")} ${cfg.path}`;
|
|
487
|
+
if (pathConfig.parameters) {
|
|
488
|
+
cfg.parameters = [...cfg.parameters ?? [], ...pathConfig.parameters];
|
|
489
|
+
}
|
|
490
|
+
const opTags = cfg.tags ?? [];
|
|
491
|
+
if (ctx.includeTags?.length && !opTags.some((t) => ctx.includeTags.includes(t))) return;
|
|
492
|
+
if (ctx.excludeTags?.length && opTags.some((t) => ctx.excludeTags.includes(t))) return;
|
|
493
|
+
const [ns, op] = getOpName(ctx, cfg);
|
|
494
|
+
if (!routes[ns]) routes[ns] = [];
|
|
495
|
+
const joined = [ns, op].join(".");
|
|
496
|
+
if (ctx.usedNames.has(joined)) {
|
|
497
|
+
return;
|
|
498
|
+
} else {
|
|
499
|
+
ctx.usedNames.add(joined);
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
routes[ns].push(prepareOp(ctx, cfg, op, httpMethod));
|
|
503
|
+
} catch (e) {
|
|
504
|
+
console.error(`${ctx.logTag} - ${e}`, cfg);
|
|
505
|
+
throw e;
|
|
506
|
+
}
|
|
507
|
+
};
|
|
423
508
|
const prepareRoutes = async (ctx) => {
|
|
424
509
|
const routes = {};
|
|
425
|
-
for (const [path,
|
|
510
|
+
for (const [path, value] of Object.entries(ctx.doc.paths ?? {})) {
|
|
426
511
|
ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
|
|
427
|
-
if (!isObject(
|
|
512
|
+
if (!isObject(value)) continue;
|
|
428
513
|
if (ctx.filterPaths) {
|
|
429
514
|
const match = ctx.filterPaths instanceof RegExp ? ctx.filterPaths.test(path) : ctx.filterPaths(path);
|
|
430
515
|
if (!match) continue;
|
|
431
516
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
517
|
+
const pathConfig = unref(ctx, value);
|
|
518
|
+
if (!pathConfig) continue;
|
|
436
519
|
for (const method of HttpMethods) {
|
|
437
|
-
ctx.logTag = `${method.toUpperCase().padEnd(6, " ")} ${path}`;
|
|
438
520
|
const config = pathConfig[method];
|
|
439
521
|
if (!config) continue;
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
const [ns, op] = getOpName(ctx, { ...config, method, path });
|
|
447
|
-
if (!routes[ns]) routes[ns] = [];
|
|
448
|
-
const joined = [ns, op].join(".");
|
|
449
|
-
if (ctx.usedNames.has(joined)) {
|
|
450
|
-
continue;
|
|
451
|
-
} else {
|
|
452
|
-
ctx.usedNames.add(joined);
|
|
453
|
-
}
|
|
454
|
-
try {
|
|
455
|
-
routes[ns].push(prepareOp(ctx, { ...config, method, path }, op));
|
|
456
|
-
} catch (e) {
|
|
457
|
-
console.error(`${ctx.logTag} - ${e}`, config);
|
|
458
|
-
throw e;
|
|
459
|
-
}
|
|
522
|
+
addOp(ctx, routes, pathConfig, { ...config, method, path }, method.toUpperCase());
|
|
523
|
+
}
|
|
524
|
+
for (const [method, value2] of Object.entries(pathConfig.additionalOperations ?? {})) {
|
|
525
|
+
const config = unref(ctx, value2);
|
|
526
|
+
if (!config) continue;
|
|
527
|
+
addOp(ctx, routes, pathConfig, { ...config, method, path }, method);
|
|
460
528
|
}
|
|
461
529
|
}
|
|
462
530
|
return routes;
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "apigen-ts",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vladkens <v.pronsky@gmail.com>",
|
|
7
|
-
"repository":
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "vladkens/apigen-ts"
|
|
10
|
+
},
|
|
8
11
|
"description": "Simple typed OpenAPI client generator",
|
|
9
12
|
"keywords": [
|
|
10
13
|
"openapi",
|
|
@@ -14,23 +17,26 @@
|
|
|
14
17
|
"generator",
|
|
15
18
|
"codegen"
|
|
16
19
|
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=22"
|
|
22
|
+
},
|
|
17
23
|
"dependencies": {
|
|
18
|
-
"@redocly/openapi-core": "2.
|
|
24
|
+
"@redocly/openapi-core": "2.76.0",
|
|
19
25
|
"@types/lodash-es": "4.17.12",
|
|
20
26
|
"@types/swagger2openapi": "7.0.4",
|
|
21
27
|
"array-utils-ts": "1.1.0",
|
|
22
|
-
"cleye": "2.
|
|
28
|
+
"cleye": "2.7.0",
|
|
23
29
|
"lodash-es": "4.18.1",
|
|
24
30
|
"swagger2openapi": "7.0.8"
|
|
25
31
|
},
|
|
26
32
|
"devDependencies": {
|
|
27
|
-
"@types/node": "
|
|
33
|
+
"@types/node": "26.6.6",
|
|
28
34
|
"fetch-mock": "12.6.0",
|
|
29
|
-
"npm-check-updates": "
|
|
30
|
-
"pkgroll": "2.
|
|
31
|
-
"prettier": "3.
|
|
35
|
+
"npm-check-updates": "23.1.1",
|
|
36
|
+
"pkgroll": "2.29.0",
|
|
37
|
+
"prettier": "3.9.10",
|
|
32
38
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
33
|
-
"tsx": "4.
|
|
39
|
+
"tsx": "4.23.15",
|
|
34
40
|
"typescript": "6.0.3"
|
|
35
41
|
},
|
|
36
42
|
"peerDependencies": {
|
package/readme.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
5
|
[<img src="https://badges.ws/npm/v/apigen-ts" alt="version" />](https://npmjs.org/package/apigen-ts)
|
|
6
|
-
[<img src="https://
|
|
6
|
+
[<img src="https://badges.ws/packagephobia/publish/apigen-ts" alt="size" />](https://packagephobia.com/result?p=apigen-ts)
|
|
7
7
|
[<img src="https://badges.ws/npm/dm/apigen-ts" alt="downloads" />](https://npmjs.org/package/apigen-ts)
|
|
8
8
|
[<img src="https://badges.ws/github/license/vladkens/apigen-ts" alt="license" />](https://github.com/vladkens/apigen-ts/blob/main/LICENSE)
|
|
9
9
|
[<img src="https://badges.ws/badge/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee&label" alt="donate" />](https://buymeacoffee.com/vladkens)
|
|
@@ -18,7 +18,7 @@ Turn your OpenAPI spec into a typed TypeScript client with one command.
|
|
|
18
18
|
|
|
19
19
|
- **One file.** Outputs a single `api-client.ts` — no scattered modules, no runtime deps in generated code.
|
|
20
20
|
- **Fully typed.** Every method returns the exact response type from your schema. No casting, no `any`.
|
|
21
|
-
- **Pure Node.js.** No Java, no Docker. Works with `
|
|
21
|
+
- **Pure Node.js.** No Java, no Docker. Works with `pnpm` in any project.
|
|
22
22
|
- **Fetch-based.** Uses native `fetch`. Override it with your own function for auth, retries, or logging.
|
|
23
23
|
- **All OpenAPI versions.** Supports v2 (Swagger), v3, and v3.1 — auto-upgrades v2 on the fly.
|
|
24
24
|
- **Extras built in.** Automatic date parsing, string literal unions instead of enums, Prettier formatting.
|
|
@@ -29,7 +29,7 @@ Unlike `openapi-typescript`, it generates a ready-to-call client — not just ty
|
|
|
29
29
|
## Install
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
|
-
|
|
32
|
+
pnpm add -D apigen-ts
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## Usage
|
|
@@ -38,16 +38,16 @@ npm i -D apigen-ts
|
|
|
38
38
|
|
|
39
39
|
```sh
|
|
40
40
|
# From a local file
|
|
41
|
-
|
|
41
|
+
pnpm apigen-ts ./openapi.json ./api-client.ts
|
|
42
42
|
|
|
43
43
|
# From a URL
|
|
44
|
-
|
|
44
|
+
pnpm apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts
|
|
45
45
|
|
|
46
46
|
# From a protected URL
|
|
47
|
-
|
|
47
|
+
pnpm apigen-ts https://secret-api.example.com ./api-client.ts -H "x-api-key: secret-key"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Run `
|
|
50
|
+
Run `pnpm apigen-ts --help` for all options. See [generated examples](./examples/).
|
|
51
51
|
|
|
52
52
|
### 2. Import
|
|
53
53
|
|
|
@@ -87,7 +87,7 @@ await api.protectedRoute.get() // authenticated
|
|
|
87
87
|
### Automatic date parsing
|
|
88
88
|
|
|
89
89
|
```sh
|
|
90
|
-
|
|
90
|
+
pnpm apigen-ts ./openapi.json ./api-client.ts --parse-dates
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
```ts
|
|
@@ -100,7 +100,7 @@ const createdAt: Date = pet.createdAt // parsed from format=date-time string
|
|
|
100
100
|
Pass `--inline-enums` to generate string literal unions — useful for Node.js [type stripping](https://nodejs.org/api/typescript.html#type-stripping):
|
|
101
101
|
|
|
102
102
|
```sh
|
|
103
|
-
|
|
103
|
+
pnpm apigen-ts ./openapi.json ./api-client.ts --inline-enums
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
```ts
|
|
@@ -119,17 +119,17 @@ enum MyEnum {
|
|
|
119
119
|
Include only the endpoints you need — useful with large schemas (e.g. Cloudflare's 8 MB monolith):
|
|
120
120
|
|
|
121
121
|
```sh
|
|
122
|
-
|
|
122
|
+
pnpm apigen-ts ./openapi.json ./api-client.ts --filter-paths '^/accounts'
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
### Filter by tag
|
|
126
126
|
|
|
127
127
|
```sh
|
|
128
128
|
# include only endpoints tagged "pets" or "store"
|
|
129
|
-
|
|
129
|
+
pnpm apigen-ts ./openapi.json ./api-client.ts --include-tags pets,store
|
|
130
130
|
|
|
131
131
|
# exclude endpoints tagged "internal"
|
|
132
|
-
|
|
132
|
+
pnpm apigen-ts ./openapi.json ./api-client.ts --exclude-tags internal
|
|
133
133
|
```
|
|
134
134
|
|
|
135
135
|
When both flags are set, `--exclude-tags` wins.
|
|
@@ -139,7 +139,7 @@ When both flags are set, `--exclude-tags` wins.
|
|
|
139
139
|
Pass `--fetch-options` to add an optional last argument to every generated method, accepting any [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit) field:
|
|
140
140
|
|
|
141
141
|
```sh
|
|
142
|
-
|
|
142
|
+
pnpm apigen-ts ./openapi.json ./api-client.ts --fetch-options
|
|
143
143
|
```
|
|
144
144
|
|
|
145
145
|
```ts
|