vovk 3.5.1 → 3.7.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/client/create-rpc.d.ts +13 -0
- package/dist/client/create-rpc.js +147 -0
- package/dist/client/default-handler.d.ts +6 -0
- package/dist/client/default-handler.js +25 -0
- package/dist/client/default-stream-handler.d.ts +16 -0
- package/dist/client/default-stream-handler.js +282 -0
- package/dist/client/fetcher.d.ts +1 -1
- package/dist/client/fetcher.js +2 -2
- package/dist/client/serialize-query.d.ts +13 -0
- package/dist/client/serialize-query.js +62 -0
- package/dist/core/apply-decorator-adapter.d.ts +7 -0
- package/dist/core/apply-decorator-adapter.js +50 -0
- package/dist/core/controllers-to-static-params.d.ts +13 -0
- package/dist/core/controllers-to-static-params.js +32 -0
- package/dist/core/create-decorator.d.ts +12 -0
- package/dist/core/create-decorator.js +52 -0
- package/dist/core/decorators.js +4 -4
- package/dist/core/get-schema.d.ts +21 -0
- package/dist/core/get-schema.js +31 -0
- package/dist/core/http-exception.d.ts +16 -0
- package/dist/core/http-exception.js +26 -0
- package/dist/core/init-segment.d.ts +33 -0
- package/dist/core/init-segment.js +62 -0
- package/dist/core/json-lines-responder.d.ts +42 -0
- package/dist/core/json-lines-responder.js +94 -0
- package/dist/core/resolve-generator-config-values.d.ts +19 -0
- package/dist/core/resolve-generator-config-values.js +59 -0
- package/dist/core/set-handler-schema.d.ts +4 -0
- package/dist/core/set-handler-schema.js +12 -0
- package/dist/core/to-download-response.d.ts +11 -0
- package/dist/core/to-download-response.js +25 -0
- package/dist/core/vovk-app.d.ts +36 -0
- package/dist/core/vovk-app.js +318 -0
- package/dist/index.d.ts +10 -10
- package/dist/index.js +10 -10
- package/dist/internal.d.ts +10 -10
- package/dist/internal.js +9 -9
- package/dist/openapi/error.js +1 -1
- package/dist/openapi/openapi-to-vovk-schema/apply-components-schemas.d.ts +23 -0
- package/dist/openapi/openapi-to-vovk-schema/apply-components-schemas.js +90 -0
- package/dist/openapi/openapi-to-vovk-schema/index.d.ts +5 -0
- package/dist/openapi/openapi-to-vovk-schema/index.js +179 -0
- package/dist/openapi/openapi-to-vovk-schema/inline-refs.d.ts +9 -0
- package/dist/openapi/openapi-to-vovk-schema/inline-refs.js +99 -0
- package/dist/openapi/openapi-to-vovk-schema/prune-components-schemas.d.ts +7 -0
- package/dist/openapi/openapi-to-vovk-schema/prune-components-schemas.js +51 -0
- package/dist/openapi/operation.js +1 -1
- package/dist/openapi/tool.js +1 -1
- package/dist/openapi/vovk-schema-to-openapi.d.ts +21 -0
- package/dist/openapi/vovk-schema-to-openapi.js +250 -0
- package/dist/req/buffer-body.d.ts +1 -0
- package/dist/req/buffer-body.js +30 -0
- package/dist/req/parse-body.d.ts +4 -0
- package/dist/req/parse-body.js +49 -0
- package/dist/req/parse-form.d.ts +1 -0
- package/dist/req/parse-form.js +24 -0
- package/dist/req/parse-query.d.ts +24 -0
- package/dist/req/parse-query.js +156 -0
- package/dist/req/req-meta.d.ts +2 -0
- package/dist/req/req-meta.js +10 -0
- package/dist/req/req-query.d.ts +2 -0
- package/dist/req/req-query.js +4 -0
- package/dist/req/validate-content-type.d.ts +1 -0
- package/dist/req/validate-content-type.js +32 -0
- package/dist/samples/create-code-samples.d.ts +20 -0
- package/dist/samples/create-code-samples.js +293 -0
- package/dist/samples/object-to-code.d.ts +8 -0
- package/dist/samples/object-to-code.js +38 -0
- package/dist/samples/schema-to-code.d.ts +11 -0
- package/dist/samples/schema-to-code.js +264 -0
- package/dist/samples/schema-to-object.d.ts +2 -0
- package/dist/samples/schema-to-object.js +164 -0
- package/dist/samples/schema-to-ts-type.d.ts +2 -0
- package/dist/samples/schema-to-ts-type.js +114 -0
- package/dist/tools/create-tool-factory.d.ts +135 -0
- package/dist/tools/create-tool-factory.js +62 -0
- package/dist/tools/create-tool.d.ts +126 -0
- package/dist/tools/create-tool.js +6 -0
- package/dist/tools/derive-tools.d.ts +46 -0
- package/dist/tools/derive-tools.js +131 -0
- package/dist/tools/to-model-output-default.d.ts +7 -0
- package/dist/tools/to-model-output-default.js +7 -0
- package/dist/tools/to-model-output-mcp.d.ts +30 -0
- package/dist/tools/to-model-output-mcp.js +54 -0
- package/dist/tools/to-model-output.d.ts +8 -0
- package/dist/tools/to-model-output.js +10 -0
- package/dist/types/client.d.ts +3 -3
- package/dist/types/core.d.ts +1 -1
- package/dist/types/inference.d.ts +1 -1
- package/dist/types/validation.d.ts +1 -1
- package/dist/utils/camel-case.d.ts +6 -0
- package/dist/utils/camel-case.js +34 -0
- package/dist/utils/deep-extend.d.ts +54 -0
- package/dist/utils/deep-extend.js +127 -0
- package/dist/utils/file-name-to-disposition.d.ts +1 -0
- package/dist/utils/file-name-to-disposition.js +3 -0
- package/dist/utils/to-kebab-case.d.ts +1 -0
- package/dist/utils/to-kebab-case.js +5 -0
- package/dist/utils/trim-path.d.ts +1 -0
- package/dist/utils/trim-path.js +1 -0
- package/dist/utils/upper-first.d.ts +1 -0
- package/dist/utils/upper-first.js +3 -0
- package/dist/validation/create-standard-validation.d.ts +268 -0
- package/dist/validation/create-standard-validation.js +45 -0
- package/dist/validation/create-validate-on-client.d.ts +14 -0
- package/dist/validation/create-validate-on-client.js +23 -0
- package/dist/validation/procedure.d.ts +24 -24
- package/dist/validation/procedure.js +1 -1
- package/dist/validation/validation-schemas-object-to-single-validation-schema.d.ts +17 -0
- package/dist/validation/validation-schemas-object-to-single-validation-schema.js +92 -0
- package/dist/validation/with-validation-library.d.ts +119 -0
- package/dist/validation/with-validation-library.js +184 -0
- package/package.json +13 -5
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function objectToCode(obj, options) {
|
|
2
|
+
const { stripQuotes = false, indent = 0, nestingIndent = 2, quote = '"' } = options || {};
|
|
3
|
+
// Use JSON.stringify with the nesting indent
|
|
4
|
+
let result = JSON.stringify(obj, null, nestingIndent);
|
|
5
|
+
// Replace double quotes with single quotes for string values if requested
|
|
6
|
+
if (quote === "'") {
|
|
7
|
+
// First, escape any existing single quotes in string values
|
|
8
|
+
result = result.replace(/"([^"]*)"/g, (match, content) => {
|
|
9
|
+
// Check if this is a key (followed by colon) or a value
|
|
10
|
+
const matchIndex = result.indexOf(match);
|
|
11
|
+
const afterMatch = result.substring(matchIndex + match.length);
|
|
12
|
+
if (afterMatch.startsWith(':')) {
|
|
13
|
+
// This is a key, keep it for now
|
|
14
|
+
return match;
|
|
15
|
+
}
|
|
16
|
+
// This is a value, replace quotes and escape single quotes
|
|
17
|
+
const escaped = content.replace(/'/g, "\\'");
|
|
18
|
+
return `'${escaped}'`;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
// Strip quotes from keys if requested
|
|
22
|
+
if (stripQuotes) {
|
|
23
|
+
// Remove quotes from keys that are valid JavaScript identifiers
|
|
24
|
+
// Keep quotes for keys with special characters (like 'x-foo', spaces, etc.)
|
|
25
|
+
const keyQuote = quote === "'" ? "'" : '"';
|
|
26
|
+
const pattern = new RegExp(`${keyQuote}([a-zA-Z_$][a-zA-Z0-9_$]*)${keyQuote}:`, 'g');
|
|
27
|
+
result = result.replace(pattern, '$1:');
|
|
28
|
+
}
|
|
29
|
+
// Apply base indentation if specified
|
|
30
|
+
if (indent > 0) {
|
|
31
|
+
const indentStr = ' '.repeat(indent);
|
|
32
|
+
result = result
|
|
33
|
+
.split('\n')
|
|
34
|
+
.map((line, i) => (i === 0 ? line : indentStr + line))
|
|
35
|
+
.join('\n');
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { VovkJSONSchemaBase } from '../types/json-schema.js';
|
|
2
|
+
interface SamplerOptions {
|
|
3
|
+
comment?: '//' | '#';
|
|
4
|
+
stripQuotes?: boolean;
|
|
5
|
+
indent?: number;
|
|
6
|
+
nestingIndent?: number;
|
|
7
|
+
ignoreBinary?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function schemaToCode(schema: VovkJSONSchemaBase, options: SamplerOptions, rootSchema?: VovkJSONSchemaBase): string;
|
|
10
|
+
export declare function getSampleValue(schema: VovkJSONSchemaBase, rootSchema?: VovkJSONSchemaBase, ignoreBinary?: boolean): unknown;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
export function schemaToCode(schema, options, rootSchema) {
|
|
2
|
+
const { comment = '//', stripQuotes = false, indent = 0, nestingIndent = 4, ignoreBinary = false } = options;
|
|
3
|
+
if (!schema || typeof schema !== 'object')
|
|
4
|
+
return 'null';
|
|
5
|
+
// Use the input schema as the root if not provided
|
|
6
|
+
rootSchema = rootSchema || schema;
|
|
7
|
+
// Get the sample value
|
|
8
|
+
const sampleValue = getSampleValue(schema, rootSchema, ignoreBinary);
|
|
9
|
+
// Format the output with descriptions
|
|
10
|
+
return formatWithDescriptions(sampleValue, schema, rootSchema, comment, stripQuotes, indent, nestingIndent, ignoreBinary, true // isTopLevel
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
export function getSampleValue(schema, rootSchema, ignoreBinary) {
|
|
14
|
+
if (!schema || typeof schema !== 'object')
|
|
15
|
+
return null;
|
|
16
|
+
rootSchema = rootSchema || schema;
|
|
17
|
+
// Check if this is a binary string schema and should be ignored
|
|
18
|
+
if (ignoreBinary && schema.type === 'string' && schema.format === 'binary') {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
// If there's an example, use it
|
|
22
|
+
if (schema.example !== undefined) {
|
|
23
|
+
return schema.example;
|
|
24
|
+
}
|
|
25
|
+
// If there are examples, use one of them
|
|
26
|
+
if (schema.examples && schema.examples.length > 0) {
|
|
27
|
+
return schema.examples[0];
|
|
28
|
+
}
|
|
29
|
+
// Handle const if present
|
|
30
|
+
if (schema.const !== undefined) {
|
|
31
|
+
return schema.const;
|
|
32
|
+
}
|
|
33
|
+
// Handle $ref if present
|
|
34
|
+
if (schema.$ref) {
|
|
35
|
+
return handleRef(schema.$ref, rootSchema, ignoreBinary);
|
|
36
|
+
}
|
|
37
|
+
// Handle enum if present
|
|
38
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
39
|
+
return schema.enum[0];
|
|
40
|
+
}
|
|
41
|
+
// Handle oneOf, anyOf, allOf
|
|
42
|
+
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
43
|
+
return getSampleValue(schema.oneOf[0], rootSchema, ignoreBinary);
|
|
44
|
+
}
|
|
45
|
+
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
46
|
+
return getSampleValue(schema.anyOf[0], rootSchema, ignoreBinary);
|
|
47
|
+
}
|
|
48
|
+
if (schema.allOf && schema.allOf.length > 0) {
|
|
49
|
+
// Merge all schemas in allOf
|
|
50
|
+
const mergedSchema = schema.allOf.reduce((acc, s) => Object.assign(acc, s), {});
|
|
51
|
+
return getSampleValue(mergedSchema, rootSchema, ignoreBinary);
|
|
52
|
+
}
|
|
53
|
+
// Handle different types
|
|
54
|
+
if (schema.type) {
|
|
55
|
+
switch (schema.type) {
|
|
56
|
+
case 'string':
|
|
57
|
+
return handleString(schema);
|
|
58
|
+
case 'number':
|
|
59
|
+
case 'integer':
|
|
60
|
+
return handleNumber(schema);
|
|
61
|
+
case 'boolean':
|
|
62
|
+
return handleBoolean();
|
|
63
|
+
case 'object':
|
|
64
|
+
return handleObject(schema, rootSchema, ignoreBinary);
|
|
65
|
+
case 'array':
|
|
66
|
+
return handleArray(schema, rootSchema, ignoreBinary);
|
|
67
|
+
case 'null':
|
|
68
|
+
return null;
|
|
69
|
+
default:
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// If type is not specified but properties are, treat it as an object
|
|
74
|
+
if (schema.properties) {
|
|
75
|
+
return handleObject(schema, rootSchema, ignoreBinary);
|
|
76
|
+
}
|
|
77
|
+
// Default fallback
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
function formatWithDescriptions(value, schema, rootSchema, comment, stripQuotes, indent, nestingIndent, ignoreBinary, isTopLevel) {
|
|
81
|
+
const indentStr = ' '.repeat(indent);
|
|
82
|
+
const nestIndentStr = ' '.repeat(nestingIndent); // Create nesting indent string
|
|
83
|
+
// Handle undefined (for ignored binary fields)
|
|
84
|
+
if (value === undefined) {
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
// Handle null
|
|
88
|
+
if (value === null) {
|
|
89
|
+
return 'null';
|
|
90
|
+
}
|
|
91
|
+
// Handle primitives
|
|
92
|
+
if (typeof value !== 'object' || value instanceof Date) {
|
|
93
|
+
return JSON.stringify(value);
|
|
94
|
+
}
|
|
95
|
+
// Handle arrays
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
if (value.length === 0)
|
|
98
|
+
return '[]';
|
|
99
|
+
const items = value.map((item) => {
|
|
100
|
+
const itemSchema = schema.items && typeof schema.items === 'object' ? schema.items : {};
|
|
101
|
+
const formattedItem = formatWithDescriptions(item, itemSchema, rootSchema, comment, stripQuotes, indent + nestingIndent, // Use nestingIndent instead of hardcoded 4
|
|
102
|
+
nestingIndent, ignoreBinary, false);
|
|
103
|
+
return `${indentStr}${nestIndentStr}${formattedItem}`; // Use nestIndentStr for item indentation
|
|
104
|
+
});
|
|
105
|
+
return `[\n${items.join(',\n')}\n${indentStr}]`;
|
|
106
|
+
}
|
|
107
|
+
// Handle objects
|
|
108
|
+
if (typeof value === 'object') {
|
|
109
|
+
const entries = Object.entries(value);
|
|
110
|
+
if (entries.length === 0)
|
|
111
|
+
return '{}';
|
|
112
|
+
const formattedEntries = [];
|
|
113
|
+
// Add top-level description for objects
|
|
114
|
+
if (isTopLevel && schema.type === 'object' && schema.description) {
|
|
115
|
+
const descLines = schema.description.split('\n');
|
|
116
|
+
formattedEntries.push(`${indentStr}${nestIndentStr}${comment} -----`);
|
|
117
|
+
descLines.forEach((line) => {
|
|
118
|
+
formattedEntries.push(`${indentStr}${nestIndentStr}${comment} ${line.trim()}`);
|
|
119
|
+
});
|
|
120
|
+
formattedEntries.push(`${indentStr}${nestIndentStr}${comment} -----`);
|
|
121
|
+
}
|
|
122
|
+
entries.forEach(([key, val], index) => {
|
|
123
|
+
const propSchema = schema.properties?.[key] ?? {};
|
|
124
|
+
// Handle $ref in property schema
|
|
125
|
+
let resolvedPropSchema = propSchema;
|
|
126
|
+
if (propSchema.$ref) {
|
|
127
|
+
resolvedPropSchema = resolveRef(propSchema.$ref, rootSchema);
|
|
128
|
+
}
|
|
129
|
+
// Add property description if it exists
|
|
130
|
+
if (resolvedPropSchema.description) {
|
|
131
|
+
const descLines = resolvedPropSchema.description.split('\n');
|
|
132
|
+
descLines.forEach((line) => {
|
|
133
|
+
formattedEntries.push(`${indentStr}${nestIndentStr}${comment} ${line.trim()}`);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// Format the key
|
|
137
|
+
const formattedKey = stripQuotes && /^[A-Za-z_$][0-9A-Za-z_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
138
|
+
// Format the value
|
|
139
|
+
const formattedValue = formatWithDescriptions(val, resolvedPropSchema, rootSchema, comment, stripQuotes, indent + nestingIndent, nestingIndent, ignoreBinary, false);
|
|
140
|
+
formattedEntries.push(`${indentStr}${nestIndentStr}${formattedKey}: ${formattedValue}${index < entries.length - 1 ? ',' : ''}`);
|
|
141
|
+
});
|
|
142
|
+
return `{\n${formattedEntries.join('\n')}\n${indentStr}}`;
|
|
143
|
+
}
|
|
144
|
+
return JSON.stringify(value);
|
|
145
|
+
}
|
|
146
|
+
function resolveRef(ref, rootSchema) {
|
|
147
|
+
const path = ref.split('/').slice(1); // Remove the initial '#'
|
|
148
|
+
let current = rootSchema;
|
|
149
|
+
for (const segment of path) {
|
|
150
|
+
current = current[segment];
|
|
151
|
+
if (current === undefined) {
|
|
152
|
+
return {};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return current;
|
|
156
|
+
}
|
|
157
|
+
function handleRef(ref, rootSchema, ignoreBinary) {
|
|
158
|
+
const resolved = resolveRef(ref, rootSchema);
|
|
159
|
+
return getSampleValue(resolved, rootSchema, ignoreBinary);
|
|
160
|
+
}
|
|
161
|
+
function handleString(schema) {
|
|
162
|
+
if (schema.format) {
|
|
163
|
+
switch (schema.format) {
|
|
164
|
+
case 'email':
|
|
165
|
+
case 'idn-email':
|
|
166
|
+
return 'user@example.com';
|
|
167
|
+
case 'uri':
|
|
168
|
+
case 'url':
|
|
169
|
+
case 'iri':
|
|
170
|
+
return 'https://example.com';
|
|
171
|
+
case 'date':
|
|
172
|
+
return '2023-01-01';
|
|
173
|
+
case 'date-time':
|
|
174
|
+
return '2023-01-01T00:00:00Z';
|
|
175
|
+
case 'time':
|
|
176
|
+
return '12:00:00Z';
|
|
177
|
+
case 'duration':
|
|
178
|
+
return 'PT1H';
|
|
179
|
+
case 'uuid':
|
|
180
|
+
return '00000000-0000-0000-0000-000000000000';
|
|
181
|
+
case 'regex':
|
|
182
|
+
return '^[a-zA-Z0-9]+$';
|
|
183
|
+
case 'relative-json-pointer':
|
|
184
|
+
return '/some/relative/path';
|
|
185
|
+
case 'color':
|
|
186
|
+
return '#000000';
|
|
187
|
+
case 'hostname':
|
|
188
|
+
return 'example.com';
|
|
189
|
+
case 'zipcode':
|
|
190
|
+
return '12345';
|
|
191
|
+
case 'phone':
|
|
192
|
+
return '+123-456-7890';
|
|
193
|
+
case 'password':
|
|
194
|
+
return '******';
|
|
195
|
+
case 'binary':
|
|
196
|
+
return 'binary-data';
|
|
197
|
+
default:
|
|
198
|
+
return 'string';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (schema.pattern) {
|
|
202
|
+
return 'pattern-string';
|
|
203
|
+
}
|
|
204
|
+
return 'string';
|
|
205
|
+
}
|
|
206
|
+
function handleNumber(schema) {
|
|
207
|
+
if (schema.minimum !== undefined && schema.maximum !== undefined) {
|
|
208
|
+
return schema.minimum;
|
|
209
|
+
}
|
|
210
|
+
else if (schema.minimum !== undefined) {
|
|
211
|
+
return schema.minimum;
|
|
212
|
+
}
|
|
213
|
+
else if (schema.maximum !== undefined) {
|
|
214
|
+
return schema.maximum;
|
|
215
|
+
}
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
function handleBoolean() {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
function handleObject(schema, rootSchema, ignoreBinary) {
|
|
222
|
+
const result = {};
|
|
223
|
+
if (schema.properties) {
|
|
224
|
+
const required = schema.required || [];
|
|
225
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
226
|
+
if (required.includes(key) || required.length === 0) {
|
|
227
|
+
const value = getSampleValue(propSchema, rootSchema, ignoreBinary);
|
|
228
|
+
// Only add the property if it's not undefined (which happens when ignoreBinary is true and it's a binary field)
|
|
229
|
+
if (value !== undefined) {
|
|
230
|
+
result[key] = value;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
236
|
+
const value = getSampleValue(schema.additionalProperties, rootSchema, ignoreBinary);
|
|
237
|
+
if (value !== undefined) {
|
|
238
|
+
result.additionalProp = value;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
function handleArray(schema, rootSchema, ignoreBinary) {
|
|
244
|
+
if (schema.items) {
|
|
245
|
+
// If items is a boolean, return empty array (true means any items allowed, false means no items)
|
|
246
|
+
if (typeof schema.items === 'boolean') {
|
|
247
|
+
return schema.items ? [null] : [];
|
|
248
|
+
}
|
|
249
|
+
const itemSchema = schema.items;
|
|
250
|
+
// Check if the items are binary strings that should be ignored
|
|
251
|
+
if (ignoreBinary && itemSchema.type === 'string' && itemSchema.format === 'binary') {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
const minItems = schema.minItems || 1;
|
|
255
|
+
const numItems = Math.min(minItems, 3);
|
|
256
|
+
const items = Array.from({ length: numItems }, () => getSampleValue(itemSchema, rootSchema, ignoreBinary)).filter((item) => item !== undefined); // Filter out undefined values from ignored binary items
|
|
257
|
+
// If all items were filtered out (e.g., all were binary), return undefined instead of empty array
|
|
258
|
+
if (items.length === 0 && numItems > 0) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
return items;
|
|
262
|
+
}
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
export function schemaToObject(schema, rootSchema) {
|
|
2
|
+
if (!schema || typeof schema !== 'object')
|
|
3
|
+
return null;
|
|
4
|
+
// Use the input schema as the root if not provided
|
|
5
|
+
rootSchema = rootSchema || schema;
|
|
6
|
+
// If there's an example, use it
|
|
7
|
+
if (schema.example !== undefined) {
|
|
8
|
+
return schema.example;
|
|
9
|
+
}
|
|
10
|
+
// If there are examples, use one of them
|
|
11
|
+
if (schema.examples && schema.examples.length > 0) {
|
|
12
|
+
return schema.examples[0];
|
|
13
|
+
}
|
|
14
|
+
// Handle const if present
|
|
15
|
+
if (schema.const !== undefined) {
|
|
16
|
+
return schema.const;
|
|
17
|
+
}
|
|
18
|
+
// Handle $ref if present
|
|
19
|
+
if (schema.$ref) {
|
|
20
|
+
return handleRef(schema.$ref, rootSchema);
|
|
21
|
+
}
|
|
22
|
+
// Handle enum if present
|
|
23
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
24
|
+
return schema.enum[0];
|
|
25
|
+
}
|
|
26
|
+
// Handle oneOf, anyOf, allOf
|
|
27
|
+
if (schema.oneOf && schema.oneOf.length > 0) {
|
|
28
|
+
return schemaToObject(schema.oneOf[0], rootSchema);
|
|
29
|
+
}
|
|
30
|
+
if (schema.anyOf && schema.anyOf.length > 0) {
|
|
31
|
+
return schemaToObject(schema.anyOf[0], rootSchema);
|
|
32
|
+
}
|
|
33
|
+
if (schema.allOf && schema.allOf.length > 0) {
|
|
34
|
+
// Merge all schemas in allOf
|
|
35
|
+
const mergedSchema = schema.allOf.reduce((acc, s) => Object.assign(acc, s), {});
|
|
36
|
+
return schemaToObject(mergedSchema, rootSchema);
|
|
37
|
+
}
|
|
38
|
+
// Handle different types
|
|
39
|
+
if (schema.type) {
|
|
40
|
+
switch (schema.type) {
|
|
41
|
+
case 'string':
|
|
42
|
+
return handleString(schema);
|
|
43
|
+
case 'number':
|
|
44
|
+
case 'integer':
|
|
45
|
+
return handleNumber(schema);
|
|
46
|
+
case 'boolean':
|
|
47
|
+
return handleBoolean();
|
|
48
|
+
case 'object':
|
|
49
|
+
return handleObject(schema, rootSchema);
|
|
50
|
+
case 'array':
|
|
51
|
+
return handleArray(schema, rootSchema);
|
|
52
|
+
case 'null':
|
|
53
|
+
return null;
|
|
54
|
+
default:
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// If type is not specified but properties are, treat it as an object
|
|
59
|
+
if (schema.properties) {
|
|
60
|
+
return handleObject(schema, rootSchema);
|
|
61
|
+
}
|
|
62
|
+
// Default fallback
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function handleRef(ref, rootSchema) {
|
|
66
|
+
// Parse the reference path
|
|
67
|
+
const path = ref.split('/').slice(1); // Remove the initial '#'
|
|
68
|
+
// Navigate through the schema to find the referenced definition
|
|
69
|
+
let current = rootSchema;
|
|
70
|
+
for (const segment of path) {
|
|
71
|
+
current = current[segment];
|
|
72
|
+
if (current === undefined) {
|
|
73
|
+
return null; // Reference not found
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Process the referenced schema
|
|
77
|
+
return schemaToObject(current, rootSchema);
|
|
78
|
+
}
|
|
79
|
+
function handleString(schema) {
|
|
80
|
+
if (schema.format) {
|
|
81
|
+
switch (schema.format) {
|
|
82
|
+
case 'email':
|
|
83
|
+
case 'idn-email':
|
|
84
|
+
return 'user@example.com';
|
|
85
|
+
case 'uri':
|
|
86
|
+
case 'url':
|
|
87
|
+
case 'iri':
|
|
88
|
+
return 'https://example.com';
|
|
89
|
+
case 'date':
|
|
90
|
+
return '2023-01-01';
|
|
91
|
+
case 'date-time':
|
|
92
|
+
return '2023-01-01T00:00:00Z';
|
|
93
|
+
case 'time':
|
|
94
|
+
return '12:00:00Z';
|
|
95
|
+
case 'duration':
|
|
96
|
+
return 'PT1H';
|
|
97
|
+
case 'uuid':
|
|
98
|
+
return '00000000-0000-0000-0000-000000000000';
|
|
99
|
+
case 'regex':
|
|
100
|
+
return '^[a-zA-Z0-9]+$';
|
|
101
|
+
case 'relative-json-pointer':
|
|
102
|
+
return '/some/relative/path';
|
|
103
|
+
case 'color':
|
|
104
|
+
return '#000000';
|
|
105
|
+
case 'hostname':
|
|
106
|
+
return 'example.com';
|
|
107
|
+
case 'zipcode':
|
|
108
|
+
return '12345';
|
|
109
|
+
case 'phone':
|
|
110
|
+
return '+123-456-7890';
|
|
111
|
+
case 'password':
|
|
112
|
+
return '******';
|
|
113
|
+
default:
|
|
114
|
+
return 'string';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (schema.pattern) {
|
|
118
|
+
// For simplicity, return a basic string for patterns
|
|
119
|
+
return 'pattern-string';
|
|
120
|
+
}
|
|
121
|
+
return 'string';
|
|
122
|
+
}
|
|
123
|
+
function handleNumber(schema) {
|
|
124
|
+
if (schema.minimum !== undefined && schema.maximum !== undefined) {
|
|
125
|
+
return schema.minimum;
|
|
126
|
+
}
|
|
127
|
+
else if (schema.minimum !== undefined) {
|
|
128
|
+
return schema.minimum;
|
|
129
|
+
}
|
|
130
|
+
else if (schema.maximum !== undefined) {
|
|
131
|
+
return schema.maximum;
|
|
132
|
+
}
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
function handleBoolean() {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
function handleObject(schema, rootSchema) {
|
|
139
|
+
const result = {};
|
|
140
|
+
if (schema.properties) {
|
|
141
|
+
const required = schema.required || [];
|
|
142
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
143
|
+
// Only include required properties or as a basic example
|
|
144
|
+
if (required.includes(key) || required.length === 0) {
|
|
145
|
+
result[key] = schemaToObject(propSchema, rootSchema);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Handle additionalProperties
|
|
150
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
|
|
151
|
+
result.additionalProp = schemaToObject(schema.additionalProperties, rootSchema);
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
function handleArray(schema, rootSchema) {
|
|
156
|
+
if (schema.items && typeof schema.items === 'object') {
|
|
157
|
+
const itemSchema = schema.items;
|
|
158
|
+
const minItems = schema.minItems || 1;
|
|
159
|
+
// Create minimum number of items (capped at a reasonable max for examples)
|
|
160
|
+
const numItems = Math.min(minItems, 3);
|
|
161
|
+
return Array.from({ length: numItems }, () => schemaToObject(itemSchema, rootSchema));
|
|
162
|
+
}
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export function schemaToTsType(jsonSchema) {
|
|
2
|
+
if (jsonSchema === true)
|
|
3
|
+
return 'unknown';
|
|
4
|
+
if (jsonSchema === false)
|
|
5
|
+
return 'never';
|
|
6
|
+
if (jsonSchema === null || jsonSchema === undefined)
|
|
7
|
+
return 'unknown';
|
|
8
|
+
if (typeof jsonSchema !== 'object')
|
|
9
|
+
return 'unknown';
|
|
10
|
+
// Handle const
|
|
11
|
+
if ('const' in jsonSchema) {
|
|
12
|
+
return JSON.stringify(jsonSchema.const);
|
|
13
|
+
}
|
|
14
|
+
// Handle enum
|
|
15
|
+
if (jsonSchema.enum) {
|
|
16
|
+
return jsonSchema.enum.map((v) => JSON.stringify(v)).join(' | ') || 'never';
|
|
17
|
+
}
|
|
18
|
+
// Handle allOf (intersection)
|
|
19
|
+
if (jsonSchema.allOf) {
|
|
20
|
+
const parts = jsonSchema.allOf.map((s) => schemaToTsType(s));
|
|
21
|
+
return parts.length ? `(${parts.join(' & ')})` : 'unknown';
|
|
22
|
+
}
|
|
23
|
+
// Handle anyOf (union)
|
|
24
|
+
if (jsonSchema.anyOf) {
|
|
25
|
+
const parts = jsonSchema.anyOf.map((s) => schemaToTsType(s));
|
|
26
|
+
return parts.length ? `(${parts.join(' | ')})` : 'never';
|
|
27
|
+
}
|
|
28
|
+
// Handle oneOf (union)
|
|
29
|
+
if (jsonSchema.oneOf) {
|
|
30
|
+
const parts = jsonSchema.oneOf.map((s) => schemaToTsType(s));
|
|
31
|
+
return parts.length ? `(${parts.join(' | ')})` : 'never';
|
|
32
|
+
}
|
|
33
|
+
// Handle not (negate - approximate as unknown)
|
|
34
|
+
if (jsonSchema.not) {
|
|
35
|
+
return 'unknown';
|
|
36
|
+
}
|
|
37
|
+
// Handle type as array (union of types)
|
|
38
|
+
if (Array.isArray(jsonSchema.type)) {
|
|
39
|
+
const types = jsonSchema.type.map((t) => schemaToTsType({ ...jsonSchema, type: t }));
|
|
40
|
+
return types.length ? `(${types.join(' | ')})` : 'unknown';
|
|
41
|
+
}
|
|
42
|
+
const type = jsonSchema.type;
|
|
43
|
+
// Primitives
|
|
44
|
+
if (type === 'string')
|
|
45
|
+
return 'string';
|
|
46
|
+
if (type === 'number' || type === 'integer')
|
|
47
|
+
return 'number';
|
|
48
|
+
if (type === 'boolean')
|
|
49
|
+
return 'boolean';
|
|
50
|
+
if (type === 'null')
|
|
51
|
+
return 'null';
|
|
52
|
+
// Object
|
|
53
|
+
if (type === 'object' || jsonSchema.properties || jsonSchema.additionalProperties !== undefined) {
|
|
54
|
+
const props = jsonSchema.properties || {};
|
|
55
|
+
const required = jsonSchema.required || [];
|
|
56
|
+
const propEntries = Object.entries(props);
|
|
57
|
+
const propStrings = propEntries.map(([key, value]) => {
|
|
58
|
+
const isRequired = required.includes(key);
|
|
59
|
+
const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
60
|
+
return `${safeName}${isRequired ? '' : '?'}: ${schemaToTsType(value)}`;
|
|
61
|
+
});
|
|
62
|
+
// Handle additionalProperties
|
|
63
|
+
let additionalType = null;
|
|
64
|
+
if (jsonSchema.additionalProperties === true) {
|
|
65
|
+
additionalType = 'unknown';
|
|
66
|
+
}
|
|
67
|
+
else if (jsonSchema.additionalProperties && typeof jsonSchema.additionalProperties === 'object') {
|
|
68
|
+
additionalType = schemaToTsType(jsonSchema.additionalProperties);
|
|
69
|
+
}
|
|
70
|
+
if (propStrings.length === 0 && additionalType) {
|
|
71
|
+
return `{ [key: string]: ${additionalType} }`;
|
|
72
|
+
}
|
|
73
|
+
if (propStrings.length === 0 && !additionalType) {
|
|
74
|
+
return jsonSchema.additionalProperties === false ? '{}' : '{ [key: string]: unknown }';
|
|
75
|
+
}
|
|
76
|
+
let result = `{ ${propStrings.join('; ')} }`;
|
|
77
|
+
if (additionalType) {
|
|
78
|
+
result = `(${result} & { [key: string]: ${additionalType} })`;
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
// Array
|
|
83
|
+
if (type === 'array' || jsonSchema.items || jsonSchema.prefixItems) {
|
|
84
|
+
// Tuple (prefixItems)
|
|
85
|
+
if (jsonSchema.prefixItems) {
|
|
86
|
+
const tupleTypes = jsonSchema.prefixItems.map((s) => schemaToTsType(s));
|
|
87
|
+
if (jsonSchema.items === false) {
|
|
88
|
+
return `[${tupleTypes.join(', ')}]`;
|
|
89
|
+
}
|
|
90
|
+
const restType = jsonSchema.items ? schemaToTsType(jsonSchema.items) : 'unknown';
|
|
91
|
+
return `[${tupleTypes.join(', ')}, ...${restType}[]]`;
|
|
92
|
+
}
|
|
93
|
+
// Regular array
|
|
94
|
+
if (jsonSchema.items) {
|
|
95
|
+
if (Array.isArray(jsonSchema.items)) {
|
|
96
|
+
// Legacy tuple syntax
|
|
97
|
+
const tupleTypes = jsonSchema.items.map((s) => schemaToTsType(s));
|
|
98
|
+
if (jsonSchema.additionalItems === false) {
|
|
99
|
+
return `[${tupleTypes.join(', ')}]`;
|
|
100
|
+
}
|
|
101
|
+
const restType = jsonSchema.additionalItems ? schemaToTsType(jsonSchema.additionalItems) : 'unknown';
|
|
102
|
+
return `[${tupleTypes.join(', ')}, ...${restType}[]]`;
|
|
103
|
+
}
|
|
104
|
+
return `${schemaToTsType(jsonSchema.items)}[]`;
|
|
105
|
+
}
|
|
106
|
+
return 'unknown[]';
|
|
107
|
+
}
|
|
108
|
+
// No type specified - try to infer from structure
|
|
109
|
+
if (jsonSchema.properties) {
|
|
110
|
+
return schemaToTsType({ ...jsonSchema, type: 'object' });
|
|
111
|
+
}
|
|
112
|
+
// Fallback for empty or unknown schema
|
|
113
|
+
return 'unknown';
|
|
114
|
+
}
|