apigen-ts 0.1.1 → 0.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/_template.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  // Note: Use uppercase for names in ApiClient to avoid conflict with the generated code
2
2
 
3
- interface ApigenConfig {
3
+ export interface ApigenConfig {
4
4
  baseUrl: string
5
5
  headers: Record<string, string>
6
6
  }
7
7
 
8
- interface ApigenRequest extends Omit<RequestInit, "body"> {
8
+ export interface ApigenRequest extends Omit<RequestInit, "body"> {
9
9
  search?: Record<string, unknown>
10
10
  body?: unknown
11
11
  }
@@ -40,7 +40,7 @@ export class ApiClient {
40
40
  }
41
41
  }
42
42
 
43
- async Fetch<T>(method: string, path: string, opts: ApigenRequest = {}): Promise<T> {
43
+ PrepareFetchUrl(path: string): URL {
44
44
  let base = this.Config.baseUrl
45
45
  if ("location" in globalThis && (base === "" || base.startsWith("/"))) {
46
46
  // make ts happy in pure nodejs environment, should never pass here
@@ -48,7 +48,12 @@ export class ApiClient {
48
48
  base = `${location.origin}${base.endsWith("/") ? base : `/${base}`}`
49
49
  }
50
50
 
51
- const url = new URL(path, base)
51
+ return new URL(path, base)
52
+ }
53
+
54
+ async Fetch<T>(method: string, path: string, opts: ApigenRequest = {}): Promise<T> {
55
+ const url = this.PrepareFetchUrl(path)
56
+
52
57
  for (const [k, v] of Object.entries(opts?.search ?? {})) {
53
58
  url.searchParams.append(k, Array.isArray(v) ? v.join(",") : (v as string))
54
59
  }
package/dist/cli.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var main$1 = require('./main-c2426ec2.cjs');
3
+ var main$1 = require('./main-Dkcn3PTN.cjs');
4
4
  require('fs/promises');
5
5
  require('path');
6
6
  require('url');
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-bbe33f1b.js';
2
+ import { a as apigen, g as getCliConfig } from './main-byN_-fkF.js';
3
3
  import 'fs/promises';
4
4
  import 'path';
5
5
  import 'url';
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as apigen, g as getCliConfig } from './main-bbe33f1b.mjs';
1
+ import { a as apigen, g as getCliConfig } from './main-byN_-fkF.mjs';
2
2
  import 'fs/promises';
3
3
  import 'path';
4
4
  import 'url';
@@ -12,6 +12,9 @@ var ts = require('typescript');
12
12
  var path = require('node:path');
13
13
 
14
14
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
+ var name = "apigen-ts";
16
+ var version = "0.2.0";
17
+
15
18
  const initCtx = (config) => {
16
19
  return {
17
20
  source: "",
@@ -26,8 +29,8 @@ const initCtx = (config) => {
26
29
  };
27
30
  const getCliConfig = () => {
28
31
  const argv = cleye.cli({
29
- name: "apigen",
30
- version: "0.1.1",
32
+ name,
33
+ version,
31
34
  parameters: ["<source>", "[output]"],
32
35
  flags: {
33
36
  name: {
@@ -52,8 +55,7 @@ const getCliConfig = () => {
52
55
  };
53
56
 
54
57
  const unref = (ctx, s) => {
55
- if (!s)
56
- return void 0;
58
+ if (!s) return void 0;
57
59
  if ("$ref" in s && s.$ref) {
58
60
  const parts = s.$ref.replace("#/", "").split("/");
59
61
  const obj = parts.reduce(
@@ -61,8 +63,7 @@ const unref = (ctx, s) => {
61
63
  (acc, x) => lodashEs.get(acc, x, lodashEs.get(acc, decodeURIComponent(x).replaceAll("~1", "/"))),
62
64
  ctx.doc
63
65
  );
64
- if (obj)
65
- return obj;
66
+ if (obj) return obj;
66
67
  console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
67
68
  return void 0;
68
69
  }
@@ -70,11 +71,9 @@ const unref = (ctx, s) => {
70
71
  };
71
72
  const getReqSchema = (ctx, config) => {
72
73
  const req = unref(ctx, config.requestBody);
73
- if (!req)
74
- return void 0;
74
+ if (!req) return void 0;
75
75
  const cts = Object.entries(req.content ?? {}).map((x) => [x[0].split(";")[0], x[1].schema]).filter((x) => x[1]);
76
- if (cts.length === 0)
77
- return void 0;
76
+ if (cts.length === 0) return void 0;
78
77
  const pretenders = [
79
78
  "application/json",
80
79
  "text/",
@@ -83,8 +82,7 @@ const getReqSchema = (ctx, config) => {
83
82
  ];
84
83
  for (const p of pretenders) {
85
84
  const ct = cts.find((x) => x[0].startsWith(p));
86
- if (ct)
87
- return ct;
85
+ if (ct) return ct;
88
86
  }
89
87
  cts.map((x) => x[0]);
90
88
  return void 0;
@@ -92,14 +90,11 @@ const getReqSchema = (ctx, config) => {
92
90
  const getRepSchema = (ctx, config) => {
93
91
  const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => lodashEs.get(config, ["responses", x, "content"]));
94
92
  const cts = Object.entries(lodashEs.get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
95
- if (cts.length === 0)
96
- return void 0;
93
+ if (cts.length === 0) return void 0;
97
94
  const ctJson = cts.find((x) => x[0].startsWith("application/json"));
98
- if (ctJson)
99
- return ctJson[1].schema;
95
+ if (ctJson) return ctJson[1].schema;
100
96
  const ctText = cts.find((x) => x[0].startsWith("text/"));
101
- if (ctText)
102
- return { type: "string" };
97
+ if (ctText) return { type: "string" };
103
98
  cts.map((x) => x[0]).join(", ");
104
99
  return void 0;
105
100
  };
@@ -172,25 +167,18 @@ const Keywords = /* @__PURE__ */ new Set([
172
167
  ]);
173
168
  const normalizeIdentifier = (val, asVar = false) => {
174
169
  let name = val.replace("#/components/schemas/", "").replaceAll("'", "").replace(/[^a-zA-Z0-9]/g, "_");
175
- if (name.match(/^\d/))
176
- name = `$${name}`;
177
- if (asVar && Keywords.has(name))
178
- name = `$${name}`;
170
+ if (name.match(/^\d/)) name = `$${name}`;
171
+ if (asVar && Keywords.has(name)) name = `$${name}`;
179
172
  return name;
180
173
  };
181
174
  const makeInlineEnum = (s) => {
182
- if (!s.enum)
183
- return void 0;
175
+ if (!s.enum) return void 0;
184
176
  const values = arrayUtilsTs.filterEmpty(s.enum);
185
- if (!values.length)
186
- return void 0;
177
+ if (!values.length) return void 0;
187
178
  if (!s.type) {
188
- if (values.every((x) => typeof x === "string"))
189
- s.type = "string";
190
- if (values.every((x) => typeof x === "number"))
191
- s.type = "number";
192
- if (values.every((x) => typeof x === "boolean"))
193
- s.type = "boolean";
179
+ if (values.every((x) => typeof x === "string")) s.type = "string";
180
+ if (values.every((x) => typeof x === "number")) s.type = "number";
181
+ if (values.every((x) => typeof x === "boolean")) s.type = "boolean";
194
182
  }
195
183
  if (s.type === "string") {
196
184
  const tokens = lodashEs.uniq(values).map((x) => f$2.createStringLiteral(x.toString()));
@@ -202,10 +190,8 @@ const makeInlineEnum = (s) => {
202
190
  }
203
191
  if (s.type === "boolean") {
204
192
  const tokens = [];
205
- if (values.includes(true))
206
- tokens.push(f$2.createToken(ts.SyntaxKind.TrueKeyword));
207
- if (values.includes(false))
208
- tokens.push(f$2.createToken(ts.SyntaxKind.FalseKeyword));
193
+ if (values.includes(true)) tokens.push(f$2.createToken(ts.SyntaxKind.TrueKeyword));
194
+ if (values.includes(false)) tokens.push(f$2.createToken(ts.SyntaxKind.FalseKeyword));
209
195
  return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
210
196
  }
211
197
  console.warn(`enum with unknown type "${s.type}" in`, s);
@@ -213,33 +199,25 @@ const makeInlineEnum = (s) => {
213
199
  };
214
200
  const makeType = (ctx, s) => {
215
201
  const mk = makeType.bind(null, ctx);
216
- if (s === void 0)
217
- return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
218
- if (s === null)
219
- return f$2.createLiteralTypeNode(f$2.createNull());
202
+ if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
203
+ if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
220
204
  if ("$ref" in s && s.$ref) {
221
205
  const parts = s.$ref.replace("#/", "").split("/");
222
206
  if (parts.length === 3 && parts[0] === "components" && parts[1] === "schemas") {
223
207
  return f$2.createTypeReferenceNode(normalizeIdentifier(parts[2], true));
224
208
  }
225
209
  const t = unref(ctx, s);
226
- if (!t)
227
- throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
210
+ if (!t) throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
228
211
  return makeType(ctx, t);
229
212
  }
230
- if ("oneOf" in s && s.oneOf)
231
- return f$2.createUnionTypeNode(s.oneOf.map(mk));
232
- if ("anyOf" in s && s.anyOf)
233
- return f$2.createUnionTypeNode(s.anyOf.map(mk));
234
- if ("allOf" in s && s.allOf)
235
- return f$2.createIntersectionTypeNode(s.allOf.map(mk));
236
- if ("type" in s && s.type === "integer")
237
- s.type = "number";
213
+ if ("oneOf" in s && s.oneOf) return f$2.createUnionTypeNode(s.oneOf.map(mk));
214
+ if ("anyOf" in s && s.anyOf) return f$2.createUnionTypeNode(s.anyOf.map(mk));
215
+ if ("allOf" in s && s.allOf) return f$2.createIntersectionTypeNode(s.allOf.map(mk));
216
+ if ("type" in s && s.type === "integer") s.type = "number";
238
217
  if ("enum" in s && s.enum && !Array.isArray(s.type)) {
239
218
  const isArray2 = s.type === "array";
240
219
  const t = makeInlineEnum(isArray2 ? { ...s, type: s.items?.type } : s);
241
- if (t)
242
- return isArray2 ? f$2.createArrayTypeNode(t) : t;
220
+ if (t) return isArray2 ? f$2.createArrayTypeNode(t) : t;
243
221
  }
244
222
  if ("properties" in s && s.properties) {
245
223
  return f$2.createTypeLiteralNode(
@@ -254,37 +232,26 @@ const makeType = (ctx, s) => {
254
232
  if (Array.isArray(s.type)) {
255
233
  const types = [];
256
234
  for (const type of s.type) {
257
- if (type === "null")
258
- types.push({ type: "null" });
259
- else
260
- types.push({ ...s, type });
235
+ if (type === "null") types.push({ type: "null" });
236
+ else types.push({ ...s, type });
261
237
  }
262
238
  return mk({ oneOf: types });
263
239
  }
264
240
  let t;
265
- if (s.type === "object")
266
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
267
- else if (s.type === "boolean")
268
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
269
- else if (s.type === "number")
270
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
271
- else if (s.type === "string")
272
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
273
- else if (s.type === "array")
274
- t = f$2.createArrayTypeNode(mk(s.items));
275
- else if (s.type === "null")
276
- t = f$2.createLiteralTypeNode(f$2.createNull());
277
- else if (lodashEs.isArray(s.type))
278
- t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
241
+ if (s.type === "object") t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
242
+ else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
243
+ else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
244
+ else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
245
+ else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
246
+ else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
247
+ else if (lodashEs.isArray(s.type)) t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
279
248
  else {
280
249
  console.warn(`makeType: unknown type "${s.type}"`);
281
250
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
282
251
  }
283
252
  if (s.type === "string") {
284
- if (s.format === "binary")
285
- t = f$2.createTypeReferenceNode("File");
286
- if (s.format === "date-time" && ctx.parseDates)
287
- t = f$2.createTypeReferenceNode("Date");
253
+ if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
254
+ if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
288
255
  }
289
256
  return s.nullable ? f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]) : t;
290
257
  }
@@ -336,15 +303,13 @@ const getOpName = (ctx, op) => {
336
303
  }
337
304
  fn = normalizeOpName(fn);
338
305
  let nsr = ns.split("").map((x) => `[${x.toUpperCase()}${x.toLowerCase()}]`).join("");
339
- if (nsr.endsWith("[Ss]"))
340
- nsr += "?";
306
+ if (nsr.endsWith("[Ss]")) nsr += "?";
341
307
  fn = fn.replace(new RegExp(`^${nsr}([Cc]ontroller|[Ss]ervice)?([A-Z].*)$`), "$2");
342
308
  fn = lodashEs.lowerFirst(fn);
343
309
  const proposal = [ns, fn];
344
310
  if (ctx.resolveName) {
345
311
  const res = ctx.resolveName(ctx, op, proposal);
346
- if (Array.isArray(res) && res.length === 2)
347
- return res;
312
+ if (Array.isArray(res) && res.length === 2) return res;
348
313
  if (res !== void 0) {
349
314
  console.warn(`${ctx.logTag} resolveName should return [ns, fn] or undefined (skipping)`);
350
315
  }
@@ -352,11 +317,9 @@ const getOpName = (ctx, op) => {
352
317
  return proposal;
353
318
  };
354
319
  const prepareUrl = (url, rename) => {
355
- for (const [k, v] of Object.entries(rename))
356
- url = url.replaceAll(`{${k}}`, "${" + v + "}");
320
+ for (const [k, v] of Object.entries(rename)) url = url.replaceAll(`{${k}}`, "${" + v + "}");
357
321
  const parts = url.split("${");
358
- if (parts.length === 1)
359
- return f$1.createStringLiteral(url);
322
+ if (parts.length === 1) return f$1.createStringLiteral(url);
360
323
  return f$1.createTemplateExpression(
361
324
  f$1.createTemplateHead(parts[0]),
362
325
  parts.slice(1).map((x, i) => {
@@ -376,13 +339,11 @@ const prepareOp = (ctx, cfg, opName) => {
376
339
  const repSchema = getRepSchema(ctx, cfg);
377
340
  const allParams = arrayUtilsTs.filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
378
341
  const params = lodashEs.uniqBy(allParams.filter((x) => x.in === "path"), "name");
379
- if (reqSchema)
380
- params.push({ name: "body", schema: reqSchema[1] });
342
+ if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
381
343
  const search = allParams.filter((x) => x.in === "query");
382
344
  allParams.filter((x) => x.in === "header");
383
345
  for (const [name, v] of Object.entries({ search })) {
384
- if (!v.length)
385
- continue;
346
+ if (!v.length) continue;
386
347
  const properties = v.reduce((acc, x) => ({ ...acc, [x.name]: x.schema }), {});
387
348
  params.push({ name, schema: { type: "object", properties } });
388
349
  }
@@ -446,8 +407,7 @@ const prepareRoutes = async (ctx) => {
446
407
  const routes = {};
447
408
  for (const [path, pathConfig] of Object.entries(ctx.doc.paths ?? {})) {
448
409
  ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
449
- if (!lodashEs.isObject(pathConfig))
450
- continue;
410
+ if (!lodashEs.isObject(pathConfig)) continue;
451
411
  if ("$ref" in pathConfig) {
452
412
  console.warn(`${ctx.logTag} $ref should be resolved before (skipping)`);
453
413
  continue;
@@ -455,14 +415,12 @@ const prepareRoutes = async (ctx) => {
455
415
  for (const method of HttpMethods) {
456
416
  ctx.logTag = `${method.toUpperCase().padEnd(6, " ")} ${path}`;
457
417
  const config = pathConfig[method];
458
- if (!config)
459
- continue;
418
+ if (!config) continue;
460
419
  if (pathConfig.parameters) {
461
420
  config.parameters = [...config.parameters ?? [], ...pathConfig.parameters];
462
421
  }
463
422
  const [ns, op] = getOpName(ctx, { ...config, method, path });
464
- if (!routes[ns])
465
- routes[ns] = [];
423
+ if (!routes[ns]) routes[ns] = [];
466
424
  const joined = [ns, op].join(".");
467
425
  if (ctx.usedNames.has(joined)) {
468
426
  continue;
@@ -502,8 +460,7 @@ const generateAst = async (ctx) => {
502
460
  return { modules, types };
503
461
  };
504
462
  const loadSchema = async (url, upgrade = true) => {
505
- if (url.startsWith("file://"))
506
- url = url.substring(7);
463
+ if (url.startsWith("file://")) url = url.substring(7);
507
464
  const { bundle } = await redocly.bundle({
508
465
  ref: url,
509
466
  config: await redocly.createConfig({}),
@@ -548,7 +505,7 @@ const apigen = async (config) => {
548
505
  const doc = await loadSchema(config.source);
549
506
  const ctx = initCtx({ ...config, doc });
550
507
  const { modules, types } = await generateAst(ctx);
551
- const filepath = path$1.join(path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('main-c2426ec2.cjs', document.baseURI).href)))), "_template.ts");
508
+ const filepath = path$1.join(path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('main-Dkcn3PTN.cjs', document.baseURI).href)))), "_template.ts");
552
509
  const file = await fs.readFile(filepath, "utf-8");
553
510
  let code = [
554
511
  `// Auto-generated by https://github.com/vladkens/apigen-ts`,
@@ -9,6 +9,9 @@ import { convertObj } from 'swagger2openapi';
9
9
  import ts from 'typescript';
10
10
  import path from 'node:path';
11
11
 
12
+ var name = "apigen-ts";
13
+ var version = "0.2.0";
14
+
12
15
  const initCtx = (config) => {
13
16
  return {
14
17
  source: "",
@@ -23,8 +26,8 @@ const initCtx = (config) => {
23
26
  };
24
27
  const getCliConfig = () => {
25
28
  const argv = cli({
26
- name: "apigen",
27
- version: "0.1.1",
29
+ name,
30
+ version,
28
31
  parameters: ["<source>", "[output]"],
29
32
  flags: {
30
33
  name: {
@@ -49,8 +52,7 @@ const getCliConfig = () => {
49
52
  };
50
53
 
51
54
  const unref = (ctx, s) => {
52
- if (!s)
53
- return void 0;
55
+ if (!s) return void 0;
54
56
  if ("$ref" in s && s.$ref) {
55
57
  const parts = s.$ref.replace("#/", "").split("/");
56
58
  const obj = parts.reduce(
@@ -58,8 +60,7 @@ const unref = (ctx, s) => {
58
60
  (acc, x) => get(acc, x, get(acc, decodeURIComponent(x).replaceAll("~1", "/"))),
59
61
  ctx.doc
60
62
  );
61
- if (obj)
62
- return obj;
63
+ if (obj) return obj;
63
64
  console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
64
65
  return void 0;
65
66
  }
@@ -67,11 +68,9 @@ const unref = (ctx, s) => {
67
68
  };
68
69
  const getReqSchema = (ctx, config) => {
69
70
  const req = unref(ctx, config.requestBody);
70
- if (!req)
71
- return void 0;
71
+ if (!req) return void 0;
72
72
  const cts = Object.entries(req.content ?? {}).map((x) => [x[0].split(";")[0], x[1].schema]).filter((x) => x[1]);
73
- if (cts.length === 0)
74
- return void 0;
73
+ if (cts.length === 0) return void 0;
75
74
  const pretenders = [
76
75
  "application/json",
77
76
  "text/",
@@ -80,8 +79,7 @@ const getReqSchema = (ctx, config) => {
80
79
  ];
81
80
  for (const p of pretenders) {
82
81
  const ct = cts.find((x) => x[0].startsWith(p));
83
- if (ct)
84
- return ct;
82
+ if (ct) return ct;
85
83
  }
86
84
  cts.map((x) => x[0]);
87
85
  return void 0;
@@ -89,14 +87,11 @@ const getReqSchema = (ctx, config) => {
89
87
  const getRepSchema = (ctx, config) => {
90
88
  const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x, "content"]));
91
89
  const cts = Object.entries(get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
92
- if (cts.length === 0)
93
- return void 0;
90
+ if (cts.length === 0) return void 0;
94
91
  const ctJson = cts.find((x) => x[0].startsWith("application/json"));
95
- if (ctJson)
96
- return ctJson[1].schema;
92
+ if (ctJson) return ctJson[1].schema;
97
93
  const ctText = cts.find((x) => x[0].startsWith("text/"));
98
- if (ctText)
99
- return { type: "string" };
94
+ if (ctText) return { type: "string" };
100
95
  cts.map((x) => x[0]).join(", ");
101
96
  return void 0;
102
97
  };
@@ -169,25 +164,18 @@ const Keywords = /* @__PURE__ */ new Set([
169
164
  ]);
170
165
  const normalizeIdentifier = (val, asVar = false) => {
171
166
  let name = val.replace("#/components/schemas/", "").replaceAll("'", "").replace(/[^a-zA-Z0-9]/g, "_");
172
- if (name.match(/^\d/))
173
- name = `$${name}`;
174
- if (asVar && Keywords.has(name))
175
- name = `$${name}`;
167
+ if (name.match(/^\d/)) name = `$${name}`;
168
+ if (asVar && Keywords.has(name)) name = `$${name}`;
176
169
  return name;
177
170
  };
178
171
  const makeInlineEnum = (s) => {
179
- if (!s.enum)
180
- return void 0;
172
+ if (!s.enum) return void 0;
181
173
  const values = filterEmpty(s.enum);
182
- if (!values.length)
183
- return void 0;
174
+ if (!values.length) return void 0;
184
175
  if (!s.type) {
185
- if (values.every((x) => typeof x === "string"))
186
- s.type = "string";
187
- if (values.every((x) => typeof x === "number"))
188
- s.type = "number";
189
- if (values.every((x) => typeof x === "boolean"))
190
- s.type = "boolean";
176
+ if (values.every((x) => typeof x === "string")) s.type = "string";
177
+ if (values.every((x) => typeof x === "number")) s.type = "number";
178
+ if (values.every((x) => typeof x === "boolean")) s.type = "boolean";
191
179
  }
192
180
  if (s.type === "string") {
193
181
  const tokens = uniq(values).map((x) => f$2.createStringLiteral(x.toString()));
@@ -199,10 +187,8 @@ const makeInlineEnum = (s) => {
199
187
  }
200
188
  if (s.type === "boolean") {
201
189
  const tokens = [];
202
- if (values.includes(true))
203
- tokens.push(f$2.createToken(ts.SyntaxKind.TrueKeyword));
204
- if (values.includes(false))
205
- tokens.push(f$2.createToken(ts.SyntaxKind.FalseKeyword));
190
+ if (values.includes(true)) tokens.push(f$2.createToken(ts.SyntaxKind.TrueKeyword));
191
+ if (values.includes(false)) tokens.push(f$2.createToken(ts.SyntaxKind.FalseKeyword));
206
192
  return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
207
193
  }
208
194
  console.warn(`enum with unknown type "${s.type}" in`, s);
@@ -210,33 +196,25 @@ const makeInlineEnum = (s) => {
210
196
  };
211
197
  const makeType = (ctx, s) => {
212
198
  const mk = makeType.bind(null, ctx);
213
- if (s === void 0)
214
- return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
215
- if (s === null)
216
- return f$2.createLiteralTypeNode(f$2.createNull());
199
+ if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
200
+ if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
217
201
  if ("$ref" in s && s.$ref) {
218
202
  const parts = s.$ref.replace("#/", "").split("/");
219
203
  if (parts.length === 3 && parts[0] === "components" && parts[1] === "schemas") {
220
204
  return f$2.createTypeReferenceNode(normalizeIdentifier(parts[2], true));
221
205
  }
222
206
  const t = unref(ctx, s);
223
- if (!t)
224
- throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
207
+ if (!t) throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
225
208
  return makeType(ctx, t);
226
209
  }
227
- if ("oneOf" in s && s.oneOf)
228
- return f$2.createUnionTypeNode(s.oneOf.map(mk));
229
- if ("anyOf" in s && s.anyOf)
230
- return f$2.createUnionTypeNode(s.anyOf.map(mk));
231
- if ("allOf" in s && s.allOf)
232
- return f$2.createIntersectionTypeNode(s.allOf.map(mk));
233
- if ("type" in s && s.type === "integer")
234
- s.type = "number";
210
+ if ("oneOf" in s && s.oneOf) return f$2.createUnionTypeNode(s.oneOf.map(mk));
211
+ if ("anyOf" in s && s.anyOf) return f$2.createUnionTypeNode(s.anyOf.map(mk));
212
+ if ("allOf" in s && s.allOf) return f$2.createIntersectionTypeNode(s.allOf.map(mk));
213
+ if ("type" in s && s.type === "integer") s.type = "number";
235
214
  if ("enum" in s && s.enum && !Array.isArray(s.type)) {
236
215
  const isArray2 = s.type === "array";
237
216
  const t = makeInlineEnum(isArray2 ? { ...s, type: s.items?.type } : s);
238
- if (t)
239
- return isArray2 ? f$2.createArrayTypeNode(t) : t;
217
+ if (t) return isArray2 ? f$2.createArrayTypeNode(t) : t;
240
218
  }
241
219
  if ("properties" in s && s.properties) {
242
220
  return f$2.createTypeLiteralNode(
@@ -251,37 +229,26 @@ const makeType = (ctx, s) => {
251
229
  if (Array.isArray(s.type)) {
252
230
  const types = [];
253
231
  for (const type of s.type) {
254
- if (type === "null")
255
- types.push({ type: "null" });
256
- else
257
- types.push({ ...s, type });
232
+ if (type === "null") types.push({ type: "null" });
233
+ else types.push({ ...s, type });
258
234
  }
259
235
  return mk({ oneOf: types });
260
236
  }
261
237
  let t;
262
- if (s.type === "object")
263
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
264
- else if (s.type === "boolean")
265
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
266
- else if (s.type === "number")
267
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
268
- else if (s.type === "string")
269
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
270
- else if (s.type === "array")
271
- t = f$2.createArrayTypeNode(mk(s.items));
272
- else if (s.type === "null")
273
- t = f$2.createLiteralTypeNode(f$2.createNull());
274
- else if (isArray(s.type))
275
- t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
238
+ if (s.type === "object") t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
239
+ else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
240
+ else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
241
+ else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
242
+ else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
243
+ else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
244
+ else if (isArray(s.type)) t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
276
245
  else {
277
246
  console.warn(`makeType: unknown type "${s.type}"`);
278
247
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
279
248
  }
280
249
  if (s.type === "string") {
281
- if (s.format === "binary")
282
- t = f$2.createTypeReferenceNode("File");
283
- if (s.format === "date-time" && ctx.parseDates)
284
- t = f$2.createTypeReferenceNode("Date");
250
+ if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
251
+ if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
285
252
  }
286
253
  return s.nullable ? f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]) : t;
287
254
  }
@@ -333,15 +300,13 @@ const getOpName = (ctx, op) => {
333
300
  }
334
301
  fn = normalizeOpName(fn);
335
302
  let nsr = ns.split("").map((x) => `[${x.toUpperCase()}${x.toLowerCase()}]`).join("");
336
- if (nsr.endsWith("[Ss]"))
337
- nsr += "?";
303
+ if (nsr.endsWith("[Ss]")) nsr += "?";
338
304
  fn = fn.replace(new RegExp(`^${nsr}([Cc]ontroller|[Ss]ervice)?([A-Z].*)$`), "$2");
339
305
  fn = lowerFirst(fn);
340
306
  const proposal = [ns, fn];
341
307
  if (ctx.resolveName) {
342
308
  const res = ctx.resolveName(ctx, op, proposal);
343
- if (Array.isArray(res) && res.length === 2)
344
- return res;
309
+ if (Array.isArray(res) && res.length === 2) return res;
345
310
  if (res !== void 0) {
346
311
  console.warn(`${ctx.logTag} resolveName should return [ns, fn] or undefined (skipping)`);
347
312
  }
@@ -349,11 +314,9 @@ const getOpName = (ctx, op) => {
349
314
  return proposal;
350
315
  };
351
316
  const prepareUrl = (url, rename) => {
352
- for (const [k, v] of Object.entries(rename))
353
- url = url.replaceAll(`{${k}}`, "${" + v + "}");
317
+ for (const [k, v] of Object.entries(rename)) url = url.replaceAll(`{${k}}`, "${" + v + "}");
354
318
  const parts = url.split("${");
355
- if (parts.length === 1)
356
- return f$1.createStringLiteral(url);
319
+ if (parts.length === 1) return f$1.createStringLiteral(url);
357
320
  return f$1.createTemplateExpression(
358
321
  f$1.createTemplateHead(parts[0]),
359
322
  parts.slice(1).map((x, i) => {
@@ -373,13 +336,11 @@ const prepareOp = (ctx, cfg, opName) => {
373
336
  const repSchema = getRepSchema(ctx, cfg);
374
337
  const allParams = filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
375
338
  const params = uniqBy(allParams.filter((x) => x.in === "path"), "name");
376
- if (reqSchema)
377
- params.push({ name: "body", schema: reqSchema[1] });
339
+ if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
378
340
  const search = allParams.filter((x) => x.in === "query");
379
341
  allParams.filter((x) => x.in === "header");
380
342
  for (const [name, v] of Object.entries({ search })) {
381
- if (!v.length)
382
- continue;
343
+ if (!v.length) continue;
383
344
  const properties = v.reduce((acc, x) => ({ ...acc, [x.name]: x.schema }), {});
384
345
  params.push({ name, schema: { type: "object", properties } });
385
346
  }
@@ -443,8 +404,7 @@ const prepareRoutes = async (ctx) => {
443
404
  const routes = {};
444
405
  for (const [path, pathConfig] of Object.entries(ctx.doc.paths ?? {})) {
445
406
  ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
446
- if (!isObject(pathConfig))
447
- continue;
407
+ if (!isObject(pathConfig)) continue;
448
408
  if ("$ref" in pathConfig) {
449
409
  console.warn(`${ctx.logTag} $ref should be resolved before (skipping)`);
450
410
  continue;
@@ -452,14 +412,12 @@ const prepareRoutes = async (ctx) => {
452
412
  for (const method of HttpMethods) {
453
413
  ctx.logTag = `${method.toUpperCase().padEnd(6, " ")} ${path}`;
454
414
  const config = pathConfig[method];
455
- if (!config)
456
- continue;
415
+ if (!config) continue;
457
416
  if (pathConfig.parameters) {
458
417
  config.parameters = [...config.parameters ?? [], ...pathConfig.parameters];
459
418
  }
460
419
  const [ns, op] = getOpName(ctx, { ...config, method, path });
461
- if (!routes[ns])
462
- routes[ns] = [];
420
+ if (!routes[ns]) routes[ns] = [];
463
421
  const joined = [ns, op].join(".");
464
422
  if (ctx.usedNames.has(joined)) {
465
423
  continue;
@@ -499,8 +457,7 @@ const generateAst = async (ctx) => {
499
457
  return { modules, types };
500
458
  };
501
459
  const loadSchema = async (url, upgrade = true) => {
502
- if (url.startsWith("file://"))
503
- url = url.substring(7);
460
+ if (url.startsWith("file://")) url = url.substring(7);
504
461
  const { bundle } = await redocly.bundle({
505
462
  ref: url,
506
463
  config: await redocly.createConfig({}),
@@ -9,6 +9,9 @@ import { convertObj } from 'swagger2openapi';
9
9
  import ts from 'typescript';
10
10
  import path from 'node:path';
11
11
 
12
+ var name = "apigen-ts";
13
+ var version = "0.2.0";
14
+
12
15
  const initCtx = (config) => {
13
16
  return {
14
17
  source: "",
@@ -23,8 +26,8 @@ const initCtx = (config) => {
23
26
  };
24
27
  const getCliConfig = () => {
25
28
  const argv = cli({
26
- name: "apigen",
27
- version: "0.1.1",
29
+ name,
30
+ version,
28
31
  parameters: ["<source>", "[output]"],
29
32
  flags: {
30
33
  name: {
@@ -49,8 +52,7 @@ const getCliConfig = () => {
49
52
  };
50
53
 
51
54
  const unref = (ctx, s) => {
52
- if (!s)
53
- return void 0;
55
+ if (!s) return void 0;
54
56
  if ("$ref" in s && s.$ref) {
55
57
  const parts = s.$ref.replace("#/", "").split("/");
56
58
  const obj = parts.reduce(
@@ -58,8 +60,7 @@ const unref = (ctx, s) => {
58
60
  (acc, x) => get(acc, x, get(acc, decodeURIComponent(x).replaceAll("~1", "/"))),
59
61
  ctx.doc
60
62
  );
61
- if (obj)
62
- return obj;
63
+ if (obj) return obj;
63
64
  console.warn(`${ctx.logTag} ref ${s.$ref} not found`);
64
65
  return void 0;
65
66
  }
@@ -67,11 +68,9 @@ const unref = (ctx, s) => {
67
68
  };
68
69
  const getReqSchema = (ctx, config) => {
69
70
  const req = unref(ctx, config.requestBody);
70
- if (!req)
71
- return void 0;
71
+ if (!req) return void 0;
72
72
  const cts = Object.entries(req.content ?? {}).map((x) => [x[0].split(";")[0], x[1].schema]).filter((x) => x[1]);
73
- if (cts.length === 0)
74
- return void 0;
73
+ if (cts.length === 0) return void 0;
75
74
  const pretenders = [
76
75
  "application/json",
77
76
  "text/",
@@ -80,8 +79,7 @@ const getReqSchema = (ctx, config) => {
80
79
  ];
81
80
  for (const p of pretenders) {
82
81
  const ct = cts.find((x) => x[0].startsWith(p));
83
- if (ct)
84
- return ct;
82
+ if (ct) return ct;
85
83
  }
86
84
  cts.map((x) => x[0]);
87
85
  return void 0;
@@ -89,14 +87,11 @@ const getReqSchema = (ctx, config) => {
89
87
  const getRepSchema = (ctx, config) => {
90
88
  const successCodes = Object.keys(config.responses ?? {}).filter((x) => x.startsWith("2")).filter((x) => get(config, ["responses", x, "content"]));
91
89
  const cts = Object.entries(get(config, ["responses", successCodes[0], "content"], {})).filter((x) => x[1].schema);
92
- if (cts.length === 0)
93
- return void 0;
90
+ if (cts.length === 0) return void 0;
94
91
  const ctJson = cts.find((x) => x[0].startsWith("application/json"));
95
- if (ctJson)
96
- return ctJson[1].schema;
92
+ if (ctJson) return ctJson[1].schema;
97
93
  const ctText = cts.find((x) => x[0].startsWith("text/"));
98
- if (ctText)
99
- return { type: "string" };
94
+ if (ctText) return { type: "string" };
100
95
  cts.map((x) => x[0]).join(", ");
101
96
  return void 0;
102
97
  };
@@ -169,25 +164,18 @@ const Keywords = /* @__PURE__ */ new Set([
169
164
  ]);
170
165
  const normalizeIdentifier = (val, asVar = false) => {
171
166
  let name = val.replace("#/components/schemas/", "").replaceAll("'", "").replace(/[^a-zA-Z0-9]/g, "_");
172
- if (name.match(/^\d/))
173
- name = `$${name}`;
174
- if (asVar && Keywords.has(name))
175
- name = `$${name}`;
167
+ if (name.match(/^\d/)) name = `$${name}`;
168
+ if (asVar && Keywords.has(name)) name = `$${name}`;
176
169
  return name;
177
170
  };
178
171
  const makeInlineEnum = (s) => {
179
- if (!s.enum)
180
- return void 0;
172
+ if (!s.enum) return void 0;
181
173
  const values = filterEmpty(s.enum);
182
- if (!values.length)
183
- return void 0;
174
+ if (!values.length) return void 0;
184
175
  if (!s.type) {
185
- if (values.every((x) => typeof x === "string"))
186
- s.type = "string";
187
- if (values.every((x) => typeof x === "number"))
188
- s.type = "number";
189
- if (values.every((x) => typeof x === "boolean"))
190
- s.type = "boolean";
176
+ if (values.every((x) => typeof x === "string")) s.type = "string";
177
+ if (values.every((x) => typeof x === "number")) s.type = "number";
178
+ if (values.every((x) => typeof x === "boolean")) s.type = "boolean";
191
179
  }
192
180
  if (s.type === "string") {
193
181
  const tokens = uniq(values).map((x) => f$2.createStringLiteral(x.toString()));
@@ -199,10 +187,8 @@ const makeInlineEnum = (s) => {
199
187
  }
200
188
  if (s.type === "boolean") {
201
189
  const tokens = [];
202
- if (values.includes(true))
203
- tokens.push(f$2.createToken(ts.SyntaxKind.TrueKeyword));
204
- if (values.includes(false))
205
- tokens.push(f$2.createToken(ts.SyntaxKind.FalseKeyword));
190
+ if (values.includes(true)) tokens.push(f$2.createToken(ts.SyntaxKind.TrueKeyword));
191
+ if (values.includes(false)) tokens.push(f$2.createToken(ts.SyntaxKind.FalseKeyword));
206
192
  return f$2.createUnionTypeNode(tokens.map((x) => f$2.createLiteralTypeNode(x)));
207
193
  }
208
194
  console.warn(`enum with unknown type "${s.type}" in`, s);
@@ -210,33 +196,25 @@ const makeInlineEnum = (s) => {
210
196
  };
211
197
  const makeType = (ctx, s) => {
212
198
  const mk = makeType.bind(null, ctx);
213
- if (s === void 0)
214
- return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
215
- if (s === null)
216
- return f$2.createLiteralTypeNode(f$2.createNull());
199
+ if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
200
+ if (s === null) return f$2.createLiteralTypeNode(f$2.createNull());
217
201
  if ("$ref" in s && s.$ref) {
218
202
  const parts = s.$ref.replace("#/", "").split("/");
219
203
  if (parts.length === 3 && parts[0] === "components" && parts[1] === "schemas") {
220
204
  return f$2.createTypeReferenceNode(normalizeIdentifier(parts[2], true));
221
205
  }
222
206
  const t = unref(ctx, s);
223
- if (!t)
224
- throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
207
+ if (!t) throw new Error(`makeTypeRef: ref not found ${JSON.stringify(s)}`);
225
208
  return makeType(ctx, t);
226
209
  }
227
- if ("oneOf" in s && s.oneOf)
228
- return f$2.createUnionTypeNode(s.oneOf.map(mk));
229
- if ("anyOf" in s && s.anyOf)
230
- return f$2.createUnionTypeNode(s.anyOf.map(mk));
231
- if ("allOf" in s && s.allOf)
232
- return f$2.createIntersectionTypeNode(s.allOf.map(mk));
233
- if ("type" in s && s.type === "integer")
234
- s.type = "number";
210
+ if ("oneOf" in s && s.oneOf) return f$2.createUnionTypeNode(s.oneOf.map(mk));
211
+ if ("anyOf" in s && s.anyOf) return f$2.createUnionTypeNode(s.anyOf.map(mk));
212
+ if ("allOf" in s && s.allOf) return f$2.createIntersectionTypeNode(s.allOf.map(mk));
213
+ if ("type" in s && s.type === "integer") s.type = "number";
235
214
  if ("enum" in s && s.enum && !Array.isArray(s.type)) {
236
215
  const isArray2 = s.type === "array";
237
216
  const t = makeInlineEnum(isArray2 ? { ...s, type: s.items?.type } : s);
238
- if (t)
239
- return isArray2 ? f$2.createArrayTypeNode(t) : t;
217
+ if (t) return isArray2 ? f$2.createArrayTypeNode(t) : t;
240
218
  }
241
219
  if ("properties" in s && s.properties) {
242
220
  return f$2.createTypeLiteralNode(
@@ -251,37 +229,26 @@ const makeType = (ctx, s) => {
251
229
  if (Array.isArray(s.type)) {
252
230
  const types = [];
253
231
  for (const type of s.type) {
254
- if (type === "null")
255
- types.push({ type: "null" });
256
- else
257
- types.push({ ...s, type });
232
+ if (type === "null") types.push({ type: "null" });
233
+ else types.push({ ...s, type });
258
234
  }
259
235
  return mk({ oneOf: types });
260
236
  }
261
237
  let t;
262
- if (s.type === "object")
263
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
264
- else if (s.type === "boolean")
265
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
266
- else if (s.type === "number")
267
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
268
- else if (s.type === "string")
269
- t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
270
- else if (s.type === "array")
271
- t = f$2.createArrayTypeNode(mk(s.items));
272
- else if (s.type === "null")
273
- t = f$2.createLiteralTypeNode(f$2.createNull());
274
- else if (isArray(s.type))
275
- t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
238
+ if (s.type === "object") t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
239
+ else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
240
+ else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
241
+ else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
242
+ else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
243
+ else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
244
+ else if (isArray(s.type)) t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
276
245
  else {
277
246
  console.warn(`makeType: unknown type "${s.type}"`);
278
247
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
279
248
  }
280
249
  if (s.type === "string") {
281
- if (s.format === "binary")
282
- t = f$2.createTypeReferenceNode("File");
283
- if (s.format === "date-time" && ctx.parseDates)
284
- t = f$2.createTypeReferenceNode("Date");
250
+ if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
251
+ if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
285
252
  }
286
253
  return s.nullable ? f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]) : t;
287
254
  }
@@ -333,15 +300,13 @@ const getOpName = (ctx, op) => {
333
300
  }
334
301
  fn = normalizeOpName(fn);
335
302
  let nsr = ns.split("").map((x) => `[${x.toUpperCase()}${x.toLowerCase()}]`).join("");
336
- if (nsr.endsWith("[Ss]"))
337
- nsr += "?";
303
+ if (nsr.endsWith("[Ss]")) nsr += "?";
338
304
  fn = fn.replace(new RegExp(`^${nsr}([Cc]ontroller|[Ss]ervice)?([A-Z].*)$`), "$2");
339
305
  fn = lowerFirst(fn);
340
306
  const proposal = [ns, fn];
341
307
  if (ctx.resolveName) {
342
308
  const res = ctx.resolveName(ctx, op, proposal);
343
- if (Array.isArray(res) && res.length === 2)
344
- return res;
309
+ if (Array.isArray(res) && res.length === 2) return res;
345
310
  if (res !== void 0) {
346
311
  console.warn(`${ctx.logTag} resolveName should return [ns, fn] or undefined (skipping)`);
347
312
  }
@@ -349,11 +314,9 @@ const getOpName = (ctx, op) => {
349
314
  return proposal;
350
315
  };
351
316
  const prepareUrl = (url, rename) => {
352
- for (const [k, v] of Object.entries(rename))
353
- url = url.replaceAll(`{${k}}`, "${" + v + "}");
317
+ for (const [k, v] of Object.entries(rename)) url = url.replaceAll(`{${k}}`, "${" + v + "}");
354
318
  const parts = url.split("${");
355
- if (parts.length === 1)
356
- return f$1.createStringLiteral(url);
319
+ if (parts.length === 1) return f$1.createStringLiteral(url);
357
320
  return f$1.createTemplateExpression(
358
321
  f$1.createTemplateHead(parts[0]),
359
322
  parts.slice(1).map((x, i) => {
@@ -373,13 +336,11 @@ const prepareOp = (ctx, cfg, opName) => {
373
336
  const repSchema = getRepSchema(ctx, cfg);
374
337
  const allParams = filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
375
338
  const params = uniqBy(allParams.filter((x) => x.in === "path"), "name");
376
- if (reqSchema)
377
- params.push({ name: "body", schema: reqSchema[1] });
339
+ if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
378
340
  const search = allParams.filter((x) => x.in === "query");
379
341
  allParams.filter((x) => x.in === "header");
380
342
  for (const [name, v] of Object.entries({ search })) {
381
- if (!v.length)
382
- continue;
343
+ if (!v.length) continue;
383
344
  const properties = v.reduce((acc, x) => ({ ...acc, [x.name]: x.schema }), {});
384
345
  params.push({ name, schema: { type: "object", properties } });
385
346
  }
@@ -443,8 +404,7 @@ const prepareRoutes = async (ctx) => {
443
404
  const routes = {};
444
405
  for (const [path, pathConfig] of Object.entries(ctx.doc.paths ?? {})) {
445
406
  ctx.logTag = `${"[ALL]".toUpperCase().padEnd(6, " ")} ${path}`;
446
- if (!isObject(pathConfig))
447
- continue;
407
+ if (!isObject(pathConfig)) continue;
448
408
  if ("$ref" in pathConfig) {
449
409
  console.warn(`${ctx.logTag} $ref should be resolved before (skipping)`);
450
410
  continue;
@@ -452,14 +412,12 @@ const prepareRoutes = async (ctx) => {
452
412
  for (const method of HttpMethods) {
453
413
  ctx.logTag = `${method.toUpperCase().padEnd(6, " ")} ${path}`;
454
414
  const config = pathConfig[method];
455
- if (!config)
456
- continue;
415
+ if (!config) continue;
457
416
  if (pathConfig.parameters) {
458
417
  config.parameters = [...config.parameters ?? [], ...pathConfig.parameters];
459
418
  }
460
419
  const [ns, op] = getOpName(ctx, { ...config, method, path });
461
- if (!routes[ns])
462
- routes[ns] = [];
420
+ if (!routes[ns]) routes[ns] = [];
463
421
  const joined = [ns, op].join(".");
464
422
  if (ctx.usedNames.has(joined)) {
465
423
  continue;
@@ -499,8 +457,7 @@ const generateAst = async (ctx) => {
499
457
  return { modules, types };
500
458
  };
501
459
  const loadSchema = async (url, upgrade = true) => {
502
- if (url.startsWith("file://"))
503
- url = url.substring(7);
460
+ if (url.startsWith("file://")) url = url.substring(7);
504
461
  const { bundle } = await redocly.bundle({
505
462
  ref: url,
506
463
  config: await redocly.createConfig({}),
package/dist/main.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  require('fs/promises');
4
4
  require('path');
5
5
  require('url');
6
- var main = require('./main-c2426ec2.cjs');
6
+ var main = require('./main-Dkcn3PTN.cjs');
7
7
  require('cleye');
8
8
  require('@redocly/openapi-core');
9
9
  require('array-utils-ts');
package/dist/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import 'fs/promises';
2
2
  import 'path';
3
3
  import 'url';
4
- export { a as apigen } from './main-bbe33f1b.js';
4
+ export { a as apigen } from './main-byN_-fkF.js';
5
5
  import 'cleye';
6
6
  import '@redocly/openapi-core';
7
7
  import 'array-utils-ts';
package/dist/main.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import 'fs/promises';
2
2
  import 'path';
3
3
  import 'url';
4
- export { a as apigen } from './main-bbe33f1b.mjs';
4
+ export { a as apigen } from './main-byN_-fkF.mjs';
5
5
  import 'cleye';
6
6
  import '@redocly/openapi-core';
7
7
  import 'array-utils-ts';
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "apigen-ts",
4
- "version": "0.1.1",
4
+ "version": "0.2.0",
5
5
  "license": "MIT",
6
- "author": "Vlad Pronsky <v.pronsky@gmail.com>",
6
+ "author": "vladkens <v.pronsky@gmail.com>",
7
7
  "repository": "vladkens/apigen-ts",
8
8
  "description": "OpenAPI TypeScript client generator",
9
9
  "keywords": [
@@ -22,7 +22,7 @@
22
22
  "ci": "tsc --noEmit && yarn test-cov && yarn build"
23
23
  },
24
24
  "dependencies": {
25
- "@redocly/openapi-core": "^1.6.0",
25
+ "@redocly/openapi-core": "^1.22.1",
26
26
  "@types/lodash-es": "^4.17.12",
27
27
  "@types/swagger2openapi": "^7.0.4",
28
28
  "array-utils-ts": "^0.1.2",
@@ -31,14 +31,14 @@
31
31
  "swagger2openapi": "^7.0.8"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/node": "^20.10.8",
35
- "c8": "^9.0.0",
36
- "fetch-mock": "^9.11.0",
37
- "pkgroll": "^2.0.1",
38
- "prettier": "^3.1.0",
39
- "prettier-plugin-organize-imports": "^3.2.4",
34
+ "@types/node": "^22.5.2",
35
+ "c8": "^10.1.2",
36
+ "fetch-mock": "^11.1.3",
37
+ "pkgroll": "^2.4.2",
38
+ "prettier": "^3.3.3",
39
+ "prettier-plugin-organize-imports": "^4.0.0",
40
40
  "tsm": "^2.3.0",
41
- "typescript": "^5.3.2",
41
+ "typescript": "^5.5.4",
42
42
  "uvu": "^0.5.6"
43
43
  },
44
44
  "peerDependencies": {
package/readme.md CHANGED
@@ -115,13 +115,34 @@ class MyClient extends ApiClient {
115
115
  }
116
116
 
117
117
  try {
118
- const api = MyClient()
118
+ const api = new MyClient()
119
119
  const pet = await api.pet.getPetById(404)
120
120
  } catch (e) {
121
121
  console.log(e) // e is { code: "API_ERROR" }
122
122
  }
123
123
  ```
124
124
 
125
+ ### Base url resolving
126
+
127
+ You can modify how the endpoint url is created. By default [URL constructor](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) used to resolve endpoint url like: `new URL(path, baseUrl)` which has specific resolving [rules](https://developer.mozilla.org/en-US/docs/Web/API/URL_API/Resolving_relative_references). E.g.:
128
+
129
+ - `new URL("/v2/cats", "https://example.com/v1/") // -> https://example.com/v2/cats`
130
+ - `new URL("v2/cats", "https://example.com/v1/") // -> https://example.com/v1/v2/cats`
131
+
132
+ If you want to have custom endpoint url resolving rules, you can override `PrepareFetchUrl` method. For more details see [issue](https://github.com/vladkens/apigen-ts/issues/2).
133
+
134
+ ```ts
135
+ class MyClient extends ApiClient {
136
+ PrepareFetchUrl(path: string) {
137
+ return new URL(`${this.Config.baseUrl}/${path}`.replace(/\/{2,}/g, "/"))
138
+ }
139
+ }
140
+
141
+ const api = new MyClient({ baseUrl: "https://example.com/v1" })
142
+ // will call: https://example.com/v1/pet/ instead of https://example.com/pet/
143
+ const pet = await api.pet.getPetById(404)
144
+ ```
145
+
125
146
  ### Node.js API
126
147
 
127
148
  Create file like `apigen.mjs` with content: