apigen-ts 1.2.1 → 1.3.1

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 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 | undefined = undefined
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as apigen, g as getCliConfig } from './main-BZrWxHPM.js';
2
+ import { a as apigen, g as getCliConfig } from './main-BiX2OIVV.js';
3
3
  import 'fs/promises';
4
4
  import 'path';
5
5
  import 'url';
@@ -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 redocly, { BaseResolver } from '@redocly/openapi-core';
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.2.1";
13
+ var version = "1.3.1";
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
- headers: parseHeaders(argv.flags.header)
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, "content"]));
112
- const cts = Object.entries(get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
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) => f$2.createNumericLiteral(x));
209
- return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
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,19 +297,24 @@ 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
- return f$2.createTypeLiteralNode(
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);
@@ -299,6 +345,9 @@ const isStringEnum = (s) => {
299
345
  const isPrefixItems = (s) => {
300
346
  return s.prefixItems !== void 0;
301
347
  };
348
+ const isConstantString = (s) => {
349
+ return s.type === "string" && s.const !== void 0;
350
+ };
302
351
  const makeTypeAlias = (ctx, name, s) => {
303
352
  if (isStringEnum(s) && !ctx.inlineEnums) {
304
353
  const tokens1 = uniq(s.enum);
@@ -390,7 +439,19 @@ const prepareOp = (ctx, cfg, opName) => {
390
439
  urlReplacements[x.name] = name;
391
440
  return f$1.createParameterDeclaration(void 0, void 0, name, void 0, type);
392
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
+ }
393
453
  const cbArgs = filterNullable([
454
+ ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
394
455
  search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
395
456
  reqSchema && f$1.createShorthandPropertyAssignment("body"),
396
457
  reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
@@ -444,6 +505,10 @@ const prepareRoutes = async (ctx) => {
444
505
  for (const [path, pathConfig] of Object.entries(ctx.doc.paths ?? {})) {
445
506
  ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
446
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
+ }
447
512
  if ("$ref" in pathConfig) {
448
513
  console.warn(`${ctx.logTag} $ref should be resolved before (skipping)`);
449
514
  continue;
@@ -455,6 +520,9 @@ const prepareRoutes = async (ctx) => {
455
520
  if (pathConfig.parameters) {
456
521
  config.parameters = [...config.parameters ?? [], ...pathConfig.parameters];
457
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;
458
526
  const [ns, op] = getOpName(ctx, { ...config, method, path });
459
527
  if (!routes[ns]) routes[ns] = [];
460
528
  const joined = [ns, op].join(".");
@@ -501,9 +569,9 @@ const loadSchema = async ({
501
569
  headers = {}
502
570
  }) => {
503
571
  if (url.startsWith("file://")) url = url.substring(7);
504
- const { bundle } = await redocly.bundle({
572
+ const { bundle: bundle$1 } = await bundle({
505
573
  ref: url,
506
- config: await redocly.createConfig({}),
574
+ config: await createConfig({}),
507
575
  removeUnusedComponents: false,
508
576
  externalRefResolver: new BaseResolver({
509
577
  http: {
@@ -513,11 +581,12 @@ const loadSchema = async ({
513
581
  }
514
582
  })
515
583
  });
516
- if (bundle.parsed.swagger && upgrade) {
517
- const { openapi } = await convertObj(bundle.parsed, { patch: true });
584
+ const parsed = bundle$1.parsed;
585
+ if ("swagger" in parsed && upgrade) {
586
+ const { openapi } = await convertObj(parsed, { patch: true });
518
587
  return openapi;
519
588
  }
520
- return bundle.parsed;
589
+ return parsed;
521
590
  };
522
591
 
523
592
  const f = ts.factory;
@@ -549,7 +618,10 @@ const formatCode = async (code) => {
549
618
  };
550
619
 
551
620
  const apigen = async (config) => {
552
- const doc = await loadSchema({ url: config.source, headers: config.headers });
621
+ const doc = await loadSchema({
622
+ url: config.source,
623
+ ...config.headers ? { headers: config.headers } : {}
624
+ });
553
625
  const ctx = initCtx({ ...config, doc });
554
626
  const { modules, types } = await generateAst(ctx);
555
627
  const filepath = join(dirname(fileURLToPath(import.meta.url)), "_template.ts");