@ps-aux/api-client-gen 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export declare const downloadSpec: (path: string, url: string) => Promise<unknown>;
package/dist/foo.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const foo = "123";
@@ -0,0 +1,7 @@
1
+ export type Params = {
2
+ srcSpec: string;
3
+ dstDir: string;
4
+ apiName: string;
5
+ env: 'browser' | 'node';
6
+ };
7
+ export declare const generateModel: ({ dstDir, apiName, env, srcSpec, }: Params, log?: (msg: string) => void) => Promise<void>;
@@ -0,0 +1 @@
1
+ export declare const generateOpenApiModel: (name: string, input: string, outputDir: string, log: (msg: string) => void, isNode?: boolean) => Promise<void>;
package/dist/go ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/env bash
2
+
3
+
4
+ echo "gogo"
package/dist/go.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/env node
2
+ export {};
package/dist/go.js ADDED
@@ -0,0 +1,141 @@
1
+ #!/bin/env node
2
+ 'use strict';
3
+
4
+ var Path = require('path');
5
+ var fs = require('fs');
6
+ var axios = require('axios');
7
+ var swaggerTypescriptApi = require('swagger-typescript-api');
8
+ var fs$1 = require('fs/promises');
9
+ var cosmiconfig = require('cosmiconfig');
10
+
11
+ /******************************************************************************
12
+ Copyright (c) Microsoft Corporation.
13
+
14
+ Permission to use, copy, modify, and/or distribute this software for any
15
+ purpose with or without fee is hereby granted.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
20
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
22
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23
+ PERFORMANCE OF THIS SOFTWARE.
24
+ ***************************************************************************** */
25
+ /* global Reflect, Promise, SuppressedError, Symbol */
26
+
27
+
28
+ function __awaiter(thisArg, _arguments, P, generator) {
29
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
30
+ return new (P || (P = Promise))(function (resolve, reject) {
31
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
32
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
33
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
34
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
35
+ });
36
+ }
37
+
38
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
39
+ var e = new Error(message);
40
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
41
+ };
42
+
43
+ const downloadSpec = (path, url) => __awaiter(void 0, void 0, void 0, function* () {
44
+ const res = yield axios({
45
+ url,
46
+ method: 'GET',
47
+ responseType: 'stream'
48
+ }).catch(err => {
49
+ throw err.toString();
50
+ });
51
+ const writer = fs.createWriteStream(path);
52
+ // TODO Format to readable JSON
53
+ yield res.data.pipe(writer);
54
+ return new Promise((resolve, reject) => {
55
+ writer.on('finish', resolve);
56
+ writer.on('error', err => {
57
+ reject(err);
58
+ });
59
+ });
60
+ });
61
+
62
+ const generateOpenApiModel = (name, input, outputDir, log, isNode = false) => __awaiter(void 0, void 0, void 0, function* () {
63
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
64
+ const specialMapping = isNode
65
+ ? {
66
+ File: '{file: Buffer | stream.Readable, name: string}',
67
+ }
68
+ : {};
69
+ const dstFile = Path.join(outputDir, name + '.ts');
70
+ yield swaggerTypescriptApi.generateApi({
71
+ name,
72
+ output: outputDir,
73
+ input: input,
74
+ // modular: true,
75
+ httpClientType: 'axios',
76
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
77
+ defaultResponseAsSuccess: true,
78
+ // generateRouteTypes: true,
79
+ singleHttpClient: true,
80
+ // extractRequestBody: true,
81
+ cleanOutput: true,
82
+ moduleNameFirstTag: true,
83
+ codeGenConstructs: (struct) => ({
84
+ // @ts-ignore
85
+ Keyword: specialMapping,
86
+ }),
87
+ // extractRequestParams: true,
88
+ });
89
+ if (isNode) {
90
+ let content = yield fs$1.readFile(dstFile).then((r) => r.toString());
91
+ // Naive solution
92
+ content = 'import stream from \'node:stream\'\n' + content;
93
+ yield fs$1.writeFile(dstFile, content);
94
+ }
95
+ });
96
+ const getThisScriptDirname = () => {
97
+ // Might be problem in ESM mode
98
+ return __dirname;
99
+ };
100
+
101
+ const generateModel = ({ dstDir, apiName, env, srcSpec, }, log = console.log) => __awaiter(void 0, void 0, void 0, function* () {
102
+ if (!srcSpec)
103
+ throw new Error(`Url or path ('srcSpec' not specified`);
104
+ if (!apiName)
105
+ throw new Error('apiName not specified');
106
+ if (!dstDir)
107
+ throw new Error('dstDir not specified');
108
+ if (!env)
109
+ throw new Error('env not specified');
110
+ const dir = Path.resolve(dstDir, apiName);
111
+ if (!fs.existsSync(dir)) {
112
+ log(`Creating dir ${dir}`);
113
+ fs.mkdirSync(dir, {
114
+ recursive: true,
115
+ });
116
+ }
117
+ const clientDir = Path.resolve(dir, 'client');
118
+ const specPath = yield getSpecPath(srcSpec, dir);
119
+ yield generateOpenApiModel(apiName + '-api', specPath, clientDir, log, env === 'node');
120
+ // TODO does not seem to currently generate all schemas
121
+ // generateSchemas(
122
+ // Path.resolve(modelDir, 'data-contracts.ts'),
123
+ // Path.resolve(dir, 'schemas.ts')
124
+ // )
125
+ });
126
+ const getSpecPath = (urlOrPath, dir) => __awaiter(void 0, void 0, void 0, function* () {
127
+ if (!urlOrPath.startsWith('http')) {
128
+ if (!fs.existsSync(urlOrPath))
129
+ throw new Error(`Spec file ${urlOrPath} does not exists`);
130
+ return urlOrPath;
131
+ }
132
+ const specPath = Path.resolve(dir, `spec.json`);
133
+ yield downloadSpec(specPath, urlOrPath);
134
+ });
135
+
136
+ const conf = cosmiconfig.cosmiconfigSync('api-client-gen')
137
+ .search();
138
+ // TODO validate config
139
+ if (!conf)
140
+ throw new Error(`No config provided`);
141
+ generateModel(conf.config);
@@ -0,0 +1,5 @@
1
+ export { generateModel } from "./gen-model";
2
+ declare const _default: {
3
+ generateModel: ({ dstDir, apiName, env, srcSpec, }: import("./gen-model").Params, log?: (msg: string) => void) => Promise<void>;
4
+ };
5
+ export default _default;
@@ -0,0 +1,135 @@
1
+ import Path from 'path';
2
+ import fs from 'fs';
3
+ import axios from 'axios';
4
+ import { generateApi } from 'swagger-typescript-api';
5
+ import fs$1 from 'fs/promises';
6
+
7
+ /******************************************************************************
8
+ Copyright (c) Microsoft Corporation.
9
+
10
+ Permission to use, copy, modify, and/or distribute this software for any
11
+ purpose with or without fee is hereby granted.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
14
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
+ PERFORMANCE OF THIS SOFTWARE.
20
+ ***************************************************************************** */
21
+ /* global Reflect, Promise, SuppressedError, Symbol */
22
+
23
+
24
+ function __awaiter(thisArg, _arguments, P, generator) {
25
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
26
+ return new (P || (P = Promise))(function (resolve, reject) {
27
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
30
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
31
+ });
32
+ }
33
+
34
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
35
+ var e = new Error(message);
36
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
37
+ };
38
+
39
+ const downloadSpec = (path, url) => __awaiter(void 0, void 0, void 0, function* () {
40
+ const res = yield axios({
41
+ url,
42
+ method: 'GET',
43
+ responseType: 'stream'
44
+ }).catch(err => {
45
+ throw err.toString();
46
+ });
47
+ const writer = fs.createWriteStream(path);
48
+ // TODO Format to readable JSON
49
+ yield res.data.pipe(writer);
50
+ return new Promise((resolve, reject) => {
51
+ writer.on('finish', resolve);
52
+ writer.on('error', err => {
53
+ reject(err);
54
+ });
55
+ });
56
+ });
57
+
58
+ const generateOpenApiModel = (name, input, outputDir, log, isNode = false) => __awaiter(void 0, void 0, void 0, function* () {
59
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
60
+ const specialMapping = isNode
61
+ ? {
62
+ File: '{file: Buffer | stream.Readable, name: string}',
63
+ }
64
+ : {};
65
+ const dstFile = Path.join(outputDir, name + '.ts');
66
+ yield generateApi({
67
+ name,
68
+ output: outputDir,
69
+ input: input,
70
+ // modular: true,
71
+ httpClientType: 'axios',
72
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
73
+ defaultResponseAsSuccess: true,
74
+ // generateRouteTypes: true,
75
+ singleHttpClient: true,
76
+ // extractRequestBody: true,
77
+ cleanOutput: true,
78
+ moduleNameFirstTag: true,
79
+ codeGenConstructs: (struct) => ({
80
+ // @ts-ignore
81
+ Keyword: specialMapping,
82
+ }),
83
+ // extractRequestParams: true,
84
+ });
85
+ if (isNode) {
86
+ let content = yield fs$1.readFile(dstFile).then((r) => r.toString());
87
+ // Naive solution
88
+ content = 'import stream from \'node:stream\'\n' + content;
89
+ yield fs$1.writeFile(dstFile, content);
90
+ }
91
+ });
92
+ const getThisScriptDirname = () => {
93
+ // Might be problem in ESM mode
94
+ return __dirname;
95
+ };
96
+
97
+ const generateModel = ({ dstDir, apiName, env, srcSpec, }, log = console.log) => __awaiter(void 0, void 0, void 0, function* () {
98
+ if (!srcSpec)
99
+ throw new Error(`Url or path ('srcSpec' not specified`);
100
+ if (!apiName)
101
+ throw new Error('apiName not specified');
102
+ if (!dstDir)
103
+ throw new Error('dstDir not specified');
104
+ if (!env)
105
+ throw new Error('env not specified');
106
+ const dir = Path.resolve(dstDir, apiName);
107
+ if (!fs.existsSync(dir)) {
108
+ log(`Creating dir ${dir}`);
109
+ fs.mkdirSync(dir, {
110
+ recursive: true,
111
+ });
112
+ }
113
+ const clientDir = Path.resolve(dir, 'client');
114
+ const specPath = yield getSpecPath(srcSpec, dir);
115
+ yield generateOpenApiModel(apiName + '-api', specPath, clientDir, log, env === 'node');
116
+ // TODO does not seem to currently generate all schemas
117
+ // generateSchemas(
118
+ // Path.resolve(modelDir, 'data-contracts.ts'),
119
+ // Path.resolve(dir, 'schemas.ts')
120
+ // )
121
+ });
122
+ const getSpecPath = (urlOrPath, dir) => __awaiter(void 0, void 0, void 0, function* () {
123
+ if (!urlOrPath.startsWith('http')) {
124
+ if (!fs.existsSync(urlOrPath))
125
+ throw new Error(`Spec file ${urlOrPath} does not exists`);
126
+ return urlOrPath;
127
+ }
128
+ const specPath = Path.resolve(dir, `spec.json`);
129
+ yield downloadSpec(specPath, urlOrPath);
130
+ });
131
+
132
+ // Bcs of mixing for ESM and CommonJS - todo fix somehow
133
+ var index = { generateModel };
134
+
135
+ export { index as default, generateModel };
package/dist/index.js ADDED
@@ -0,0 +1,140 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('path'), require('fs'), require('axios'), require('swagger-typescript-api'), require('fs/promises')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'path', 'fs', 'axios', 'swagger-typescript-api', 'fs/promises'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MyLibrary = {}, global.Path, global.fs, global.axios, global.swaggerTypescriptApi, global.fs$1));
5
+ })(this, (function (exports, Path, fs, axios, swaggerTypescriptApi, fs$1) { 'use strict';
6
+
7
+ /******************************************************************************
8
+ Copyright (c) Microsoft Corporation.
9
+
10
+ Permission to use, copy, modify, and/or distribute this software for any
11
+ purpose with or without fee is hereby granted.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
14
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
+ PERFORMANCE OF THIS SOFTWARE.
20
+ ***************************************************************************** */
21
+ /* global Reflect, Promise, SuppressedError, Symbol */
22
+
23
+
24
+ function __awaiter(thisArg, _arguments, P, generator) {
25
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
26
+ return new (P || (P = Promise))(function (resolve, reject) {
27
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
30
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
31
+ });
32
+ }
33
+
34
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
35
+ var e = new Error(message);
36
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
37
+ };
38
+
39
+ const downloadSpec = (path, url) => __awaiter(void 0, void 0, void 0, function* () {
40
+ const res = yield axios({
41
+ url,
42
+ method: 'GET',
43
+ responseType: 'stream'
44
+ }).catch(err => {
45
+ throw err.toString();
46
+ });
47
+ const writer = fs.createWriteStream(path);
48
+ // TODO Format to readable JSON
49
+ yield res.data.pipe(writer);
50
+ return new Promise((resolve, reject) => {
51
+ writer.on('finish', resolve);
52
+ writer.on('error', err => {
53
+ reject(err);
54
+ });
55
+ });
56
+ });
57
+
58
+ const generateOpenApiModel = (name, input, outputDir, log, isNode = false) => __awaiter(void 0, void 0, void 0, function* () {
59
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`);
60
+ const specialMapping = isNode
61
+ ? {
62
+ File: '{file: Buffer | stream.Readable, name: string}',
63
+ }
64
+ : {};
65
+ const dstFile = Path.join(outputDir, name + '.ts');
66
+ yield swaggerTypescriptApi.generateApi({
67
+ name,
68
+ output: outputDir,
69
+ input: input,
70
+ // modular: true,
71
+ httpClientType: 'axios',
72
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
73
+ defaultResponseAsSuccess: true,
74
+ // generateRouteTypes: true,
75
+ singleHttpClient: true,
76
+ // extractRequestBody: true,
77
+ cleanOutput: true,
78
+ moduleNameFirstTag: true,
79
+ codeGenConstructs: (struct) => ({
80
+ // @ts-ignore
81
+ Keyword: specialMapping,
82
+ }),
83
+ // extractRequestParams: true,
84
+ });
85
+ if (isNode) {
86
+ let content = yield fs$1.readFile(dstFile).then((r) => r.toString());
87
+ // Naive solution
88
+ content = 'import stream from \'node:stream\'\n' + content;
89
+ yield fs$1.writeFile(dstFile, content);
90
+ }
91
+ });
92
+ const getThisScriptDirname = () => {
93
+ // Might be problem in ESM mode
94
+ return __dirname;
95
+ };
96
+
97
+ const generateModel = ({ dstDir, apiName, env, srcSpec, }, log = console.log) => __awaiter(void 0, void 0, void 0, function* () {
98
+ if (!srcSpec)
99
+ throw new Error(`Url or path ('srcSpec' not specified`);
100
+ if (!apiName)
101
+ throw new Error('apiName not specified');
102
+ if (!dstDir)
103
+ throw new Error('dstDir not specified');
104
+ if (!env)
105
+ throw new Error('env not specified');
106
+ const dir = Path.resolve(dstDir, apiName);
107
+ if (!fs.existsSync(dir)) {
108
+ log(`Creating dir ${dir}`);
109
+ fs.mkdirSync(dir, {
110
+ recursive: true,
111
+ });
112
+ }
113
+ const clientDir = Path.resolve(dir, 'client');
114
+ const specPath = yield getSpecPath(srcSpec, dir);
115
+ yield generateOpenApiModel(apiName + '-api', specPath, clientDir, log, env === 'node');
116
+ // TODO does not seem to currently generate all schemas
117
+ // generateSchemas(
118
+ // Path.resolve(modelDir, 'data-contracts.ts'),
119
+ // Path.resolve(dir, 'schemas.ts')
120
+ // )
121
+ });
122
+ const getSpecPath = (urlOrPath, dir) => __awaiter(void 0, void 0, void 0, function* () {
123
+ if (!urlOrPath.startsWith('http')) {
124
+ if (!fs.existsSync(urlOrPath))
125
+ throw new Error(`Spec file ${urlOrPath} does not exists`);
126
+ return urlOrPath;
127
+ }
128
+ const specPath = Path.resolve(dir, `spec.json`);
129
+ yield downloadSpec(specPath, urlOrPath);
130
+ });
131
+
132
+ // Bcs of mixing for ESM and CommonJS - todo fix somehow
133
+ var index = { generateModel };
134
+
135
+ exports.default = index;
136
+ exports.generateModel = generateModel;
137
+
138
+ Object.defineProperty(exports, '__esModule', { value: true });
139
+
140
+ }));
package/jest.config.js ADDED
@@ -0,0 +1,17 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest/presets/default-esm', // or other ESM presets
4
+ moduleNameMapper: {
5
+ '^(\\.{1,2}/.*)\\.js$': '$1',
6
+ },
7
+ transform: {
8
+ // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
9
+ // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
10
+ '^.+\\.tsx?$': [
11
+ 'ts-jest',
12
+ {
13
+ useESM: true,
14
+ },
15
+ ],
16
+ },
17
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@ps-aux/api-client-gen",
3
+ "version": "0.0.1",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "bin": {
7
+ "generate-api-client": "dist/go.js"
8
+ },
9
+ "scripts": {
10
+ "build": "rollup -c",
11
+ "tc": "tsc",
12
+ "eh": "foo"
13
+ },
14
+ "author": "",
15
+ "license": "ISC",
16
+ "dependencies": {
17
+ "axios": "^1.5.0",
18
+ "cosmiconfig": "^8.3.6",
19
+ "swagger-typescript-api": "^13.0.3"
20
+ },
21
+ "devDependencies": {
22
+ "rollup": "^4.1.4",
23
+ "rollup-plugin-typescript2": "^0.36.0"
24
+ }
25
+ }
@@ -0,0 +1,26 @@
1
+ import typescript from 'rollup-plugin-typescript2'
2
+
3
+ export default [{
4
+ input: 'src/index.ts',
5
+ output: [
6
+ {
7
+ file: 'dist/index.js',
8
+ format: 'umd',
9
+ name: 'MyLibrary',
10
+ },
11
+ {
12
+ file: 'dist/index.esm.js',
13
+ format: 'es',
14
+ },
15
+ ],
16
+ plugins: [typescript()],
17
+ }, {
18
+ input: 'src/go.ts',
19
+ output: [
20
+ {
21
+ file: 'dist/go.js',
22
+ format: 'cjs',
23
+ }],
24
+
25
+ plugins: [typescript()],
26
+ }]
@@ -0,0 +1,24 @@
1
+ import axios from 'axios'
2
+ import fs from 'fs'
3
+
4
+ export const downloadSpec = async (path: string, url: string) => {
5
+ const res = await axios({
6
+ url,
7
+ method: 'GET',
8
+ responseType: 'stream'
9
+ }).catch(err => {
10
+ throw err.toString()
11
+ })
12
+
13
+ const writer = fs.createWriteStream(path)
14
+
15
+ // TODO Format to readable JSON
16
+ await res.data.pipe(writer)
17
+
18
+ return new Promise((resolve, reject) => {
19
+ writer.on('finish', resolve)
20
+ writer.on('error', err => {
21
+ reject(err)
22
+ })
23
+ })
24
+ }
@@ -0,0 +1,59 @@
1
+ import Path from 'path'
2
+ import fs from 'fs'
3
+ import { downloadSpec } from './downloadSpec'
4
+ import { generateOpenApiModel } from './generateOpenApiModel'
5
+
6
+ export type Params = {
7
+ srcSpec: string, // path or http(s) URL
8
+ dstDir: string
9
+ apiName: string
10
+ env: 'browser' | 'node'
11
+ }
12
+
13
+ export const generateModel = async ({
14
+ dstDir,
15
+ apiName,
16
+ env,
17
+ srcSpec,
18
+ }: Params, log: (msg: string) => void = console.log): Promise<void> => {
19
+ if (!srcSpec) throw new Error(`Url or path ('srcSpec' not specified`)
20
+ if (!apiName) throw new Error('apiName not specified')
21
+ if (!dstDir) throw new Error('dstDir not specified')
22
+ if (!env) throw new Error('env not specified')
23
+
24
+ const dir = Path.resolve(dstDir, apiName)
25
+ if (!fs.existsSync(dir)) {
26
+ log(`Creating dir ${dir}`)
27
+ fs.mkdirSync(dir, {
28
+ recursive: true,
29
+ })
30
+ }
31
+ const clientDir = Path.resolve(dir, 'client')
32
+
33
+ const specPath = await getSpecPath(srcSpec, dir)
34
+
35
+ await generateOpenApiModel(
36
+ apiName + '-api',
37
+ specPath,
38
+ clientDir,
39
+ log,
40
+ env === 'node',
41
+ )
42
+
43
+ // TODO does not seem to currently generate all schemas
44
+ // generateSchemas(
45
+ // Path.resolve(modelDir, 'data-contracts.ts'),
46
+ // Path.resolve(dir, 'schemas.ts')
47
+ // )
48
+ }
49
+
50
+ const getSpecPath = async (urlOrPath: string, dir: string): Promise<string> => {
51
+ if (!urlOrPath.startsWith('http')) {
52
+ if (!fs.existsSync(urlOrPath))
53
+ throw new Error(`Spec file ${urlOrPath} does not exists`)
54
+ return urlOrPath
55
+ }
56
+ const specPath = Path.resolve(dir, `spec.json`)
57
+
58
+ await downloadSpec(specPath, urlOrPath)
59
+ }
@@ -0,0 +1,54 @@
1
+ import { generateApi } from 'swagger-typescript-api'
2
+ import Path from 'path'
3
+ import fs from 'fs/promises'
4
+
5
+ export const generateOpenApiModel = async (
6
+ name: string,
7
+ input: string,
8
+ outputDir: string,
9
+ log: (msg: string) => void,
10
+ isNode = false,
11
+ ): Promise<void> => {
12
+
13
+ log(`Will generate API client name=${name} to ${outputDir}, node=${isNode}`)
14
+ const specialMapping = isNode
15
+ ? {
16
+ File: '{file: Buffer | stream.Readable, name: string}',
17
+ }
18
+ : {}
19
+
20
+ const dstFile = Path.join(outputDir, name + '.ts')
21
+
22
+ await generateApi({
23
+ name,
24
+ output: outputDir,
25
+ input: input,
26
+ // modular: true,
27
+ httpClientType: 'axios',
28
+ templates: Path.resolve(getThisScriptDirname(), '../templates'),
29
+ defaultResponseAsSuccess: true,
30
+ // generateRouteTypes: true,
31
+ singleHttpClient: true,
32
+ // extractRequestBody: true,
33
+ cleanOutput: true,
34
+ moduleNameFirstTag: true,
35
+ codeGenConstructs: (struct) => ({
36
+ // @ts-ignore
37
+ Keyword: specialMapping,
38
+ }),
39
+ // extractRequestParams: true,
40
+ })
41
+
42
+ if (isNode) {
43
+ let content = await fs.readFile(dstFile).then((r) => r.toString())
44
+ // Naive solution
45
+ content = 'import stream from \'node:stream\'\n' + content
46
+
47
+ await fs.writeFile(dstFile, content)
48
+ }
49
+ }
50
+
51
+ const getThisScriptDirname = (): string => {
52
+ // Might be problem in ESM mode
53
+ return __dirname
54
+ }
package/src/go.ts ADDED
@@ -0,0 +1,13 @@
1
+ #!/bin/env node
2
+ import { generateModel } from './gen-model'
3
+ import { cosmiconfigSync } from 'cosmiconfig'
4
+
5
+ const conf = cosmiconfigSync('api-client-gen')
6
+ .search()
7
+
8
+ // TODO validate config
9
+
10
+ if (!conf)
11
+ throw new Error(`No config provided`)
12
+
13
+ generateModel(conf.config)
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { generateModel } from "./gen-model";
2
+
3
+ export { generateModel } from "./gen-model";
4
+
5
+ // Bcs of mixing for ESM and CommonJS - todo fix somehow
6
+ export default { generateModel };
7
+
8
+
@@ -0,0 +1,27 @@
1
+ // Use this file also as a symlink for http-client.eta file
2
+
3
+ export type ContentType = {}
4
+
5
+ export type RequestParams = {
6
+ fileDownload?: boolean
7
+ }
8
+
9
+ export const ContentType = {
10
+ Json: 'application/json',
11
+ FormData: 'multipart/form-data',
12
+ }
13
+
14
+ export type Request = {
15
+ path: string
16
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE'
17
+ format?: 'json'
18
+ query?: any
19
+ body?: any
20
+ type?: string
21
+ secure?: boolean
22
+ fileDownload?: boolean
23
+ }
24
+
25
+ export type HttpClient<Any = any> = {
26
+ request: <Data, A = any>(req: Request) => Promise<Data>
27
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "moduleResolution": "node"
4
+ },
5
+ "extends": "../../tsconfig.json",
6
+ "include": [
7
+ "src/**/*.ts"
8
+ ]
9
+ }