zod-codegen 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/.github/ISSUE_TEMPLATE/bug_report.yml +93 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +70 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +87 -0
- package/.github/dependabot.yml +76 -0
- package/.github/workflows/ci.yml +143 -0
- package/.github/workflows/release.yml +65 -0
- package/.husky/commit-msg +2 -0
- package/.husky/pre-commit +5 -0
- package/.lintstagedrc.json +4 -0
- package/.nvmrc +1 -0
- package/.prettierrc.json +7 -0
- package/.releaserc.json +159 -0
- package/CHANGELOG.md +24 -0
- package/CONTRIBUTING.md +274 -0
- package/LICENCE +201 -0
- package/README.md +263 -0
- package/SECURITY.md +108 -0
- package/codecov.yml +29 -0
- package/commitlint.config.mjs +28 -0
- package/dist/scripts/update-manifest.js +31 -0
- package/dist/src/assets/manifest.json +5 -0
- package/dist/src/cli.js +60 -0
- package/dist/src/generator.js +55 -0
- package/dist/src/http/fetch-client.js +141 -0
- package/dist/src/interfaces/code-generator.js +1 -0
- package/dist/src/interfaces/file-reader.js +1 -0
- package/dist/src/polyfills/fetch.js +18 -0
- package/dist/src/services/code-generator.service.js +419 -0
- package/dist/src/services/file-reader.service.js +25 -0
- package/dist/src/services/file-writer.service.js +32 -0
- package/dist/src/services/import-builder.service.js +45 -0
- package/dist/src/services/type-builder.service.js +42 -0
- package/dist/src/types/http.js +10 -0
- package/dist/src/types/openapi.js +173 -0
- package/dist/src/utils/error-handler.js +11 -0
- package/dist/src/utils/execution-time.js +3 -0
- package/dist/src/utils/manifest.js +9 -0
- package/dist/src/utils/reporter.js +15 -0
- package/dist/src/utils/signal-handler.js +12 -0
- package/dist/src/utils/tty.js +3 -0
- package/dist/tests/integration/cli.test.js +25 -0
- package/dist/tests/unit/generator.test.js +29 -0
- package/dist/vitest.config.js +38 -0
- package/eslint.config.mjs +33 -0
- package/package.json +102 -0
- package/samples/openapi.json +1 -0
- package/samples/saris-openapi.json +7122 -0
- package/samples/swagger-petstore.yaml +802 -0
- package/samples/swagger-saris.yaml +3585 -0
- package/samples/test-logical.yaml +50 -0
- package/scripts/update-manifest.js +31 -0
- package/scripts/update-manifest.ts +47 -0
- package/src/assets/manifest.json +5 -0
- package/src/cli.ts +68 -0
- package/src/generator.ts +61 -0
- package/src/http/fetch-client.ts +181 -0
- package/src/interfaces/code-generator.ts +25 -0
- package/src/interfaces/file-reader.ts +15 -0
- package/src/polyfills/fetch.ts +26 -0
- package/src/services/code-generator.service.ts +775 -0
- package/src/services/file-reader.service.ts +29 -0
- package/src/services/file-writer.service.ts +36 -0
- package/src/services/import-builder.service.ts +64 -0
- package/src/services/type-builder.service.ts +77 -0
- package/src/types/http.ts +35 -0
- package/src/types/openapi.ts +202 -0
- package/src/utils/error-handler.ts +13 -0
- package/src/utils/execution-time.ts +3 -0
- package/src/utils/manifest.ts +17 -0
- package/src/utils/reporter.ts +16 -0
- package/src/utils/signal-handler.ts +14 -0
- package/src/utils/tty.ts +3 -0
- package/tests/integration/cli.test.ts +29 -0
- package/tests/unit/generator.test.ts +36 -0
- package/tsconfig.json +44 -0
- package/vitest.config.ts +39 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
openapi: 3.0.0
|
|
2
|
+
info:
|
|
3
|
+
title: Test Logical Operators
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
paths: {}
|
|
6
|
+
components:
|
|
7
|
+
schemas:
|
|
8
|
+
TestAnyOf:
|
|
9
|
+
anyOf:
|
|
10
|
+
- type: string
|
|
11
|
+
- type: number
|
|
12
|
+
- type: boolean
|
|
13
|
+
|
|
14
|
+
TestOneOf:
|
|
15
|
+
oneOf:
|
|
16
|
+
- type: string
|
|
17
|
+
- type: number
|
|
18
|
+
|
|
19
|
+
TestAllOf:
|
|
20
|
+
allOf:
|
|
21
|
+
- type: object
|
|
22
|
+
properties:
|
|
23
|
+
name:
|
|
24
|
+
type: string
|
|
25
|
+
- type: object
|
|
26
|
+
properties:
|
|
27
|
+
age:
|
|
28
|
+
type: number
|
|
29
|
+
|
|
30
|
+
TestNot:
|
|
31
|
+
not:
|
|
32
|
+
type: string
|
|
33
|
+
|
|
34
|
+
TestComplex:
|
|
35
|
+
type: object
|
|
36
|
+
properties:
|
|
37
|
+
unionField:
|
|
38
|
+
anyOf:
|
|
39
|
+
- type: string
|
|
40
|
+
- type: number
|
|
41
|
+
intersectionField:
|
|
42
|
+
allOf:
|
|
43
|
+
- type: object
|
|
44
|
+
properties:
|
|
45
|
+
id:
|
|
46
|
+
type: string
|
|
47
|
+
- type: object
|
|
48
|
+
properties:
|
|
49
|
+
timestamp:
|
|
50
|
+
type: number
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {readFileSync, writeFileSync} from 'fs';
|
|
2
|
+
import {resolve} from 'path';
|
|
3
|
+
import {z} from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Type guard for the package.json object
|
|
6
|
+
*
|
|
7
|
+
* @param input Unknown input
|
|
8
|
+
* @returns true if the input is an event object
|
|
9
|
+
*/
|
|
10
|
+
export function isPackageJson(input) {
|
|
11
|
+
const event = z
|
|
12
|
+
.object({
|
|
13
|
+
name: z.string(),
|
|
14
|
+
version: z.string(),
|
|
15
|
+
description: z.string(),
|
|
16
|
+
})
|
|
17
|
+
.strict()
|
|
18
|
+
.catchall(z.any())
|
|
19
|
+
.required();
|
|
20
|
+
const {success} = event.safeParse(input);
|
|
21
|
+
return success;
|
|
22
|
+
}
|
|
23
|
+
const sourcePath = resolve(__dirname, '..', 'package.json');
|
|
24
|
+
const data = JSON.parse(readFileSync(sourcePath, 'utf8'));
|
|
25
|
+
if (!isPackageJson(data)) {
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const {name, version, description} = data;
|
|
29
|
+
const targetPath = resolve(__dirname, '..', 'src', 'assets', 'manifest.json');
|
|
30
|
+
writeFileSync(targetPath, JSON.stringify({name, version, description}, null, 2));
|
|
31
|
+
process.exit(0);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {readFileSync, writeFileSync} from 'fs';
|
|
2
|
+
import {resolve} from 'path';
|
|
3
|
+
import {z} from 'zod';
|
|
4
|
+
|
|
5
|
+
interface PackageJson {
|
|
6
|
+
name: string;
|
|
7
|
+
version: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Type guard for the package.json object
|
|
13
|
+
*
|
|
14
|
+
* @param input Unknown input
|
|
15
|
+
* @returns true if the input is an event object
|
|
16
|
+
*/
|
|
17
|
+
export function isPackageJson(input: unknown): input is PackageJson {
|
|
18
|
+
const event = z
|
|
19
|
+
.object({
|
|
20
|
+
name: z.string(),
|
|
21
|
+
version: z.string(),
|
|
22
|
+
description: z.string(),
|
|
23
|
+
})
|
|
24
|
+
.strict()
|
|
25
|
+
.catchall(z.any())
|
|
26
|
+
.required();
|
|
27
|
+
|
|
28
|
+
const {success} = event.safeParse(input);
|
|
29
|
+
|
|
30
|
+
return success;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const sourcePath = resolve(__dirname, '..', 'package.json');
|
|
34
|
+
|
|
35
|
+
const data: unknown = JSON.parse(readFileSync(sourcePath, 'utf8'));
|
|
36
|
+
|
|
37
|
+
if (!isPackageJson(data)) {
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const {name, version, description} = data;
|
|
42
|
+
|
|
43
|
+
const targetPath = resolve(__dirname, '..', 'src', 'assets', 'manifest.json');
|
|
44
|
+
|
|
45
|
+
writeFileSync(targetPath, JSON.stringify({name, version, description}, null, 2));
|
|
46
|
+
|
|
47
|
+
process.exit(0);
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import yargs from 'yargs';
|
|
4
|
+
import {hideBin} from 'yargs/helpers';
|
|
5
|
+
import {Generator} from './generator.js';
|
|
6
|
+
import {readFileSync} from 'node:fs';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {dirname, join} from 'node:path';
|
|
9
|
+
|
|
10
|
+
import loudRejection from 'loud-rejection';
|
|
11
|
+
import {handleErrors} from './utils/error-handler.js';
|
|
12
|
+
import {handleSignals} from './utils/signal-handler.js';
|
|
13
|
+
import debug from 'debug';
|
|
14
|
+
import {isManifest} from './utils/manifest.js';
|
|
15
|
+
import {Reporter} from './utils/reporter.js';
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const manifestData: unknown = JSON.parse(readFileSync(join(__dirname, 'assets', 'manifest.json'), 'utf-8'));
|
|
19
|
+
|
|
20
|
+
if (!isManifest(manifestData)) {
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const {name, description, version} = manifestData;
|
|
25
|
+
const reporter = new Reporter(process.stdout);
|
|
26
|
+
const startTime = process.hrtime.bigint();
|
|
27
|
+
|
|
28
|
+
debug(`${name}:${String(process.pid)}`);
|
|
29
|
+
|
|
30
|
+
loudRejection();
|
|
31
|
+
handleSignals(process, startTime);
|
|
32
|
+
handleErrors(process, startTime);
|
|
33
|
+
|
|
34
|
+
const argv = yargs(hideBin(process.argv))
|
|
35
|
+
.scriptName(name)
|
|
36
|
+
.usage(`${description}\n\nUsage: $0 [options]`)
|
|
37
|
+
.version(version)
|
|
38
|
+
.option('input', {
|
|
39
|
+
alias: 'i',
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'Path or URL to OpenAPI file',
|
|
42
|
+
demandOption: true,
|
|
43
|
+
})
|
|
44
|
+
.option('output', {
|
|
45
|
+
alias: 'o',
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Directory to output the generated files',
|
|
48
|
+
default: 'generated',
|
|
49
|
+
})
|
|
50
|
+
.help()
|
|
51
|
+
.parseSync();
|
|
52
|
+
|
|
53
|
+
const {input, output} = argv;
|
|
54
|
+
|
|
55
|
+
void (async () => {
|
|
56
|
+
try {
|
|
57
|
+
const generator = new Generator(name, version, reporter, input, output);
|
|
58
|
+
const exitCode = await generator.run();
|
|
59
|
+
process.exit(exitCode);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error instanceof Error) {
|
|
62
|
+
reporter.error(`Fatal error: ${error.message}`);
|
|
63
|
+
} else {
|
|
64
|
+
reporter.error('An unknown fatal error occurred');
|
|
65
|
+
}
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
})();
|
package/src/generator.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type {Reporter} from './utils/reporter.js';
|
|
2
|
+
import type {OpenApiSpecType} from './types/openapi.js';
|
|
3
|
+
import {OpenApiFileParserService, SyncFileReaderService} from './services/file-reader.service.js';
|
|
4
|
+
import {TypeScriptCodeGeneratorService} from './services/code-generator.service.js';
|
|
5
|
+
import {SyncFileWriterService} from './services/file-writer.service.js';
|
|
6
|
+
|
|
7
|
+
export class Generator {
|
|
8
|
+
private readonly fileReader = new SyncFileReaderService();
|
|
9
|
+
private readonly fileParser = new OpenApiFileParserService();
|
|
10
|
+
private readonly codeGenerator = new TypeScriptCodeGeneratorService();
|
|
11
|
+
private readonly fileWriter: SyncFileWriterService;
|
|
12
|
+
private readonly outputPath: string;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
private readonly _name: string,
|
|
16
|
+
private readonly _version: string,
|
|
17
|
+
private readonly reporter: Reporter,
|
|
18
|
+
private readonly inputPath: string,
|
|
19
|
+
private readonly _outputDir: string,
|
|
20
|
+
) {
|
|
21
|
+
this.fileWriter = new SyncFileWriterService(this._name, this._version, inputPath);
|
|
22
|
+
this.outputPath = this.fileWriter.resolveOutputPath(this._outputDir);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async run(): Promise<number> {
|
|
26
|
+
try {
|
|
27
|
+
const rawSource = this.readFile();
|
|
28
|
+
const openApiSpec = this.parseFile(rawSource);
|
|
29
|
+
const generatedCode = this.generateCode(openApiSpec);
|
|
30
|
+
|
|
31
|
+
this.writeFile(generatedCode);
|
|
32
|
+
this.reporter.log(`✅ Generated types successfully at: ${this.outputPath}`);
|
|
33
|
+
|
|
34
|
+
return 0;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof Error) {
|
|
37
|
+
this.reporter.error(`❌ Error: ${error.message}`);
|
|
38
|
+
} else {
|
|
39
|
+
this.reporter.error('❌ An unknown error occurred');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return Promise.resolve(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private readFile(): string {
|
|
47
|
+
return this.fileReader.readFile(this.inputPath);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private parseFile(source: string): OpenApiSpecType {
|
|
51
|
+
return this.fileParser.parse(source);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private generateCode(spec: OpenApiSpecType): string {
|
|
55
|
+
return this.codeGenerator.generate(spec);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private writeFile(content: string): void {
|
|
59
|
+
this.fileWriter.writeFile(this.outputPath, content);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {HttpClient, HttpError, HttpRequestConfig, HttpResponse} from '../types/http.js';
|
|
2
|
+
|
|
3
|
+
declare const globalThis: typeof global & {
|
|
4
|
+
fetch?: typeof fetch;
|
|
5
|
+
Headers?: typeof Headers;
|
|
6
|
+
Request?: typeof Request;
|
|
7
|
+
Response?: typeof Response;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type FetchFunction = (input: string | Request, init?: RequestInit) => Promise<Response>;
|
|
11
|
+
|
|
12
|
+
type HeadersConstructor = new (init?: HeadersInit) => Headers;
|
|
13
|
+
|
|
14
|
+
export class FetchHttpClient implements HttpClient {
|
|
15
|
+
private readonly fetch: FetchFunction;
|
|
16
|
+
private readonly Headers: HeadersConstructor;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly baseUrl = '',
|
|
20
|
+
private readonly defaultHeaders: Record<string, string> = {},
|
|
21
|
+
) {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
23
|
+
if (typeof globalThis.fetch === 'function' && globalThis.Headers) {
|
|
24
|
+
this.fetch = globalThis.fetch.bind(globalThis);
|
|
25
|
+
this.Headers = globalThis.Headers;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
30
|
+
this.fetch = window.fetch.bind(window);
|
|
31
|
+
this.Headers = window.Headers;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
throw new Error(
|
|
36
|
+
"Fetch API is not available. Please ensure you're running in a compatible environment or polyfill fetch.",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async request<TResponse = unknown, TRequest = unknown>(
|
|
41
|
+
config: HttpRequestConfig<TRequest>,
|
|
42
|
+
): Promise<HttpResponse<TResponse>> {
|
|
43
|
+
const url = this.buildUrl(config.url, config.params);
|
|
44
|
+
const headers = this.buildHeaders(config.headers);
|
|
45
|
+
const body = this.buildBody(config.data, headers);
|
|
46
|
+
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timeoutId = config.timeout
|
|
49
|
+
? setTimeout(() => {
|
|
50
|
+
controller.abort();
|
|
51
|
+
}, config.timeout)
|
|
52
|
+
: undefined;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const response = await this.fetch(url, {
|
|
56
|
+
method: config.method,
|
|
57
|
+
headers,
|
|
58
|
+
body,
|
|
59
|
+
signal: controller.signal,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const responseHeaders = this.extractHeaders(response.headers);
|
|
63
|
+
const data = await this.parseResponse<TResponse>(response);
|
|
64
|
+
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
throw new HttpError(`HTTP ${String(response.status)}: ${response.statusText}`, response.status, {
|
|
67
|
+
data,
|
|
68
|
+
status: response.status,
|
|
69
|
+
statusText: response.statusText,
|
|
70
|
+
headers: responseHeaders,
|
|
71
|
+
url: response.url,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
data,
|
|
77
|
+
status: response.status,
|
|
78
|
+
statusText: response.statusText,
|
|
79
|
+
headers: responseHeaders,
|
|
80
|
+
url: response.url,
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error instanceof HttpError) {
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (error instanceof Error) {
|
|
88
|
+
if (error.name === 'AbortError') {
|
|
89
|
+
throw new HttpError('Request timeout', 408);
|
|
90
|
+
}
|
|
91
|
+
throw new HttpError(`Network error: ${error.message}`, 0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw new HttpError('Unknown network error', 0);
|
|
95
|
+
} finally {
|
|
96
|
+
if (timeoutId) {
|
|
97
|
+
clearTimeout(timeoutId);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private buildUrl(path: string, params?: Record<string, string | number | boolean>): string {
|
|
103
|
+
const url = new URL(path, this.baseUrl || 'http://localhost');
|
|
104
|
+
|
|
105
|
+
if (params) {
|
|
106
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
108
|
+
if (value !== undefined && value !== null) {
|
|
109
|
+
url.searchParams.set(key, String(value));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return url.toString();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private buildHeaders(customHeaders?: Record<string, string>): Headers {
|
|
118
|
+
const headers = new this.Headers();
|
|
119
|
+
|
|
120
|
+
Object.entries(this.defaultHeaders).forEach(([key, value]) => {
|
|
121
|
+
headers.set(key, value);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (customHeaders) {
|
|
125
|
+
Object.entries(customHeaders).forEach(([key, value]) => {
|
|
126
|
+
headers.set(key, value);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return headers;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private buildBody(data?: unknown, headers?: Headers): string | null {
|
|
134
|
+
if (!data) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const contentType = headers?.get('content-type') ?? 'application/json';
|
|
139
|
+
|
|
140
|
+
if (contentType.includes('application/json')) {
|
|
141
|
+
if (!headers?.has('content-type')) {
|
|
142
|
+
headers?.set('content-type', 'application/json');
|
|
143
|
+
}
|
|
144
|
+
return JSON.stringify(data);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (typeof data === 'string') {
|
|
148
|
+
return data;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return JSON.stringify(data);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private extractHeaders(headers: Headers): Record<string, string> {
|
|
155
|
+
const result: Record<string, string> = {};
|
|
156
|
+
headers.forEach((value, key) => {
|
|
157
|
+
result[key.toLowerCase()] = value;
|
|
158
|
+
});
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private async parseResponse<TResponse>(response: Response): Promise<TResponse> {
|
|
163
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
164
|
+
|
|
165
|
+
if (contentType.includes('application/json')) {
|
|
166
|
+
return (await response.json()) as TResponse;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (contentType.includes('text/')) {
|
|
170
|
+
return (await response.text()) as unknown as TResponse;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const text = await response.text();
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
176
|
+
return text ? JSON.parse(text) : ({} as TResponse);
|
|
177
|
+
} catch {
|
|
178
|
+
return {} as TResponse;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {OpenApiSpecType} from '../types/openapi.js';
|
|
2
|
+
|
|
3
|
+
export interface CodeGenerator {
|
|
4
|
+
generate(spec: OpenApiSpecType): string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface SchemaBuilder {
|
|
8
|
+
buildSchema(schema: unknown, required?: boolean): unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TypeBuilder {
|
|
12
|
+
buildType(type: string): unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ImportBuilder {
|
|
16
|
+
buildImports(): unknown[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ClassBuilder {
|
|
20
|
+
buildClass(spec: OpenApiSpecType): unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface FileWriter {
|
|
24
|
+
writeFile(filePath: string, content: string): Promise<void> | void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface FileReader {
|
|
2
|
+
readFile(path: string): Promise<string> | string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface FileParser<TInput, TOutput> {
|
|
6
|
+
parse(input: TInput): TOutput;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface OpenApiFileReader extends FileReader {
|
|
10
|
+
readFile(path: string): Promise<string> | string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface OpenApiFileParser<TOutput> extends FileParser<unknown, TOutput> {
|
|
14
|
+
parse(input: unknown): TOutput;
|
|
15
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface FetchPolyfillOptions {
|
|
2
|
+
enableNodejsPolyfill?: boolean;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export async function setupFetchPolyfill(options: FetchPolyfillOptions = {}): Promise<void> {
|
|
6
|
+
if (typeof fetch === 'function') {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (options.enableNodejsPolyfill && typeof process !== 'undefined' && process.versions.node) {
|
|
15
|
+
try {
|
|
16
|
+
await import('undici');
|
|
17
|
+
return;
|
|
18
|
+
} catch {
|
|
19
|
+
// Fall through to error
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
throw new Error(
|
|
24
|
+
'Fetch API is not available. ' + 'For Node.js environments, please install undici: npm install undici',
|
|
25
|
+
);
|
|
26
|
+
}
|