@ps-aux/api-client-gen 0.1.0-rc1 → 0.1.2-rc1

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/bin.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/bin/env node
2
- import { g as generateApiClient } from './generateApiClient-PeRLjZk9.js';
2
+ import { g as generateApiClient } from './generateApiClient-QyDbHHKj.js';
3
3
  import { cosmiconfigSync } from 'cosmiconfig';
4
4
  import 'path';
5
5
  import 'fs';
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/env node
2
2
  'use strict';
3
3
 
4
- var generateApiClient = require('./generateApiClient-d_X--WFl.js');
4
+ var generateApiClient = require('./generateApiClient-C11lxAxn.js');
5
5
  var cosmiconfig = require('cosmiconfig');
6
6
  require('path');
7
7
  require('fs');
@@ -0,0 +1,201 @@
1
+ 'use strict';
2
+
3
+ var Path = require('path');
4
+ var fs$1 = require('fs');
5
+ var axios = require('axios');
6
+ var fs = require('fs/promises');
7
+ var swaggerTypescriptApi = require('swagger-typescript-api');
8
+ var tsToZod = require('ts-to-zod');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs$1);
28
+
29
+ const downloadSpec = async (path, url) => {
30
+ const res = await axios({
31
+ url,
32
+ method: 'GET',
33
+ responseType: 'arraybuffer',
34
+ }).catch(err => {
35
+ throw err.toString();
36
+ });
37
+ const content = res.data.toString();
38
+ await fs.writeFile(path, format(content));
39
+ };
40
+ const format = (content) => JSON.stringify(JSON.parse(content), null, 4);
41
+
42
+ const generateOpenApiModel = async ({ input, isNode, responseWrapper, name, outputDir, ignoreOperationsWithTags }, log) => {
43
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
44
+ const specialMapping = isNode
45
+ ? {
46
+ File: '{file: Buffer | stream.Readable, name: string}'
47
+ }
48
+ : {};
49
+ const fileName = 'index';
50
+ const dstFile = Path.join(outputDir, `${fileName}.ts`);
51
+ const ignoreTags = ignoreOperationsWithTags && new Set(ignoreOperationsWithTags);
52
+ await swaggerTypescriptApi.generateApi({
53
+ name: 'index',
54
+ output: outputDir,
55
+ input: input,
56
+ // modular: true,
57
+ httpClientType: 'axios',
58
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
59
+ defaultResponseAsSuccess: true,
60
+ sortTypes: true,
61
+ // generateRouteTypes: true,
62
+ singleHttpClient: true,
63
+ // extractRequestBody: true,
64
+ cleanOutput: true,
65
+ moduleNameFirstTag: true,
66
+ apiClassName: capitalize(name) + 'Api',
67
+ hooks: {
68
+ onCreateRoute: (routeData) => {
69
+ if (ignoreTags) {
70
+ const ignore = routeData.raw.tags?.find(t => ignoreTags.has(t));
71
+ if (ignore)
72
+ return false;
73
+ }
74
+ return routeData;
75
+ }
76
+ },
77
+ // @ts-ignore
78
+ codeGenConstructs: (struct) => ({
79
+ // @ts-ignore
80
+ Keyword: specialMapping
81
+ })
82
+ // extractRequestParams: true,
83
+ });
84
+ const addImports = [];
85
+ const replaces = [];
86
+ if (isNode) {
87
+ addImports.push('import stream from \'node:stream\'');
88
+ }
89
+ if (responseWrapper) {
90
+ log(`Will use response wrapper '${JSON.stringify(responseWrapper)}'`);
91
+ if (responseWrapper.import)
92
+ addImports.push(responseWrapper.import);
93
+ replaces.push(['Promise', responseWrapper.symbol]);
94
+ }
95
+ // Remove unnecessary generics
96
+ // replaces.push(['<SecurityDataType extends unknown>', ''])
97
+ // replaces.push(['<SecurityDataType>', ''])
98
+ // replaces.push(['request: <Data, A = any>', 'request: <Data>'])
99
+ log(`Will modify the outputs ${JSON.stringify({
100
+ addImports,
101
+ replaces
102
+ })}`);
103
+ await modifyOutput(dstFile, {
104
+ addImports,
105
+ replaces
106
+ });
107
+ return dstFile;
108
+ };
109
+ const modifyOutput = async (path, cmd) => {
110
+ let content = await fs.readFile(path).then((r) => r.toString());
111
+ if (cmd.addImports.length) {
112
+ content = cmd.addImports.join('\n') + '\n' + content;
113
+ }
114
+ cmd.replaces.forEach(([from, to]) => {
115
+ content = content.replaceAll(from, to);
116
+ });
117
+ await fs.writeFile(path, content);
118
+ };
119
+ const getThisScriptDirname = () => {
120
+ // Might be problem in ESM mode
121
+ return __dirname;
122
+ };
123
+ const capitalize = (str) => {
124
+ return str[0].toUpperCase() + str.substring(1);
125
+ };
126
+
127
+ // TODO needed?
128
+ const removeGenericTypes = (code) => {
129
+ const regex = /export (interface|enum|type) [^<]* \{\n(.*\n)*?}/mg;
130
+ const matches = code.matchAll(regex);
131
+ const types = Array.from(matches).map(m => m[0]);
132
+ return types.join('\n');
133
+ };
134
+ const generateSchemas = (inputFile, outputFile, opts = {}) => {
135
+ const code = fs$1.readFileSync(inputFile).toString();
136
+ const { getZodSchemasFile } = tsToZod.generate({
137
+ sourceText: removeGenericTypes(code),
138
+ // inputOutputMappings: {
139
+ //
140
+ // },
141
+ customJSDocFormatTypes: {
142
+ // Custom mapping
143
+ // 'date-time': 'blablaj' - regex
144
+ }
145
+ });
146
+ const fileName = Path.basename(inputFile);
147
+ let f = getZodSchemasFile(`./${fileName.replace('.ts', '')}`);
148
+ // Add import from the api model - TODO find a way how to define on generate
149
+ f = f.replaceAll('"./index";', '"./client"');
150
+ // Backend sends nulls not undefined - should be properly set in the OpenAPI TS types generation!
151
+ f = f.replaceAll('.optional()', '.nullable()');
152
+ if (opts.localDateTimes) {
153
+ f = f.replaceAll('z.string().datetime()', 'z.string().datetime({local: true})');
154
+ }
155
+ fs$1.writeFileSync(outputFile, f);
156
+ };
157
+
158
+ const generateApiClient = async ({ dstDir, apiName, env, srcSpec, responseWrapper, ignoreOperationsWithTags, zodSchemas }, log = console.log) => {
159
+ if (!srcSpec)
160
+ throw new Error(`Url or path ('srcSpec' not specified`);
161
+ if (!apiName)
162
+ throw new Error('apiName not specified');
163
+ if (!dstDir)
164
+ throw new Error('dstDir not specified');
165
+ if (!env)
166
+ throw new Error('env not specified');
167
+ const dir = Path.resolve(dstDir, apiName);
168
+ if (!fs__namespace.existsSync(dir)) {
169
+ log(`Creating dir ${dir}`);
170
+ fs__namespace.mkdirSync(dir, {
171
+ recursive: true
172
+ });
173
+ }
174
+ const clientDir = Path.resolve(dir, 'client');
175
+ const specPath = await getSpecPath(srcSpec, dir, log);
176
+ const clientFile = await generateOpenApiModel({
177
+ name: apiName,
178
+ input: specPath,
179
+ outputDir: clientDir,
180
+ ignoreOperationsWithTags,
181
+ isNode: env === 'node',
182
+ responseWrapper
183
+ }, log);
184
+ if (zodSchemas?.enabled === false)
185
+ return;
186
+ log('Generating Zod schemas');
187
+ generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'), zodSchemas);
188
+ };
189
+ const getSpecPath = async (urlOrPath, dir, log) => {
190
+ if (!urlOrPath.startsWith('http')) {
191
+ if (!fs__namespace.existsSync(urlOrPath))
192
+ throw new Error(`Spec file ${urlOrPath} does not exists`);
193
+ return urlOrPath;
194
+ }
195
+ const specPath = Path.resolve(dir, `spec.json`);
196
+ log(`Will download the API spec from ${urlOrPath} to ${specPath}`);
197
+ await downloadSpec(specPath, urlOrPath);
198
+ return specPath;
199
+ };
200
+
201
+ exports.generateApiClient = generateApiClient;
@@ -0,0 +1,181 @@
1
+ import Path from 'path';
2
+ import * as fs from 'fs';
3
+ import fs__default from 'fs';
4
+ import axios from 'axios';
5
+ import fs$1 from 'fs/promises';
6
+ import { generateApi } from 'swagger-typescript-api';
7
+ import { generate } from 'ts-to-zod';
8
+
9
+ const downloadSpec = async (path, url) => {
10
+ const res = await axios({
11
+ url,
12
+ method: 'GET',
13
+ responseType: 'arraybuffer',
14
+ }).catch(err => {
15
+ throw err.toString();
16
+ });
17
+ const content = res.data.toString();
18
+ await fs$1.writeFile(path, format(content));
19
+ };
20
+ const format = (content) => JSON.stringify(JSON.parse(content), null, 4);
21
+
22
+ const generateOpenApiModel = async ({ input, isNode, responseWrapper, name, outputDir, ignoreOperationsWithTags }, log) => {
23
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
24
+ const specialMapping = isNode
25
+ ? {
26
+ File: '{file: Buffer | stream.Readable, name: string}'
27
+ }
28
+ : {};
29
+ const fileName = 'index';
30
+ const dstFile = Path.join(outputDir, `${fileName}.ts`);
31
+ const ignoreTags = ignoreOperationsWithTags && new Set(ignoreOperationsWithTags);
32
+ await generateApi({
33
+ name: 'index',
34
+ output: outputDir,
35
+ input: input,
36
+ // modular: true,
37
+ httpClientType: 'axios',
38
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
39
+ defaultResponseAsSuccess: true,
40
+ sortTypes: true,
41
+ // generateRouteTypes: true,
42
+ singleHttpClient: true,
43
+ // extractRequestBody: true,
44
+ cleanOutput: true,
45
+ moduleNameFirstTag: true,
46
+ apiClassName: capitalize(name) + 'Api',
47
+ hooks: {
48
+ onCreateRoute: (routeData) => {
49
+ if (ignoreTags) {
50
+ const ignore = routeData.raw.tags?.find(t => ignoreTags.has(t));
51
+ if (ignore)
52
+ return false;
53
+ }
54
+ return routeData;
55
+ }
56
+ },
57
+ // @ts-ignore
58
+ codeGenConstructs: (struct) => ({
59
+ // @ts-ignore
60
+ Keyword: specialMapping
61
+ })
62
+ // extractRequestParams: true,
63
+ });
64
+ const addImports = [];
65
+ const replaces = [];
66
+ if (isNode) {
67
+ addImports.push('import stream from \'node:stream\'');
68
+ }
69
+ if (responseWrapper) {
70
+ log(`Will use response wrapper '${JSON.stringify(responseWrapper)}'`);
71
+ if (responseWrapper.import)
72
+ addImports.push(responseWrapper.import);
73
+ replaces.push(['Promise', responseWrapper.symbol]);
74
+ }
75
+ // Remove unnecessary generics
76
+ // replaces.push(['<SecurityDataType extends unknown>', ''])
77
+ // replaces.push(['<SecurityDataType>', ''])
78
+ // replaces.push(['request: <Data, A = any>', 'request: <Data>'])
79
+ log(`Will modify the outputs ${JSON.stringify({
80
+ addImports,
81
+ replaces
82
+ })}`);
83
+ await modifyOutput(dstFile, {
84
+ addImports,
85
+ replaces
86
+ });
87
+ return dstFile;
88
+ };
89
+ const modifyOutput = async (path, cmd) => {
90
+ let content = await fs$1.readFile(path).then((r) => r.toString());
91
+ if (cmd.addImports.length) {
92
+ content = cmd.addImports.join('\n') + '\n' + content;
93
+ }
94
+ cmd.replaces.forEach(([from, to]) => {
95
+ content = content.replaceAll(from, to);
96
+ });
97
+ await fs$1.writeFile(path, content);
98
+ };
99
+ const getThisScriptDirname = () => {
100
+ // Might be problem in ESM mode
101
+ return __dirname;
102
+ };
103
+ const capitalize = (str) => {
104
+ return str[0].toUpperCase() + str.substring(1);
105
+ };
106
+
107
+ // TODO needed?
108
+ const removeGenericTypes = (code) => {
109
+ const regex = /export (interface|enum|type) [^<]* \{\n(.*\n)*?}/mg;
110
+ const matches = code.matchAll(regex);
111
+ const types = Array.from(matches).map(m => m[0]);
112
+ return types.join('\n');
113
+ };
114
+ const generateSchemas = (inputFile, outputFile, opts = {}) => {
115
+ const code = fs__default.readFileSync(inputFile).toString();
116
+ const { getZodSchemasFile } = generate({
117
+ sourceText: removeGenericTypes(code),
118
+ // inputOutputMappings: {
119
+ //
120
+ // },
121
+ customJSDocFormatTypes: {
122
+ // Custom mapping
123
+ // 'date-time': 'blablaj' - regex
124
+ }
125
+ });
126
+ const fileName = Path.basename(inputFile);
127
+ let f = getZodSchemasFile(`./${fileName.replace('.ts', '')}`);
128
+ // Add import from the api model - TODO find a way how to define on generate
129
+ f = f.replaceAll('"./index";', '"./client"');
130
+ // Backend sends nulls not undefined - should be properly set in the OpenAPI TS types generation!
131
+ f = f.replaceAll('.optional()', '.nullable()');
132
+ if (opts.localDateTimes) {
133
+ f = f.replaceAll('z.string().datetime()', 'z.string().datetime({local: true})');
134
+ }
135
+ fs__default.writeFileSync(outputFile, f);
136
+ };
137
+
138
+ const generateApiClient = async ({ dstDir, apiName, env, srcSpec, responseWrapper, ignoreOperationsWithTags, zodSchemas }, log = console.log) => {
139
+ if (!srcSpec)
140
+ throw new Error(`Url or path ('srcSpec' not specified`);
141
+ if (!apiName)
142
+ throw new Error('apiName not specified');
143
+ if (!dstDir)
144
+ throw new Error('dstDir not specified');
145
+ if (!env)
146
+ throw new Error('env not specified');
147
+ const dir = Path.resolve(dstDir, apiName);
148
+ if (!fs.existsSync(dir)) {
149
+ log(`Creating dir ${dir}`);
150
+ fs.mkdirSync(dir, {
151
+ recursive: true
152
+ });
153
+ }
154
+ const clientDir = Path.resolve(dir, 'client');
155
+ const specPath = await getSpecPath(srcSpec, dir, log);
156
+ const clientFile = await generateOpenApiModel({
157
+ name: apiName,
158
+ input: specPath,
159
+ outputDir: clientDir,
160
+ ignoreOperationsWithTags,
161
+ isNode: env === 'node',
162
+ responseWrapper
163
+ }, log);
164
+ if (zodSchemas?.enabled === false)
165
+ return;
166
+ log('Generating Zod schemas');
167
+ generateSchemas(Path.resolve(dir, clientFile), Path.resolve(dir, './zod.ts'), zodSchemas);
168
+ };
169
+ const getSpecPath = async (urlOrPath, dir, log) => {
170
+ if (!urlOrPath.startsWith('http')) {
171
+ if (!fs.existsSync(urlOrPath))
172
+ throw new Error(`Spec file ${urlOrPath} does not exists`);
173
+ return urlOrPath;
174
+ }
175
+ const specPath = Path.resolve(dir, `spec.json`);
176
+ log(`Will download the API spec from ${urlOrPath} to ${specPath}`);
177
+ await downloadSpec(specPath, urlOrPath);
178
+ return specPath;
179
+ };
180
+
181
+ export { generateApiClient as g };
@@ -8,8 +8,9 @@ export type Params = {
8
8
  symbol: string;
9
9
  import?: string;
10
10
  };
11
+ ignoreOperationsWithTags?: string[];
11
12
  zodSchemas?: GenerateSchemasOptions & {
12
13
  enabled?: boolean;
13
14
  };
14
15
  };
15
- export declare const generateApiClient: ({ dstDir, apiName, env, srcSpec, responseWrapper, zodSchemas }: Params, log?: (msg: string) => void) => Promise<void>;
16
+ export declare const generateApiClient: ({ dstDir, apiName, env, srcSpec, responseWrapper, ignoreOperationsWithTags, zodSchemas }: Params, log?: (msg: string) => void) => Promise<void>;
@@ -3,9 +3,10 @@ export type GenerateOpenApiModelCmd = {
3
3
  input: string;
4
4
  outputDir: string;
5
5
  isNode?: boolean;
6
+ ignoreOperationsWithTags?: string[];
6
7
  responseWrapper?: {
7
8
  symbol: string;
8
9
  import?: string;
9
10
  };
10
11
  };
11
- export declare const generateOpenApiModel: ({ input, isNode, responseWrapper, name, outputDir }: GenerateOpenApiModelCmd, log: (msg: string) => void) => Promise<string>;
12
+ export declare const generateOpenApiModel: ({ input, isNode, responseWrapper, name, outputDir, ignoreOperationsWithTags }: GenerateOpenApiModelCmd, log: (msg: string) => void) => Promise<string>;
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- export { g as generateApiClient } from './generateApiClient-PeRLjZk9.js';
1
+ export { g as generateApiClient } from './generateApiClient-QyDbHHKj.js';
2
2
  import 'path';
3
3
  import 'fs';
4
4
  import 'axios';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var generateApiClient = require('./generateApiClient-d_X--WFl.js');
3
+ var generateApiClient = require('./generateApiClient-C11lxAxn.js');
4
4
  require('path');
5
5
  require('fs');
6
6
  require('axios');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ps-aux/api-client-gen",
3
- "version": "0.1.0-rc1",
3
+ "version": "0.1.2-rc1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.esm.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,8 +12,7 @@
12
12
  "dev": "rollup -c --watch",
13
13
  "pub": "npm test && npm run build && npm publish",
14
14
  "test": "vitest",
15
- "tc": "tsc",
16
- "eh": "foo"
15
+ "tc": "tsc"
17
16
  },
18
17
  "author": "",
19
18
  "license": "ISC",
@@ -12,6 +12,7 @@ export type Request = {
12
12
  query?: any
13
13
  body?: any
14
14
  type?: string
15
+ secure?: boolean
15
16
  }
16
17
 
17
18
  export type HttpClient<RequestParams> = {