apigen-ts 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var main$1 = require('./main-Cic4LkJH.cjs');
3
+ var main$1 = require('./main-C0qK6dZX.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-CRZklgfk.js';
2
+ import { a as apigen, g as getCliConfig } from './main-l0LIDQ3K.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-CRZklgfk.mjs';
1
+ import { a as apigen, g as getCliConfig } from './main-l0LIDQ3K.mjs';
2
2
  import 'fs/promises';
3
3
  import 'path';
4
4
  import 'url';
@@ -13,20 +13,30 @@ var path = require('node:path');
13
13
 
14
14
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
15
  var name = "apigen-ts";
16
- var version = "1.0.1";
16
+ var version = "1.2.0";
17
17
 
18
18
  const initCtx = (config) => {
19
19
  return {
20
20
  source: "",
21
21
  output: "",
22
22
  name: "ApiClient",
23
- parseDates: false,
24
23
  doc: { openapi: "3.1.0" },
24
+ parseDates: false,
25
+ inlineEnums: false,
26
+ headers: {},
25
27
  ...config,
26
28
  logTag: "",
27
29
  usedNames: /* @__PURE__ */ new Set()
28
30
  };
29
31
  };
32
+ const parseHeaders = (items) => {
33
+ const headers = {};
34
+ for (const item of items) {
35
+ const [key, val] = item.split(":");
36
+ if (key && val) headers[key.trim()] = val.trim();
37
+ }
38
+ return headers;
39
+ };
30
40
  const getCliConfig = () => {
31
41
  const argv = cleye.cli({
32
42
  name,
@@ -35,13 +45,24 @@ const getCliConfig = () => {
35
45
  flags: {
36
46
  name: {
37
47
  type: String,
38
- description: "api class name to export",
48
+ description: "API class name to export",
39
49
  default: "ApiClient"
40
50
  },
41
51
  parseDates: {
42
52
  type: Boolean,
43
- description: "parse dates as Date objects",
53
+ description: "Parse dates as Date objects",
54
+ default: false
55
+ },
56
+ inlineEnums: {
57
+ type: Boolean,
58
+ description: "Use inline enums instead of enum types",
44
59
  default: false
60
+ },
61
+ header: {
62
+ type: [String],
63
+ alias: "H",
64
+ description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
65
+ default: []
45
66
  }
46
67
  }
47
68
  });
@@ -49,7 +70,9 @@ const getCliConfig = () => {
49
70
  source: argv._.source,
50
71
  output: argv._.output ?? null,
51
72
  name: argv.flags.name,
52
- parseDates: argv.flags.parseDates
73
+ parseDates: argv.flags.parseDates,
74
+ inlineEnums: argv.flags.inlineEnums,
75
+ headers: parseHeaders(argv.flags.header)
53
76
  };
54
77
  return config;
55
78
  };
@@ -207,6 +230,12 @@ const makeObject = (ctx, s) => {
207
230
  }
208
231
  return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
209
232
  };
233
+ const makeLiteralUnion = (ctx, types) => {
234
+ const tokens = types.map((x) => makeType(ctx, { type: x }));
235
+ const hasUnknown = tokens.some((x) => x.kind === ts.SyntaxKind.UnknownKeyword);
236
+ if (hasUnknown) return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
237
+ return f$2.createUnionTypeNode(tokens);
238
+ };
210
239
  const makeType = (ctx, s) => {
211
240
  const mk = makeType.bind(null, ctx);
212
241
  if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
@@ -239,22 +268,14 @@ const makeType = (ctx, s) => {
239
268
  );
240
269
  }
241
270
  if ("type" in s) {
242
- if (Array.isArray(s.type)) {
243
- const types = [];
244
- for (const type of s.type) {
245
- if (type === "null") types.push({ type: "null" });
246
- else types.push({ ...s, type });
247
- }
248
- return mk({ oneOf: types });
249
- }
250
271
  let t;
251
272
  if (s.type === "object") t = makeObject(ctx, s);
252
273
  else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
253
274
  else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
254
275
  else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
255
- else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
256
276
  else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
257
- else if (lodashEs.isArray(s.type)) t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
277
+ else if (lodashEs.isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
278
+ else if (s.type === "array" && !lodashEs.isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
258
279
  else {
259
280
  console.warn(`makeType: unknown type "${s.type}"`);
260
281
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
@@ -263,7 +284,10 @@ const makeType = (ctx, s) => {
263
284
  if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
264
285
  if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
265
286
  }
266
- return s.nullable ? f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]) : t;
287
+ if ("nullable" in s && s.nullable) {
288
+ return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
289
+ }
290
+ return t;
267
291
  }
268
292
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
269
293
  };
@@ -274,7 +298,7 @@ const isStringEnum = (s) => {
274
298
  return false;
275
299
  };
276
300
  const makeTypeAlias = (ctx, name, s) => {
277
- if (isStringEnum(s)) {
301
+ if (isStringEnum(s) && !ctx.inlineEnums) {
278
302
  const tokens1 = lodashEs.uniq(s.enum);
279
303
  const tokens2 = arrayUtilsTs.filterEmpty(tokens1);
280
304
  if (tokens1.length !== tokens2.length) {
@@ -469,12 +493,23 @@ const generateAst = async (ctx) => {
469
493
  }
470
494
  return { modules, types };
471
495
  };
472
- const loadSchema = async (url, upgrade = true) => {
496
+ const loadSchema = async ({
497
+ url,
498
+ upgrade = true,
499
+ headers = {}
500
+ }) => {
473
501
  if (url.startsWith("file://")) url = url.substring(7);
474
502
  const { bundle } = await redocly.bundle({
475
503
  ref: url,
476
504
  config: await redocly.createConfig({}),
477
- removeUnusedComponents: false
505
+ removeUnusedComponents: false,
506
+ externalRefResolver: new redocly.BaseResolver({
507
+ http: {
508
+ headers: Object.entries(headers).map(([name, value]) => {
509
+ return { name, value, matches: "**" };
510
+ })
511
+ }
512
+ })
478
513
  });
479
514
  if (bundle.parsed.swagger && upgrade) {
480
515
  const { openapi } = await swagger2openapi.convertObj(bundle.parsed, { patch: true });
@@ -512,10 +547,10 @@ const formatCode = async (code) => {
512
547
  };
513
548
 
514
549
  const apigen = async (config) => {
515
- const doc = await loadSchema(config.source);
550
+ const doc = await loadSchema({ url: config.source, headers: config.headers });
516
551
  const ctx = initCtx({ ...config, doc });
517
552
  const { modules, types } = await generateAst(ctx);
518
- 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-Cic4LkJH.cjs', document.baseURI).href)))), "_template.ts");
553
+ const filepath = path$1.join(path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('main-C0qK6dZX.cjs', document.baseURI).href)))), "_template.ts");
519
554
  const file = await fs.readFile(filepath, "utf-8");
520
555
  let code = [
521
556
  `// Auto-generated by https://github.com/vladkens/apigen-ts`,
@@ -2,28 +2,38 @@ import fs from 'fs/promises';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { cli } from 'cleye';
5
- import redocly from '@redocly/openapi-core';
5
+ import redocly, { BaseResolver } from '@redocly/openapi-core';
6
6
  import { filterEmpty, filterNullable } from 'array-utils-ts';
7
- import { get, uniq, upperFirst, isArray, isBoolean, isObject, sortBy, lowerFirst, uniqBy } from 'lodash-es';
7
+ import { get, uniq, upperFirst, isArray, isBoolean, sortBy, isObject, lowerFirst, uniqBy } from 'lodash-es';
8
8
  import { convertObj } from 'swagger2openapi';
9
9
  import ts from 'typescript';
10
10
  import path from 'node:path';
11
11
 
12
12
  var name = "apigen-ts";
13
- var version = "1.0.1";
13
+ var version = "1.2.0";
14
14
 
15
15
  const initCtx = (config) => {
16
16
  return {
17
17
  source: "",
18
18
  output: "",
19
19
  name: "ApiClient",
20
- parseDates: false,
21
20
  doc: { openapi: "3.1.0" },
21
+ parseDates: false,
22
+ inlineEnums: false,
23
+ headers: {},
22
24
  ...config,
23
25
  logTag: "",
24
26
  usedNames: /* @__PURE__ */ new Set()
25
27
  };
26
28
  };
29
+ const parseHeaders = (items) => {
30
+ const headers = {};
31
+ for (const item of items) {
32
+ const [key, val] = item.split(":");
33
+ if (key && val) headers[key.trim()] = val.trim();
34
+ }
35
+ return headers;
36
+ };
27
37
  const getCliConfig = () => {
28
38
  const argv = cli({
29
39
  name,
@@ -32,13 +42,24 @@ const getCliConfig = () => {
32
42
  flags: {
33
43
  name: {
34
44
  type: String,
35
- description: "api class name to export",
45
+ description: "API class name to export",
36
46
  default: "ApiClient"
37
47
  },
38
48
  parseDates: {
39
49
  type: Boolean,
40
- description: "parse dates as Date objects",
50
+ description: "Parse dates as Date objects",
51
+ default: false
52
+ },
53
+ inlineEnums: {
54
+ type: Boolean,
55
+ description: "Use inline enums instead of enum types",
41
56
  default: false
57
+ },
58
+ header: {
59
+ type: [String],
60
+ alias: "H",
61
+ description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
62
+ default: []
42
63
  }
43
64
  }
44
65
  });
@@ -46,7 +67,9 @@ const getCliConfig = () => {
46
67
  source: argv._.source,
47
68
  output: argv._.output ?? null,
48
69
  name: argv.flags.name,
49
- parseDates: argv.flags.parseDates
70
+ parseDates: argv.flags.parseDates,
71
+ inlineEnums: argv.flags.inlineEnums,
72
+ headers: parseHeaders(argv.flags.header)
50
73
  };
51
74
  return config;
52
75
  };
@@ -204,6 +227,12 @@ const makeObject = (ctx, s) => {
204
227
  }
205
228
  return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
206
229
  };
230
+ const makeLiteralUnion = (ctx, types) => {
231
+ const tokens = types.map((x) => makeType(ctx, { type: x }));
232
+ const hasUnknown = tokens.some((x) => x.kind === ts.SyntaxKind.UnknownKeyword);
233
+ if (hasUnknown) return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
234
+ return f$2.createUnionTypeNode(tokens);
235
+ };
207
236
  const makeType = (ctx, s) => {
208
237
  const mk = makeType.bind(null, ctx);
209
238
  if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
@@ -236,22 +265,14 @@ const makeType = (ctx, s) => {
236
265
  );
237
266
  }
238
267
  if ("type" in s) {
239
- if (Array.isArray(s.type)) {
240
- const types = [];
241
- for (const type of s.type) {
242
- if (type === "null") types.push({ type: "null" });
243
- else types.push({ ...s, type });
244
- }
245
- return mk({ oneOf: types });
246
- }
247
268
  let t;
248
269
  if (s.type === "object") t = makeObject(ctx, s);
249
270
  else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
250
271
  else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
251
272
  else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
252
- else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
253
273
  else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
254
- else if (isArray(s.type)) t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
274
+ else if (isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
275
+ else if (s.type === "array" && !isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
255
276
  else {
256
277
  console.warn(`makeType: unknown type "${s.type}"`);
257
278
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
@@ -260,7 +281,10 @@ const makeType = (ctx, s) => {
260
281
  if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
261
282
  if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
262
283
  }
263
- return s.nullable ? f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]) : t;
284
+ if ("nullable" in s && s.nullable) {
285
+ return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
286
+ }
287
+ return t;
264
288
  }
265
289
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
266
290
  };
@@ -271,7 +295,7 @@ const isStringEnum = (s) => {
271
295
  return false;
272
296
  };
273
297
  const makeTypeAlias = (ctx, name, s) => {
274
- if (isStringEnum(s)) {
298
+ if (isStringEnum(s) && !ctx.inlineEnums) {
275
299
  const tokens1 = uniq(s.enum);
276
300
  const tokens2 = filterEmpty(tokens1);
277
301
  if (tokens1.length !== tokens2.length) {
@@ -466,12 +490,23 @@ const generateAst = async (ctx) => {
466
490
  }
467
491
  return { modules, types };
468
492
  };
469
- const loadSchema = async (url, upgrade = true) => {
493
+ const loadSchema = async ({
494
+ url,
495
+ upgrade = true,
496
+ headers = {}
497
+ }) => {
470
498
  if (url.startsWith("file://")) url = url.substring(7);
471
499
  const { bundle } = await redocly.bundle({
472
500
  ref: url,
473
501
  config: await redocly.createConfig({}),
474
- removeUnusedComponents: false
502
+ removeUnusedComponents: false,
503
+ externalRefResolver: new BaseResolver({
504
+ http: {
505
+ headers: Object.entries(headers).map(([name, value]) => {
506
+ return { name, value, matches: "**" };
507
+ })
508
+ }
509
+ })
475
510
  });
476
511
  if (bundle.parsed.swagger && upgrade) {
477
512
  const { openapi } = await convertObj(bundle.parsed, { patch: true });
@@ -509,7 +544,7 @@ const formatCode = async (code) => {
509
544
  };
510
545
 
511
546
  const apigen = async (config) => {
512
- const doc = await loadSchema(config.source);
547
+ const doc = await loadSchema({ url: config.source, headers: config.headers });
513
548
  const ctx = initCtx({ ...config, doc });
514
549
  const { modules, types } = await generateAst(ctx);
515
550
  const filepath = join(dirname(fileURLToPath(import.meta.url)), "_template.ts");
@@ -2,28 +2,38 @@ import fs from 'fs/promises';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { cli } from 'cleye';
5
- import redocly from '@redocly/openapi-core';
5
+ import redocly, { BaseResolver } from '@redocly/openapi-core';
6
6
  import { filterEmpty, filterNullable } from 'array-utils-ts';
7
- import { get, uniq, upperFirst, isArray, isBoolean, isObject, sortBy, lowerFirst, uniqBy } from 'lodash-es';
7
+ import { get, uniq, upperFirst, isArray, isBoolean, sortBy, isObject, lowerFirst, uniqBy } from 'lodash-es';
8
8
  import { convertObj } from 'swagger2openapi';
9
9
  import ts from 'typescript';
10
10
  import path from 'node:path';
11
11
 
12
12
  var name = "apigen-ts";
13
- var version = "1.0.1";
13
+ var version = "1.2.0";
14
14
 
15
15
  const initCtx = (config) => {
16
16
  return {
17
17
  source: "",
18
18
  output: "",
19
19
  name: "ApiClient",
20
- parseDates: false,
21
20
  doc: { openapi: "3.1.0" },
21
+ parseDates: false,
22
+ inlineEnums: false,
23
+ headers: {},
22
24
  ...config,
23
25
  logTag: "",
24
26
  usedNames: /* @__PURE__ */ new Set()
25
27
  };
26
28
  };
29
+ const parseHeaders = (items) => {
30
+ const headers = {};
31
+ for (const item of items) {
32
+ const [key, val] = item.split(":");
33
+ if (key && val) headers[key.trim()] = val.trim();
34
+ }
35
+ return headers;
36
+ };
27
37
  const getCliConfig = () => {
28
38
  const argv = cli({
29
39
  name,
@@ -32,13 +42,24 @@ const getCliConfig = () => {
32
42
  flags: {
33
43
  name: {
34
44
  type: String,
35
- description: "api class name to export",
45
+ description: "API class name to export",
36
46
  default: "ApiClient"
37
47
  },
38
48
  parseDates: {
39
49
  type: Boolean,
40
- description: "parse dates as Date objects",
50
+ description: "Parse dates as Date objects",
51
+ default: false
52
+ },
53
+ inlineEnums: {
54
+ type: Boolean,
55
+ description: "Use inline enums instead of enum types",
41
56
  default: false
57
+ },
58
+ header: {
59
+ type: [String],
60
+ alias: "H",
61
+ description: 'HTTP header as key=value (e.g., -H "x-api-key: your-key"). Used only when generating code.',
62
+ default: []
42
63
  }
43
64
  }
44
65
  });
@@ -46,7 +67,9 @@ const getCliConfig = () => {
46
67
  source: argv._.source,
47
68
  output: argv._.output ?? null,
48
69
  name: argv.flags.name,
49
- parseDates: argv.flags.parseDates
70
+ parseDates: argv.flags.parseDates,
71
+ inlineEnums: argv.flags.inlineEnums,
72
+ headers: parseHeaders(argv.flags.header)
50
73
  };
51
74
  return config;
52
75
  };
@@ -204,6 +227,12 @@ const makeObject = (ctx, s) => {
204
227
  }
205
228
  return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
206
229
  };
230
+ const makeLiteralUnion = (ctx, types) => {
231
+ const tokens = types.map((x) => makeType(ctx, { type: x }));
232
+ const hasUnknown = tokens.some((x) => x.kind === ts.SyntaxKind.UnknownKeyword);
233
+ if (hasUnknown) return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
234
+ return f$2.createUnionTypeNode(tokens);
235
+ };
207
236
  const makeType = (ctx, s) => {
208
237
  const mk = makeType.bind(null, ctx);
209
238
  if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
@@ -236,22 +265,14 @@ const makeType = (ctx, s) => {
236
265
  );
237
266
  }
238
267
  if ("type" in s) {
239
- if (Array.isArray(s.type)) {
240
- const types = [];
241
- for (const type of s.type) {
242
- if (type === "null") types.push({ type: "null" });
243
- else types.push({ ...s, type });
244
- }
245
- return mk({ oneOf: types });
246
- }
247
268
  let t;
248
269
  if (s.type === "object") t = makeObject(ctx, s);
249
270
  else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
250
271
  else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
251
272
  else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
252
- else if (s.type === "array") t = f$2.createArrayTypeNode(mk(s.items));
253
273
  else if (s.type === "null") t = f$2.createLiteralTypeNode(f$2.createNull());
254
- else if (isArray(s.type)) t = f$2.createUnionTypeNode(s.type.map((x) => mk({ type: x })));
274
+ else if (isArray(s.type)) t = makeLiteralUnion(ctx, s.type);
275
+ else if (s.type === "array" && !isBoolean(s.items)) t = f$2.createArrayTypeNode(mk(s.items));
255
276
  else {
256
277
  console.warn(`makeType: unknown type "${s.type}"`);
257
278
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
@@ -260,7 +281,10 @@ const makeType = (ctx, s) => {
260
281
  if (s.format === "binary") t = f$2.createTypeReferenceNode("File");
261
282
  if (s.format === "date-time" && ctx.parseDates) t = f$2.createTypeReferenceNode("Date");
262
283
  }
263
- return s.nullable ? f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]) : t;
284
+ if ("nullable" in s && s.nullable) {
285
+ return f$2.createUnionTypeNode([t, f$2.createLiteralTypeNode(f$2.createNull())]);
286
+ }
287
+ return t;
264
288
  }
265
289
  return f$2.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
266
290
  };
@@ -271,7 +295,7 @@ const isStringEnum = (s) => {
271
295
  return false;
272
296
  };
273
297
  const makeTypeAlias = (ctx, name, s) => {
274
- if (isStringEnum(s)) {
298
+ if (isStringEnum(s) && !ctx.inlineEnums) {
275
299
  const tokens1 = uniq(s.enum);
276
300
  const tokens2 = filterEmpty(tokens1);
277
301
  if (tokens1.length !== tokens2.length) {
@@ -466,12 +490,23 @@ const generateAst = async (ctx) => {
466
490
  }
467
491
  return { modules, types };
468
492
  };
469
- const loadSchema = async (url, upgrade = true) => {
493
+ const loadSchema = async ({
494
+ url,
495
+ upgrade = true,
496
+ headers = {}
497
+ }) => {
470
498
  if (url.startsWith("file://")) url = url.substring(7);
471
499
  const { bundle } = await redocly.bundle({
472
500
  ref: url,
473
501
  config: await redocly.createConfig({}),
474
- removeUnusedComponents: false
502
+ removeUnusedComponents: false,
503
+ externalRefResolver: new BaseResolver({
504
+ http: {
505
+ headers: Object.entries(headers).map(([name, value]) => {
506
+ return { name, value, matches: "**" };
507
+ })
508
+ }
509
+ })
475
510
  });
476
511
  if (bundle.parsed.swagger && upgrade) {
477
512
  const { openapi } = await convertObj(bundle.parsed, { patch: true });
@@ -509,7 +544,7 @@ const formatCode = async (code) => {
509
544
  };
510
545
 
511
546
  const apigen = async (config) => {
512
- const doc = await loadSchema(config.source);
547
+ const doc = await loadSchema({ url: config.source, headers: config.headers });
513
548
  const ctx = initCtx({ ...config, doc });
514
549
  const { modules, types } = await generateAst(ctx);
515
550
  const filepath = join(dirname(fileURLToPath(import.meta.url)), "_template.ts");
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-Cic4LkJH.cjs');
6
+ var main = require('./main-C0qK6dZX.cjs');
7
7
  require('cleye');
8
8
  require('@redocly/openapi-core');
9
9
  require('array-utils-ts');
package/dist/main.d.cts CHANGED
@@ -11,7 +11,9 @@ type Config = {
11
11
  output: string | null;
12
12
  name: string;
13
13
  parseDates: boolean;
14
+ inlineEnums: boolean;
14
15
  resolveName?: (ctx: Context, op: OpConfig, proposal: OpName) => OpName | undefined;
16
+ headers: Record<string, string>;
15
17
  };
16
18
  type Context = Config & {
17
19
  doc: Oas3Definition;
package/dist/main.d.mts CHANGED
@@ -11,7 +11,9 @@ type Config = {
11
11
  output: string | null;
12
12
  name: string;
13
13
  parseDates: boolean;
14
+ inlineEnums: boolean;
14
15
  resolveName?: (ctx: Context, op: OpConfig, proposal: OpName) => OpName | undefined;
16
+ headers: Record<string, string>;
15
17
  };
16
18
  type Context = Config & {
17
19
  doc: Oas3Definition;
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-CRZklgfk.js';
4
+ export { a as apigen } from './main-l0LIDQ3K.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-CRZklgfk.mjs';
4
+ export { a as apigen } from './main-l0LIDQ3K.mjs';
5
5
  import 'cleye';
6
6
  import '@redocly/openapi-core';
7
7
  import 'array-utils-ts';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "apigen-ts",
4
- "version": "1.0.1",
4
+ "version": "1.2.0",
5
5
  "license": "MIT",
6
6
  "author": "vladkens <v.pronsky@gmail.com>",
7
7
  "repository": "vladkens/apigen-ts",
@@ -22,23 +22,23 @@
22
22
  "ci": "tsc --noEmit && yarn test-cov && yarn build"
23
23
  },
24
24
  "dependencies": {
25
- "@redocly/openapi-core": "1.25.5",
25
+ "@redocly/openapi-core": "1.34.1",
26
26
  "@types/lodash-es": "4.17.12",
27
27
  "@types/swagger2openapi": "7.0.4",
28
- "array-utils-ts": "0.1.2",
29
- "cleye": "1.3.2",
28
+ "array-utils-ts": "1.0.2",
29
+ "cleye": "1.3.4",
30
30
  "lodash-es": "4.17.21",
31
31
  "swagger2openapi": "7.0.8"
32
32
  },
33
33
  "devDependencies": {
34
- "@types/node": "22.7.5",
35
- "c8": "10.1.2",
36
- "fetch-mock": "11.1.5",
37
- "pkgroll": "2.5.0",
38
- "prettier": "3.3.3",
34
+ "@types/node": "22.14.0",
35
+ "c8": "10.1.3",
36
+ "fetch-mock": "12.5.2",
37
+ "pkgroll": "2.12.1",
38
+ "prettier": "3.5.3",
39
39
  "prettier-plugin-organize-imports": "4.1.0",
40
40
  "tsm": "2.3.0",
41
- "typescript": "5.6.3",
41
+ "typescript": "5.8.3",
42
42
  "uvu": "0.5.6"
43
43
  },
44
44
  "peerDependencies": {
package/readme.md CHANGED
@@ -2,17 +2,16 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- [<img src="https://badgen.net/npm/v/apigen-ts" alt="version" />](https://npmjs.org/package/apigen-ts)
6
- [<img src="https://badgen.net/packagephobia/publish/apigen-ts" alt="size" />](https://packagephobia.now.sh/result?p=apigen-ts)
7
- [<img src="https://badgen.net/npm/dm/apigen-ts" alt="downloads" />](https://npmjs.org/package/apigen-ts)
8
- [<img src="https://badgen.net/github/license/vladkens/apigen-ts" alt="license" />](https://github.com/vladkens/apigen-ts/blob/main/LICENSE)
9
- [<img src="https://badgen.net/static/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee&label" alt="donate" />](https://buymeacoffee.com/vladkens)
5
+ [<img src="https://badges.ws/npm/v/apigen-ts" alt="version" />](https://npmjs.org/package/apigen-ts)
6
+ [<img src="https://badges.ws/packagephobia/publish/apigen-ts" alt="size" />](https://packagephobia.now.sh/result?p=apigen-ts)
7
+ [<img src="https://badges.ws/npm/dm/apigen-ts" alt="downloads" />](https://npmjs.org/package/apigen-ts)
8
+ [<img src="https://badges.ws/github/license/vladkens/apigen-ts" alt="license" />](https://github.com/vladkens/apigen-ts/blob/main/LICENSE)
9
+ [<img src="https://badges.ws/badge/-/buy%20me%20a%20coffee/ff813f?icon=buymeacoffee&label" alt="donate" />](https://buymeacoffee.com/vladkens)
10
10
 
11
11
  </div>
12
12
 
13
13
  <div align="center">
14
14
  <img src="./logo.svg" alt="apigen-ts logo" height="80" />
15
- <div>Simple typed OpenAPI client generator</div>
16
15
  </div>
17
16
 
18
17
  ## Features
@@ -29,7 +28,11 @@
29
28
  ## Install
30
29
 
31
30
  ```sh
32
- yarn install -D apigen-ts
31
+ npm install apigen-ts --save-dev
32
+ ```
33
+
34
+ ```sh
35
+ yarn add -D apigen-ts
33
36
  ```
34
37
 
35
38
  ## Usage
@@ -37,11 +40,14 @@ yarn install -D apigen-ts
37
40
  ### 1. Generate
38
41
 
39
42
  ```sh
43
+ # From file
44
+ yarn apigen-ts ./openapi.json ./api-client.ts
45
+
40
46
  # From url
41
47
  yarn apigen-ts https://petstore3.swagger.io/api/v3/openapi.json ./api-client.ts
42
48
 
43
- # From file
44
- yarn apigen-ts ./openapi.json ./api-client.ts
49
+ # From protected url
50
+ yarn apigen-ts https://secret-api.example.com ./api-client.ts -H "x-api-key: secret-key"
45
51
  ```
46
52
 
47
53
  Run `yarn apigen-ts --help` for more options. Examples of generated clients [here](./examples/).
@@ -92,6 +98,26 @@ const pet = await api.pet.getPetById(1)
92
98
  const createdAt: Date = pet.createdAt // date parsed from string with format=date-time
93
99
  ```
94
100
 
101
+ ### String union as enums
102
+
103
+ You can generate string literal union instead of native enums in case you want to run in Node.js environment with [type-stripping](https://nodejs.org/api/typescript.html#type-stripping). To achive this pass `--inline-enums` command line argument or use `inlineEnums: true` in Node.js API.
104
+
105
+ ```sh
106
+ yarn apigen-ts ./openapi.json ./api-client.ts --inline-enums
107
+ ```
108
+
109
+ This will generate:
110
+
111
+ ```ts
112
+ type MyEnum = "OptionA" | "OptionB"
113
+
114
+ // instead of
115
+ enum MyEnum = {
116
+ OptionA = "OptionA",
117
+ OptionB = "OptionB"
118
+ }
119
+ ```
120
+
95
121
  ### Errors handling
96
122
 
97
123
  An exception will be thrown for all unsuccessful return codes.
@@ -156,6 +182,8 @@ await apigen({
156
182
  // everything below is optional
157
183
  name: "MyApiClient", // default "ApiClient"
158
184
  parseDates: true, // default false
185
+ inlineEnums: false, // default false, use string literal union instead of enum
186
+ headers: { "x-api-key": "secret-key" }, // Custom HTTP headers to use when fetching schema
159
187
  resolveName(ctx, op, proposal) {
160
188
  // proposal is [string, string] which represents module.funcName
161
189
  if (proposal[0] === "users") return // will use default proposal