vovk-cli 0.0.1-draft.256 → 0.0.1-draft.258
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.
|
@@ -5,7 +5,7 @@ import type { VovkRequest, VovkStreamAsyncIterable, KnownAny } from 'vovk';
|
|
|
5
5
|
|
|
6
6
|
export namespace Mixins {
|
|
7
7
|
<% for (const segment of mixins) { %>
|
|
8
|
-
<%
|
|
8
|
+
<% if(segment.meta?.components?.schemas) { %>
|
|
9
9
|
export namespace <%= t._.upperFirst(t._.camelCase(segment.segmentName)) %> {
|
|
10
10
|
<% for (const [componentName, componentSchema] of Object.entries(segment.meta.components.schemas)) { %>
|
|
11
11
|
<%- await t.compileJSONSchemaToTypeScriptType(componentSchema, componentName, segment.meta.components) %>
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { compile } from 'json-schema-to-typescript';
|
|
2
|
+
function fixUnresolvedLocalRefs(schema) {
|
|
3
|
+
const schemaStr = JSON.stringify(schema);
|
|
4
|
+
const refs = schemaStr.match(/#\/\$defs\/\w+/g) || [];
|
|
5
|
+
const defs = schema.$defs || {};
|
|
6
|
+
const missingRefs = refs.map((ref) => ref.replace('#/$defs/', '')).filter((defName) => !defs[defName]);
|
|
7
|
+
// Add missing definitions
|
|
8
|
+
if (missingRefs.length > 0) {
|
|
9
|
+
schema.$defs = schema.$defs || {};
|
|
10
|
+
missingRefs.forEach((defName) => {
|
|
11
|
+
schema.$defs[defName] = { tsType: 'unknown' }; // or whatever default you want
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return schema;
|
|
15
|
+
}
|
|
2
16
|
export async function compileJSONSchemaToTypeScriptType(schema, typeName, components = {}) {
|
|
3
17
|
if (!schema)
|
|
4
18
|
return '';
|
|
19
|
+
schema = fixUnresolvedLocalRefs(schema);
|
|
20
|
+
// tsType attribute isn't working with objects that use $ref, so we need to handle it separately
|
|
21
|
+
if ('tsType' in schema && typeof schema.tsType === 'string')
|
|
22
|
+
return `export type ${typeName} = ${schema.tsType};\n`;
|
|
5
23
|
const tsType = await compile({ ...schema, components }, typeName, {
|
|
6
24
|
bannerComment: schema.description ? `/**\n * ${schema.description}\n */` : '',
|
|
7
25
|
style: {
|
|
@@ -12,6 +30,8 @@ export async function compileJSONSchemaToTypeScriptType(schema, typeName, compon
|
|
|
12
30
|
tabWidth: 2,
|
|
13
31
|
useTabs: false,
|
|
14
32
|
trailingComma: 'all',
|
|
33
|
+
unreachableDefinitions: false,
|
|
34
|
+
declareExternallyReferenced: false,
|
|
15
35
|
},
|
|
16
36
|
// Don't generate separate interfaces for additionalProperties
|
|
17
37
|
additionalProperties: false,
|
|
@@ -21,205 +41,3 @@ export async function compileJSONSchemaToTypeScriptType(schema, typeName, compon
|
|
|
21
41
|
});
|
|
22
42
|
return tsType;
|
|
23
43
|
}
|
|
24
|
-
/*
|
|
25
|
-
// Extend JSONSchema to include custom x-formData property
|
|
26
|
-
interface ExtendedJSONSchema extends JSONSchema {
|
|
27
|
-
'x-formData'?: boolean;
|
|
28
|
-
[key: string]: any;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface OpenAPIDocument {
|
|
32
|
-
openapi: string;
|
|
33
|
-
components?: {
|
|
34
|
-
schemas?: Record<string, ExtendedJSONSchema>;
|
|
35
|
-
};
|
|
36
|
-
[key: string]: any;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Converts OpenAPI 3.1 component schemas to TypeScript type definitions
|
|
41
|
-
* @param oasDoc - The OpenAPI 3.1 document
|
|
42
|
-
* @param options - Optional configuration for the TypeScript generation
|
|
43
|
-
* @returns A string containing all TypeScript type definitions
|
|
44
|
-
* /
|
|
45
|
-
export async function oasToTypeScript(
|
|
46
|
-
oasDoc: OpenAPIDocument,
|
|
47
|
-
options?: {
|
|
48
|
-
bannerComment?: string;
|
|
49
|
-
style?: {
|
|
50
|
-
bracketSpacing?: boolean;
|
|
51
|
-
printWidth?: number;
|
|
52
|
-
semi?: boolean;
|
|
53
|
-
singleQuote?: boolean;
|
|
54
|
-
tabWidth?: number;
|
|
55
|
-
useTabs?: boolean;
|
|
56
|
-
trailingComma?: 'all' | 'es5' | 'none';
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
): Promise<string> {
|
|
60
|
-
// Validate that this is an OAS 3.1 document
|
|
61
|
-
if (!oasDoc.openapi || !oasDoc.openapi.startsWith('3.1')) {
|
|
62
|
-
throw new Error('Document must be an OpenAPI 3.1 specification');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Extract schemas from components
|
|
66
|
-
const schemas = oasDoc.components?.schemas || {};
|
|
67
|
-
|
|
68
|
-
if (Object.keys(schemas).length === 0) {
|
|
69
|
-
return '// No schemas found in components';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const typeDefinitions: string[] = [];
|
|
73
|
-
|
|
74
|
-
// Process each schema
|
|
75
|
-
for (const [schemaName, schema] of Object.entries(schemas)) {
|
|
76
|
-
try {
|
|
77
|
-
// Check if schema has x-formData: true
|
|
78
|
-
if (schema['x-formData'] === true) {
|
|
79
|
-
// Generate a simple type alias to FormData
|
|
80
|
-
typeDefinitions.push(`export type ${schemaName} = FormData;\n`);
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Handle schema references
|
|
85
|
-
const resolvedSchema = resolveSchemaReferences(schema, schemas);
|
|
86
|
-
|
|
87
|
-
// Compile to TypeScript
|
|
88
|
-
const tsType = await compile(resolvedSchema as JSONSchema, schemaName, {
|
|
89
|
-
bannerComment: options?.bannerComment || '',
|
|
90
|
-
style: options?.style || {
|
|
91
|
-
bracketSpacing: true,
|
|
92
|
-
printWidth: 80,
|
|
93
|
-
semi: true,
|
|
94
|
-
singleQuote: true,
|
|
95
|
-
tabWidth: 2,
|
|
96
|
-
useTabs: false,
|
|
97
|
-
trailingComma: 'all',
|
|
98
|
-
},
|
|
99
|
-
// Don't generate separate interfaces for additionalProperties
|
|
100
|
-
additionalProperties: false,
|
|
101
|
-
// Enable strict null checks
|
|
102
|
-
strictIndexSignatures: true,
|
|
103
|
-
// Don't add schema as comment
|
|
104
|
-
$refOptions: {
|
|
105
|
-
resolve: {
|
|
106
|
-
// Resolve internal references
|
|
107
|
-
internal: true,
|
|
108
|
-
// Add our custom resolver
|
|
109
|
-
custom: {
|
|
110
|
-
order: 1,
|
|
111
|
-
canRead: (ref: string) => ref.startsWith('#/components/schemas/'),
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
typeDefinitions.push(tsType);
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error(`Error processing schema "${schemaName}":`, error);
|
|
120
|
-
typeDefinitions.push(`// Error processing schema "${schemaName}": ${error.message}`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return typeDefinitions.join('\n');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Resolves $ref references within a schema
|
|
129
|
-
* /
|
|
130
|
-
function resolveSchemaReferences(
|
|
131
|
-
schema: any,
|
|
132
|
-
allSchemas: Record<string, ExtendedJSONSchema>,
|
|
133
|
-
visited = new Set<string>()
|
|
134
|
-
): any {
|
|
135
|
-
if (!schema || typeof schema !== 'object') {
|
|
136
|
-
return schema;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Handle $ref
|
|
140
|
-
if (schema.$ref && typeof schema.$ref === 'string') {
|
|
141
|
-
const refPath = schema.$ref.replace('#/components/schemas/', '');
|
|
142
|
-
|
|
143
|
-
// Prevent circular references
|
|
144
|
-
if (visited.has(refPath)) {
|
|
145
|
-
return { type: 'object', additionalProperties: true };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
visited.add(refPath);
|
|
149
|
-
const referencedSchema = allSchemas[refPath];
|
|
150
|
-
|
|
151
|
-
if (referencedSchema) {
|
|
152
|
-
return resolveSchemaReferences(referencedSchema, allSchemas, visited);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return schema;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Handle arrays
|
|
159
|
-
if (Array.isArray(schema)) {
|
|
160
|
-
return schema.map((item) => resolveSchemaReferences(item, allSchemas, visited));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Handle objects recursively
|
|
164
|
-
const resolved: any = {};
|
|
165
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
166
|
-
resolved[key] = resolveSchemaReferences(value, allSchemas, visited);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return resolved;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Example usage:
|
|
173
|
-
/*
|
|
174
|
-
const oasDoc = {
|
|
175
|
-
openapi: '3.1.0',
|
|
176
|
-
info: {
|
|
177
|
-
title: 'My API',
|
|
178
|
-
version: '1.0.0'
|
|
179
|
-
},
|
|
180
|
-
components: {
|
|
181
|
-
schemas: {
|
|
182
|
-
User: {
|
|
183
|
-
type: 'object',
|
|
184
|
-
properties: {
|
|
185
|
-
id: { type: 'integer' },
|
|
186
|
-
name: { type: 'string' },
|
|
187
|
-
email: { type: 'string', format: 'email' },
|
|
188
|
-
roles: {
|
|
189
|
-
type: 'array',
|
|
190
|
-
items: { $ref: '#/components/schemas/Role' }
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
required: ['id', 'name', 'email']
|
|
194
|
-
},
|
|
195
|
-
Role: {
|
|
196
|
-
type: 'object',
|
|
197
|
-
properties: {
|
|
198
|
-
id: { type: 'integer' },
|
|
199
|
-
name: { type: 'string' },
|
|
200
|
-
permissions: {
|
|
201
|
-
type: 'array',
|
|
202
|
-
items: { type: 'string' }
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
required: ['id', 'name']
|
|
206
|
-
},
|
|
207
|
-
UploadFileRequest: {
|
|
208
|
-
'x-formData': true,
|
|
209
|
-
type: 'object',
|
|
210
|
-
properties: {
|
|
211
|
-
file: { type: 'string', format: 'binary' },
|
|
212
|
-
description: { type: 'string' }
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const types = await oasToTypeScript(oasDoc);
|
|
220
|
-
console.log(types);
|
|
221
|
-
// Output will include:
|
|
222
|
-
// export interface User { ... }
|
|
223
|
-
// export interface Role { ... }
|
|
224
|
-
// export type UploadFileRequest = FormData;
|
|
225
|
-
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vovk-cli",
|
|
3
|
-
"version": "0.0.1-draft.
|
|
3
|
+
"version": "0.0.1-draft.258",
|
|
4
4
|
"bin": {
|
|
5
5
|
"vovk": "./dist/index.mjs"
|
|
6
6
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://vovk.dev",
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"vovk": "^3.0.0-draft.
|
|
38
|
+
"vovk": "^3.0.0-draft.296"
|
|
39
39
|
},
|
|
40
40
|
"optionalDependencies": {
|
|
41
41
|
"vovk-python": "^0.0.1-draft.41"
|