apigen-ts 0.2.0 → 1.0.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,8 +1,11 @@
1
1
  // Note: Use uppercase for names in ApiClient to avoid conflict with the generated code
2
2
 
3
+ type Headers = Record<string, string>
4
+ export type ApigenHeaders = Headers | ((method: string, path: string) => Headers | Promise<Headers>)
5
+
3
6
  export interface ApigenConfig {
4
7
  baseUrl: string
5
- headers: Record<string, string>
8
+ headers: ApigenHeaders
6
9
  }
7
10
 
8
11
  export interface ApigenRequest extends Omit<RequestInit, "body"> {
@@ -58,7 +61,12 @@ export class ApiClient {
58
61
  url.searchParams.append(k, Array.isArray(v) ? v.join(",") : (v as string))
59
62
  }
60
63
 
61
- const headers = new Headers({ ...this.Config.headers, ...opts.headers })
64
+ const configHeaders =
65
+ typeof this.Config.headers === "function"
66
+ ? await this.Config.headers(method, path)
67
+ : this.Config.headers
68
+
69
+ const headers = new Headers({ ...configHeaders, ...opts.headers })
62
70
  const ct = headers.get("content-type") ?? "application/json"
63
71
 
64
72
  let body: FormData | URLSearchParams | string | undefined = undefined
package/dist/cli.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var main$1 = require('./main-Dkcn3PTN.cjs');
3
+ var main$1 = require('./main-oWgfChRG.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-byN_-fkF.js';
2
+ import { a as apigen, g as getCliConfig } from './main-BTcttgNf.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-byN_-fkF.mjs';
1
+ import { a as apigen, g as getCliConfig } from './main-BTcttgNf.mjs';
2
2
  import 'fs/promises';
3
3
  import 'path';
4
4
  import 'url';
@@ -4,13 +4,13 @@ import { fileURLToPath } from 'url';
4
4
  import { cli } from 'cleye';
5
5
  import redocly from '@redocly/openapi-core';
6
6
  import { filterEmpty, filterNullable } from 'array-utils-ts';
7
- import { get, uniq, upperFirst, isArray, isObject, sortBy, lowerFirst, uniqBy } from 'lodash-es';
7
+ import { get, uniq, upperFirst, isArray, isBoolean, isObject, sortBy, 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 = "0.2.0";
13
+ var version = "1.0.0";
14
14
 
15
15
  const initCtx = (config) => {
16
16
  return {
@@ -194,6 +194,16 @@ const makeInlineEnum = (s) => {
194
194
  console.warn(`enum with unknown type "${s.type}" in`, s);
195
195
  return void 0;
196
196
  };
197
+ const makeObject = (ctx, s) => {
198
+ if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
199
+ if (s.additionalProperties && !isBoolean(s.additionalProperties)) {
200
+ return f$2.createTypeReferenceNode("Record", [
201
+ f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
202
+ makeType(ctx, s.additionalProperties)
203
+ ]);
204
+ }
205
+ return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
206
+ };
197
207
  const makeType = (ctx, s) => {
198
208
  const mk = makeType.bind(null, ctx);
199
209
  if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
@@ -235,7 +245,7 @@ const makeType = (ctx, s) => {
235
245
  return mk({ oneOf: types });
236
246
  }
237
247
  let t;
238
- if (s.type === "object") t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
248
+ if (s.type === "object") t = makeObject(ctx, s);
239
249
  else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
240
250
  else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
241
251
  else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
@@ -4,13 +4,13 @@ import { fileURLToPath } from 'url';
4
4
  import { cli } from 'cleye';
5
5
  import redocly from '@redocly/openapi-core';
6
6
  import { filterEmpty, filterNullable } from 'array-utils-ts';
7
- import { get, uniq, upperFirst, isArray, isObject, sortBy, lowerFirst, uniqBy } from 'lodash-es';
7
+ import { get, uniq, upperFirst, isArray, isBoolean, isObject, sortBy, 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 = "0.2.0";
13
+ var version = "1.0.0";
14
14
 
15
15
  const initCtx = (config) => {
16
16
  return {
@@ -194,6 +194,16 @@ const makeInlineEnum = (s) => {
194
194
  console.warn(`enum with unknown type "${s.type}" in`, s);
195
195
  return void 0;
196
196
  };
197
+ const makeObject = (ctx, s) => {
198
+ if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
199
+ if (s.additionalProperties && !isBoolean(s.additionalProperties)) {
200
+ return f$2.createTypeReferenceNode("Record", [
201
+ f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
202
+ makeType(ctx, s.additionalProperties)
203
+ ]);
204
+ }
205
+ return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
206
+ };
197
207
  const makeType = (ctx, s) => {
198
208
  const mk = makeType.bind(null, ctx);
199
209
  if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
@@ -235,7 +245,7 @@ const makeType = (ctx, s) => {
235
245
  return mk({ oneOf: types });
236
246
  }
237
247
  let t;
238
- if (s.type === "object") t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
248
+ if (s.type === "object") t = makeObject(ctx, s);
239
249
  else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
240
250
  else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
241
251
  else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
@@ -13,7 +13,7 @@ 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 = "0.2.0";
16
+ var version = "1.0.0";
17
17
 
18
18
  const initCtx = (config) => {
19
19
  return {
@@ -197,6 +197,16 @@ const makeInlineEnum = (s) => {
197
197
  console.warn(`enum with unknown type "${s.type}" in`, s);
198
198
  return void 0;
199
199
  };
200
+ const makeObject = (ctx, s) => {
201
+ if (s.type !== "object") throw new Error(`makeObject: not an object ${JSON.stringify(s)}`);
202
+ if (s.additionalProperties && !lodashEs.isBoolean(s.additionalProperties)) {
203
+ return f$2.createTypeReferenceNode("Record", [
204
+ f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
205
+ makeType(ctx, s.additionalProperties)
206
+ ]);
207
+ }
208
+ return f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
209
+ };
200
210
  const makeType = (ctx, s) => {
201
211
  const mk = makeType.bind(null, ctx);
202
212
  if (s === void 0) return f$2.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword);
@@ -238,7 +248,7 @@ const makeType = (ctx, s) => {
238
248
  return mk({ oneOf: types });
239
249
  }
240
250
  let t;
241
- if (s.type === "object") t = f$2.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword);
251
+ if (s.type === "object") t = makeObject(ctx, s);
242
252
  else if (s.type === "boolean") t = f$2.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
243
253
  else if (s.type === "number") t = f$2.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
244
254
  else if (s.type === "string") t = f$2.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
@@ -505,7 +515,7 @@ const apigen = async (config) => {
505
515
  const doc = await loadSchema(config.source);
506
516
  const ctx = initCtx({ ...config, doc });
507
517
  const { modules, types } = await generateAst(ctx);
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");
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-oWgfChRG.cjs', document.baseURI).href)))), "_template.ts");
509
519
  const file = await fs.readFile(filepath, "utf-8");
510
520
  let code = [
511
521
  `// Auto-generated by https://github.com/vladkens/apigen-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-Dkcn3PTN.cjs');
6
+ var main = require('./main-oWgfChRG.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-byN_-fkF.js';
4
+ export { a as apigen } from './main-BTcttgNf.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-byN_-fkF.mjs';
4
+ export { a as apigen } from './main-BTcttgNf.mjs';
5
5
  import 'cleye';
6
6
  import '@redocly/openapi-core';
7
7
  import 'array-utils-ts';
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "apigen-ts",
4
- "version": "0.2.0",
4
+ "version": "1.0.0",
5
5
  "license": "MIT",
6
6
  "author": "vladkens <v.pronsky@gmail.com>",
7
7
  "repository": "vladkens/apigen-ts",
8
- "description": "OpenAPI TypeScript client generator",
8
+ "description": "Simple typed OpenAPI client generator",
9
9
  "keywords": [
10
10
  "openapi",
11
11
  "swagger",
@@ -22,24 +22,24 @@
22
22
  "ci": "tsc --noEmit && yarn test-cov && yarn build"
23
23
  },
24
24
  "dependencies": {
25
- "@redocly/openapi-core": "^1.22.1",
26
- "@types/lodash-es": "^4.17.12",
27
- "@types/swagger2openapi": "^7.0.4",
28
- "array-utils-ts": "^0.1.2",
29
- "cleye": "^1.3.2",
30
- "lodash-es": "^4.17.21",
31
- "swagger2openapi": "^7.0.8"
25
+ "@redocly/openapi-core": "1.25.5",
26
+ "@types/lodash-es": "4.17.12",
27
+ "@types/swagger2openapi": "7.0.4",
28
+ "array-utils-ts": "0.1.2",
29
+ "cleye": "1.3.2",
30
+ "lodash-es": "4.17.21",
31
+ "swagger2openapi": "7.0.8"
32
32
  },
33
33
  "devDependencies": {
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
- "tsm": "^2.3.0",
41
- "typescript": "^5.5.4",
42
- "uvu": "^0.5.6"
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",
39
+ "prettier-plugin-organize-imports": "4.1.0",
40
+ "tsm": "2.3.0",
41
+ "typescript": "5.6.3",
42
+ "uvu": "0.5.6"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "prettier": "^3.0.0",
package/readme.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  <div align="center">
14
14
  <img src="./logo.svg" alt="apigen-ts logo" height="80" />
15
- <div>TypeScript client generator from OpenAPI schema</div>
15
+ <div>Simple typed OpenAPI client generator</div>
16
16
  </div>
17
17
 
18
18
  ## Features
@@ -168,6 +168,33 @@ await apigen({
168
168
 
169
169
  Then run with: `node apigen.mjs`
170
170
 
171
+ ## Usage with different backend frameworks
172
+
173
+ ### FastAPI
174
+
175
+ By default `apigen-ts` generates noisy method names when used with FastAPI. This can be fixed by [custom resolving](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#using-the-path-operation-function-name-as-the-operationid) for operations ids.
176
+
177
+ ```py
178
+ from fastapi import FastAPI
179
+ from fastapi.routing import APIRoute
180
+
181
+ app = FastAPI()
182
+
183
+ # add your routes here
184
+
185
+ def update_operation_ids(app: FastAPI) -> None:
186
+ for route in app.routes:
187
+ if isinstance(route, APIRoute):
188
+ ns = route.tags[0] if route.tags else "general"
189
+ route.operation_id = f"{ns}_{route.name}".lower()
190
+
191
+
192
+ # this function should be after all routes added
193
+ update_operation_ids(app)
194
+ ```
195
+
196
+ _Note: If you want FastAPI to be added as preset, open PR please._
197
+
171
198
  ## Compare
172
199
 
173
200
  - [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen) ([npm](https://www.npmjs.com/package/openapi-typescript-codegen)): no single file mode [#1263](https://github.com/ferdikoomen/openapi-typescript-codegen/issues/1263#issuecomment-1502890838)