vovk 3.0.0-draft.46 → 3.0.0-draft.460
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/LICENSE +1 -1
- package/README.md +8 -96
- package/bin/index.mjs +8 -0
- package/cjs/JSONLinesResponse.d.ts +19 -0
- package/cjs/JSONLinesResponse.js +95 -0
- package/{VovkApp.d.ts → cjs/VovkApp.d.ts} +14 -8
- package/cjs/VovkApp.js +304 -0
- package/cjs/client/createRPC.d.ts +5 -0
- package/cjs/client/createRPC.js +116 -0
- package/cjs/client/defaultHandler.d.ts +6 -0
- package/{client → cjs/client}/defaultHandler.js +9 -2
- package/cjs/client/defaultStreamHandler.d.ts +9 -0
- package/{client → cjs/client}/defaultStreamHandler.js +49 -26
- package/cjs/client/fetcher.d.ts +18 -0
- package/cjs/client/fetcher.js +97 -0
- package/cjs/client/index.d.ts +4 -0
- package/cjs/client/index.js +10 -0
- package/cjs/client/progressive.d.ts +9 -0
- package/cjs/client/progressive.js +54 -0
- package/cjs/client/types.d.ts +120 -0
- package/{createVovkApp.d.ts → cjs/createVovkApp.d.ts} +19 -16
- package/cjs/createVovkApp.js +146 -0
- package/cjs/index.d.ts +69 -0
- package/cjs/index.js +42 -0
- package/cjs/openapi/error.d.ts +2 -0
- package/cjs/openapi/error.js +100 -0
- package/cjs/openapi/index.d.ts +8 -0
- package/cjs/openapi/index.js +21 -0
- package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
- package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
- package/cjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
- package/cjs/openapi/openAPIToVovkSchema/index.js +141 -0
- package/cjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
- package/cjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
- package/cjs/openapi/vovkSchemaToOpenAPI.d.ts +8 -0
- package/cjs/openapi/vovkSchemaToOpenAPI.js +238 -0
- package/cjs/types.d.ts +466 -0
- package/{types.js → cjs/types.js} +14 -2
- package/cjs/utils/camelCase.d.ts +6 -0
- package/cjs/utils/camelCase.js +37 -0
- package/cjs/utils/createCodeSamples.d.ts +20 -0
- package/cjs/utils/createCodeSamples.js +279 -0
- package/cjs/utils/createDecorator.d.ts +6 -0
- package/{createDecorator.js → cjs/utils/createDecorator.js} +24 -14
- package/cjs/utils/createLLMTools.d.ts +50 -0
- package/cjs/utils/createLLMTools.js +168 -0
- package/cjs/utils/createStandardValidation.d.ts +82 -0
- package/cjs/utils/createStandardValidation.js +34 -0
- package/cjs/utils/createValidateOnClient.d.ts +7 -0
- package/cjs/utils/createValidateOnClient.js +19 -0
- package/cjs/utils/deepExtend.d.ts +54 -0
- package/cjs/utils/deepExtend.js +134 -0
- package/{utils → cjs/utils}/generateStaticAPI.d.ts +2 -2
- package/cjs/utils/generateStaticAPI.js +30 -0
- package/cjs/utils/getJSONSchemaExample.d.ts +11 -0
- package/cjs/utils/getJSONSchemaExample.js +264 -0
- package/cjs/utils/getJSONSchemaSample.d.ts +2 -0
- package/cjs/utils/getJSONSchemaSample.js +167 -0
- package/cjs/utils/getSampleFromObject.d.ts +9 -0
- package/cjs/utils/getSampleFromObject.js +41 -0
- package/cjs/utils/getSchema.d.ts +21 -0
- package/cjs/utils/getSchema.js +38 -0
- package/cjs/utils/multitenant.d.ts +24 -0
- package/cjs/utils/multitenant.js +131 -0
- package/cjs/utils/parseQuery.d.ts +25 -0
- package/cjs/utils/parseQuery.js +156 -0
- package/cjs/utils/reqForm.d.ts +2 -0
- package/cjs/utils/reqForm.js +33 -0
- package/cjs/utils/reqMeta.d.ts +2 -0
- package/cjs/utils/reqQuery.d.ts +2 -0
- package/cjs/utils/reqQuery.js +10 -0
- package/cjs/utils/resolveGeneratorConfigValues.d.ts +18 -0
- package/cjs/utils/resolveGeneratorConfigValues.js +82 -0
- package/cjs/utils/serializeQuery.d.ts +13 -0
- package/cjs/utils/serializeQuery.js +65 -0
- package/cjs/utils/setHandlerSchema.d.ts +4 -0
- package/cjs/utils/setHandlerSchema.js +15 -0
- package/cjs/utils/upperFirst.d.ts +1 -0
- package/cjs/utils/upperFirst.js +6 -0
- package/cjs/utils/withValidationLibrary.d.ts +77 -0
- package/cjs/utils/withValidationLibrary.js +124 -0
- package/mjs/HttpException.d.ts +7 -0
- package/mjs/HttpException.js +15 -0
- package/mjs/JSONLinesResponse.d.ts +19 -0
- package/mjs/JSONLinesResponse.js +95 -0
- package/mjs/VovkApp.d.ts +34 -0
- package/mjs/VovkApp.js +304 -0
- package/mjs/client/createRPC.d.ts +5 -0
- package/mjs/client/createRPC.js +116 -0
- package/mjs/client/defaultHandler.d.ts +6 -0
- package/mjs/client/defaultHandler.js +29 -0
- package/mjs/client/defaultStreamHandler.d.ts +9 -0
- package/mjs/client/defaultStreamHandler.js +105 -0
- package/mjs/client/fetcher.d.ts +18 -0
- package/mjs/client/fetcher.js +97 -0
- package/mjs/client/index.d.ts +4 -0
- package/mjs/client/index.js +10 -0
- package/mjs/client/progressive.d.ts +9 -0
- package/mjs/client/progressive.js +54 -0
- package/mjs/client/types.d.ts +120 -0
- package/mjs/createVovkApp.d.ts +65 -0
- package/mjs/createVovkApp.js +146 -0
- package/mjs/index.d.ts +69 -0
- package/mjs/index.js +42 -0
- package/mjs/openapi/error.d.ts +2 -0
- package/mjs/openapi/error.js +100 -0
- package/mjs/openapi/index.d.ts +8 -0
- package/mjs/openapi/index.js +21 -0
- package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
- package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
- package/mjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
- package/mjs/openapi/openAPIToVovkSchema/index.js +141 -0
- package/mjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
- package/mjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
- package/mjs/openapi/vovkSchemaToOpenAPI.d.ts +8 -0
- package/mjs/openapi/vovkSchemaToOpenAPI.js +238 -0
- package/mjs/types.d.ts +466 -0
- package/mjs/types.js +77 -0
- package/mjs/utils/camelCase.d.ts +6 -0
- package/mjs/utils/camelCase.js +37 -0
- package/mjs/utils/createCodeSamples.d.ts +20 -0
- package/mjs/utils/createCodeSamples.js +279 -0
- package/mjs/utils/createDecorator.d.ts +6 -0
- package/mjs/utils/createDecorator.js +48 -0
- package/mjs/utils/createLLMTools.d.ts +50 -0
- package/mjs/utils/createLLMTools.js +168 -0
- package/mjs/utils/createStandardValidation.d.ts +82 -0
- package/mjs/utils/createStandardValidation.js +34 -0
- package/mjs/utils/createValidateOnClient.d.ts +7 -0
- package/mjs/utils/createValidateOnClient.js +19 -0
- package/mjs/utils/deepExtend.d.ts +54 -0
- package/mjs/utils/deepExtend.js +134 -0
- package/mjs/utils/generateStaticAPI.d.ts +4 -0
- package/mjs/utils/generateStaticAPI.js +30 -0
- package/mjs/utils/getJSONSchemaExample.d.ts +11 -0
- package/mjs/utils/getJSONSchemaExample.js +264 -0
- package/mjs/utils/getJSONSchemaSample.d.ts +2 -0
- package/mjs/utils/getJSONSchemaSample.js +167 -0
- package/mjs/utils/getSampleFromObject.d.ts +9 -0
- package/mjs/utils/getSampleFromObject.js +41 -0
- package/mjs/utils/getSchema.d.ts +21 -0
- package/mjs/utils/getSchema.js +38 -0
- package/mjs/utils/multitenant.d.ts +24 -0
- package/mjs/utils/multitenant.js +131 -0
- package/mjs/utils/parseQuery.d.ts +25 -0
- package/mjs/utils/parseQuery.js +156 -0
- package/mjs/utils/reqForm.d.ts +2 -0
- package/mjs/utils/reqForm.js +33 -0
- package/mjs/utils/reqMeta.d.ts +2 -0
- package/mjs/utils/reqMeta.js +13 -0
- package/mjs/utils/reqQuery.d.ts +2 -0
- package/mjs/utils/reqQuery.js +10 -0
- package/mjs/utils/resolveGeneratorConfigValues.d.ts +18 -0
- package/mjs/utils/resolveGeneratorConfigValues.js +82 -0
- package/mjs/utils/serializeQuery.d.ts +13 -0
- package/mjs/utils/serializeQuery.js +65 -0
- package/mjs/utils/setHandlerSchema.d.ts +4 -0
- package/mjs/utils/setHandlerSchema.js +15 -0
- package/mjs/utils/shim.d.ts +1 -0
- package/mjs/utils/shim.js +18 -0
- package/mjs/utils/upperFirst.d.ts +1 -0
- package/mjs/utils/upperFirst.js +6 -0
- package/mjs/utils/withValidationLibrary.d.ts +77 -0
- package/mjs/utils/withValidationLibrary.js +124 -0
- package/package.json +28 -6
- package/.npmignore +0 -2
- package/StreamJSONResponse.d.ts +0 -17
- package/StreamJSONResponse.js +0 -54
- package/VovkApp.js +0 -185
- package/client/clientizeController.d.ts +0 -4
- package/client/clientizeController.js +0 -93
- package/client/defaultFetcher.d.ts +0 -4
- package/client/defaultFetcher.js +0 -49
- package/client/defaultHandler.d.ts +0 -2
- package/client/defaultStreamHandler.d.ts +0 -4
- package/client/index.d.ts +0 -4
- package/client/index.js +0 -5
- package/client/types.d.ts +0 -100
- package/createDecorator.d.ts +0 -4
- package/createVovkApp.js +0 -118
- package/index.d.ts +0 -60
- package/index.js +0 -20
- package/types.d.ts +0 -157
- package/utils/generateStaticAPI.js +0 -18
- package/utils/getSchema.d.ts +0 -8
- package/utils/getSchema.js +0 -38
- package/utils/reqForm.d.ts +0 -3
- package/utils/reqForm.js +0 -13
- package/utils/reqMeta.d.ts +0 -3
- package/utils/reqQuery.d.ts +0 -3
- package/utils/reqQuery.js +0 -25
- package/utils/setClientValidatorsForHandler.d.ts +0 -5
- package/utils/setClientValidatorsForHandler.js +0 -25
- package/worker/index.d.ts +0 -3
- package/worker/index.js +0 -7
- package/worker/promisifyWorker.d.ts +0 -2
- package/worker/promisifyWorker.js +0 -141
- package/worker/types.d.ts +0 -31
- package/worker/worker.d.ts +0 -1
- package/worker/worker.js +0 -43
- /package/{HttpException.d.ts → cjs/HttpException.d.ts} +0 -0
- /package/{HttpException.js → cjs/HttpException.js} +0 -0
- /package/{client → cjs/client}/types.js +0 -0
- /package/{utils → cjs/utils}/reqMeta.js +0 -0
- /package/{utils → cjs/utils}/shim.d.ts +0 -0
- /package/{utils → cjs/utils}/shim.js +0 -0
- /package/{worker → mjs/client}/types.js +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.openAPIToVovkSchema = openAPIToVovkSchema;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const applyComponentsSchemas_1 = require("./applyComponentsSchemas");
|
|
6
|
+
const inlineRefs_1 = require("./inlineRefs");
|
|
7
|
+
function openAPIToVovkSchema({ apiRoot, source: { object: openAPIObject }, getModuleName, getMethodName, errorMessageKey, segmentName, }) {
|
|
8
|
+
segmentName = segmentName ?? '';
|
|
9
|
+
const forceApiRoot = apiRoot ||
|
|
10
|
+
(openAPIObject.servers?.[0]?.url ??
|
|
11
|
+
('host' in openAPIObject
|
|
12
|
+
? `https://${openAPIObject.host}${'basePath' in openAPIObject ? openAPIObject.basePath : ''}`
|
|
13
|
+
: null));
|
|
14
|
+
if (!forceApiRoot) {
|
|
15
|
+
throw new Error('API root URL is required in OpenAPI configuration');
|
|
16
|
+
}
|
|
17
|
+
const { paths, ...noPathsOpenAPIObject } = openAPIObject;
|
|
18
|
+
const schema = {
|
|
19
|
+
$schema: types_1.VovkSchemaIdEnum.SCHEMA,
|
|
20
|
+
segments: {
|
|
21
|
+
[segmentName]: {
|
|
22
|
+
$schema: types_1.VovkSchemaIdEnum.SEGMENT,
|
|
23
|
+
emitSchema: true,
|
|
24
|
+
segmentName,
|
|
25
|
+
segmentType: 'mixin',
|
|
26
|
+
controllers: {},
|
|
27
|
+
forceApiRoot,
|
|
28
|
+
meta: {
|
|
29
|
+
openAPIObject: noPathsOpenAPIObject,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
const segment = schema.segments[segmentName];
|
|
35
|
+
return Object.entries(paths ?? {}).reduce((acc, [path, operations]) => {
|
|
36
|
+
Object.entries(operations ?? {})
|
|
37
|
+
.filter(([, operation]) => operation && typeof operation === 'object')
|
|
38
|
+
.forEach(([method, operation]) => {
|
|
39
|
+
const rpcModuleName = getModuleName({
|
|
40
|
+
method: method.toUpperCase(),
|
|
41
|
+
path,
|
|
42
|
+
openAPIObject,
|
|
43
|
+
operationObject: operation,
|
|
44
|
+
});
|
|
45
|
+
const handlerName = getMethodName({
|
|
46
|
+
method: method.toUpperCase(),
|
|
47
|
+
path,
|
|
48
|
+
openAPIObject,
|
|
49
|
+
operationObject: operation,
|
|
50
|
+
});
|
|
51
|
+
segment.controllers[rpcModuleName] ??= {
|
|
52
|
+
rpcModuleName,
|
|
53
|
+
handlers: {},
|
|
54
|
+
};
|
|
55
|
+
const parameters = (0, inlineRefs_1.inlineRefs)(operation.parameters ?? [], openAPIObject);
|
|
56
|
+
const queryProperties = parameters.filter((p) => p.in === 'query') ?? null;
|
|
57
|
+
const pathProperties = parameters.filter((p) => p.in === 'path') ?? null;
|
|
58
|
+
const query = queryProperties?.length
|
|
59
|
+
? {
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: Object.fromEntries(queryProperties.map((p) => [p.name, p.schema])),
|
|
62
|
+
required: queryProperties.filter((p) => p.required).map((p) => p.name),
|
|
63
|
+
}
|
|
64
|
+
: null;
|
|
65
|
+
const params = pathProperties?.length
|
|
66
|
+
? {
|
|
67
|
+
type: 'object',
|
|
68
|
+
properties: Object.fromEntries(pathProperties.map((p) => [p.name, p.schema])),
|
|
69
|
+
required: pathProperties.filter((p) => p.required).map((p) => p.name),
|
|
70
|
+
}
|
|
71
|
+
: null;
|
|
72
|
+
// TODO: how to utilize ReferenceObject?
|
|
73
|
+
const requestBodyContent = (0, inlineRefs_1.inlineRefs)(operation.requestBody, openAPIObject)?.content ?? {};
|
|
74
|
+
const jsonBody = requestBodyContent['application/json']?.schema ?? null;
|
|
75
|
+
const formDataBody = requestBodyContent['multipart/form-data']?.schema ?? null;
|
|
76
|
+
let urlEncodedBody = requestBodyContent['application/x-www-form-urlencoded']?.schema ?? null;
|
|
77
|
+
if (formDataBody && urlEncodedBody && JSON.stringify(formDataBody) === JSON.stringify(urlEncodedBody)) {
|
|
78
|
+
urlEncodedBody = null; // Avoid duplication if both form-data and url-encoded bodies are the same
|
|
79
|
+
}
|
|
80
|
+
if (formDataBody) {
|
|
81
|
+
Object.assign(formDataBody, {
|
|
82
|
+
'x-isForm': true,
|
|
83
|
+
'x-tsType': 'FormData',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (urlEncodedBody) {
|
|
87
|
+
Object.assign(urlEncodedBody, {
|
|
88
|
+
'x-isForm': true,
|
|
89
|
+
'x-tsType': 'FormData',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const bodySchemas = [jsonBody, formDataBody, urlEncodedBody].filter(Boolean);
|
|
93
|
+
const body = !bodySchemas.length
|
|
94
|
+
? null
|
|
95
|
+
: bodySchemas.length === 1
|
|
96
|
+
? bodySchemas[0]
|
|
97
|
+
: {
|
|
98
|
+
anyOf: bodySchemas,
|
|
99
|
+
};
|
|
100
|
+
const output = operation.responses?.['200']?.content?.['application/json']?.schema ??
|
|
101
|
+
operation.responses?.['201']?.content?.['application/json']?.schema ??
|
|
102
|
+
null;
|
|
103
|
+
const iteration = operation.responses?.['200']?.content?.['application/jsonl']?.schema ??
|
|
104
|
+
operation.responses?.['201']?.content?.['application/jsonl']?.schema ??
|
|
105
|
+
operation.responses?.['200']?.content?.['application/jsonlines']?.schema ??
|
|
106
|
+
operation.responses?.['201']?.content?.['application/jsonlines']?.schema ??
|
|
107
|
+
null;
|
|
108
|
+
if (errorMessageKey) {
|
|
109
|
+
operation['x-errorMessageKey'] = errorMessageKey;
|
|
110
|
+
}
|
|
111
|
+
const componentsSchemas = openAPIObject.components?.schemas ??
|
|
112
|
+
('definitions' in openAPIObject ? openAPIObject.definitions : {});
|
|
113
|
+
segment.controllers[rpcModuleName].handlers[handlerName] = {
|
|
114
|
+
httpMethod: method.toUpperCase(),
|
|
115
|
+
path,
|
|
116
|
+
operationObject: operation,
|
|
117
|
+
misc: {
|
|
118
|
+
originalPath: path,
|
|
119
|
+
},
|
|
120
|
+
validation: {
|
|
121
|
+
...(query && {
|
|
122
|
+
query: (0, applyComponentsSchemas_1.applyComponentsSchemas)(query, componentsSchemas, segmentName),
|
|
123
|
+
}),
|
|
124
|
+
...(params && {
|
|
125
|
+
params: (0, applyComponentsSchemas_1.applyComponentsSchemas)(params, componentsSchemas, segmentName),
|
|
126
|
+
}),
|
|
127
|
+
...(body && {
|
|
128
|
+
body: (0, applyComponentsSchemas_1.applyComponentsSchemas)(body, componentsSchemas, segmentName),
|
|
129
|
+
}),
|
|
130
|
+
...(output && {
|
|
131
|
+
output: (0, applyComponentsSchemas_1.applyComponentsSchemas)(output, componentsSchemas, segmentName),
|
|
132
|
+
}),
|
|
133
|
+
...(iteration && {
|
|
134
|
+
iteration: (0, applyComponentsSchemas_1.applyComponentsSchemas)(iteration, componentsSchemas, segmentName),
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
return acc;
|
|
140
|
+
}, schema);
|
|
141
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
2
|
+
import { KnownAny } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves $ref references at the first level only (except for components/schemas references)
|
|
5
|
+
* For arrays, checks each item at the first level
|
|
6
|
+
* @param obj - The object to process (may contain $ref properties)
|
|
7
|
+
* @param openAPIObject - The complete OpenAPI document containing definitions
|
|
8
|
+
* @returns The object with resolved references (except components/schemas)
|
|
9
|
+
*/
|
|
10
|
+
export declare function inlineRefs<T extends object>(obj: KnownAny, openAPIObject: OpenAPIObject): T;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inlineRefs = inlineRefs;
|
|
4
|
+
/**
|
|
5
|
+
* Resolves $ref references at the first level only (except for components/schemas references)
|
|
6
|
+
* For arrays, checks each item at the first level
|
|
7
|
+
* @param obj - The object to process (may contain $ref properties)
|
|
8
|
+
* @param openAPIObject - The complete OpenAPI document containing definitions
|
|
9
|
+
* @returns The object with resolved references (except components/schemas)
|
|
10
|
+
*/
|
|
11
|
+
function inlineRefs(obj, openAPIObject) {
|
|
12
|
+
// Handle null or undefined
|
|
13
|
+
if (obj === null || obj === undefined) {
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
|
16
|
+
// Handle arrays - check each item for $ref at first level only
|
|
17
|
+
if (Array.isArray(obj)) {
|
|
18
|
+
return obj.map((item) => {
|
|
19
|
+
// Only resolve if item is an object with $ref
|
|
20
|
+
if (item && typeof item === 'object' && '$ref' in item && typeof item.$ref === 'string') {
|
|
21
|
+
// Skip components/schemas references
|
|
22
|
+
if (item.$ref.startsWith('#/components/schemas/')) {
|
|
23
|
+
return item;
|
|
24
|
+
}
|
|
25
|
+
// Resolve the reference
|
|
26
|
+
const resolved = resolveRef(item.$ref, openAPIObject);
|
|
27
|
+
// If resolution successful, return resolved value (with any additional properties merged)
|
|
28
|
+
if (resolved !== undefined) {
|
|
29
|
+
// If there are additional properties besides $ref, merge them
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
31
|
+
const { $ref: _$ref, ...additionalProps } = item;
|
|
32
|
+
if (Object.keys(additionalProps).length > 0) {
|
|
33
|
+
return { ...resolved, ...additionalProps };
|
|
34
|
+
}
|
|
35
|
+
return resolved;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Return item as-is if not a resolvable $ref
|
|
39
|
+
return item;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Handle non-objects (primitives)
|
|
43
|
+
if (typeof obj !== 'object') {
|
|
44
|
+
return obj;
|
|
45
|
+
}
|
|
46
|
+
// Check if object has a $ref property at the first level
|
|
47
|
+
if ('$ref' in obj && typeof obj.$ref === 'string') {
|
|
48
|
+
// Check if the reference points to components/schemas
|
|
49
|
+
if (obj.$ref.startsWith('#/components/schemas/')) {
|
|
50
|
+
// Return as-is for schema references
|
|
51
|
+
return obj;
|
|
52
|
+
}
|
|
53
|
+
// Resolve the reference
|
|
54
|
+
const resolved = resolveRef(obj.$ref, openAPIObject);
|
|
55
|
+
// If resolution successful, return resolved value (with any additional properties merged)
|
|
56
|
+
if (resolved !== undefined) {
|
|
57
|
+
// If there are additional properties besides $ref, merge them
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
59
|
+
const { $ref: _$ref, ...additionalProps } = obj;
|
|
60
|
+
if (Object.keys(additionalProps).length > 0) {
|
|
61
|
+
return { ...resolved, ...additionalProps };
|
|
62
|
+
}
|
|
63
|
+
return resolved;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// For regular objects without $ref, return as-is (no recursion)
|
|
67
|
+
return obj;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolves a JSON Reference ($ref) to its target value
|
|
71
|
+
* @param ref - The reference string (e.g., "#/components/parameters/id")
|
|
72
|
+
* @param openAPIObject - The complete OpenAPI document
|
|
73
|
+
* @returns The resolved value or undefined if not found
|
|
74
|
+
*/
|
|
75
|
+
function resolveRef(ref, openAPIObject) {
|
|
76
|
+
// Handle only local references (starting with #)
|
|
77
|
+
if (!ref.startsWith('#/')) {
|
|
78
|
+
// eslint-disable-next-line no-console
|
|
79
|
+
console.warn(`External references are not supported: ${ref}`);
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
// Remove the leading # and split the path
|
|
83
|
+
const path = ref
|
|
84
|
+
.substring(1)
|
|
85
|
+
.split('/')
|
|
86
|
+
.filter((p) => p !== '');
|
|
87
|
+
// Navigate through the object following the path
|
|
88
|
+
let current = openAPIObject;
|
|
89
|
+
for (const segment of path) {
|
|
90
|
+
// Decode the segment (handles encoded characters like ~0 for ~ and ~1 for /)
|
|
91
|
+
const decodedSegment = segment.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
92
|
+
if (current && typeof current === 'object' && decodedSegment in current) {
|
|
93
|
+
current = current[decodedSegment];
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// eslint-disable-next-line no-console
|
|
97
|
+
console.warn(`Could not resolve reference: ${ref}`);
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return current;
|
|
102
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { OpenAPIObject } from 'openapi3-ts/oas31';
|
|
2
|
+
import { type VovkSchema, type VovkGeneratorConfig } from '../types';
|
|
3
|
+
export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, configs, segmentName: givenSegmentName, }: {
|
|
4
|
+
rootEntry?: string;
|
|
5
|
+
schema: VovkSchema;
|
|
6
|
+
configs?: VovkGeneratorConfig[];
|
|
7
|
+
segmentName?: string;
|
|
8
|
+
}): OpenAPIObject;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.vovkSchemaToOpenAPI = vovkSchemaToOpenAPI;
|
|
4
|
+
const createCodeSamples_1 = require("../utils/createCodeSamples");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
const getJSONSchemaSample_1 = require("../utils/getJSONSchemaSample");
|
|
7
|
+
const resolveGeneratorConfigValues_1 = require("../utils/resolveGeneratorConfigValues");
|
|
8
|
+
function extractComponents(schema) {
|
|
9
|
+
if (!schema)
|
|
10
|
+
return [undefined, {}];
|
|
11
|
+
const components = {};
|
|
12
|
+
// Function to collect components and replace $refs recursively
|
|
13
|
+
const process = (obj, path = []) => {
|
|
14
|
+
if (!obj || typeof obj !== 'object')
|
|
15
|
+
return obj;
|
|
16
|
+
// Handle arrays
|
|
17
|
+
if (Array.isArray(obj)) {
|
|
18
|
+
return obj.map((item) => process(item, path));
|
|
19
|
+
}
|
|
20
|
+
// Create a copy to modify
|
|
21
|
+
const result = {};
|
|
22
|
+
Object.entries({ ...obj.definitions, ...obj.$defs }).forEach(([key, value]) => {
|
|
23
|
+
components[key] = process(value, [...path, key]);
|
|
24
|
+
});
|
|
25
|
+
// Process all properties
|
|
26
|
+
for (const [key, value] of Object.entries(obj ?? {})) {
|
|
27
|
+
// Skip already processed special properties
|
|
28
|
+
if (key === '$defs' || key === 'definitions')
|
|
29
|
+
continue;
|
|
30
|
+
if (key === '$ref' && typeof value === 'string') {
|
|
31
|
+
// Extract the component name from the reference
|
|
32
|
+
const refParts = value.split('/');
|
|
33
|
+
const refName = refParts[refParts.length - 1];
|
|
34
|
+
// Replace with component reference
|
|
35
|
+
result[key] = `#/components/schemas/${refName}`;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Recursively process other properties
|
|
39
|
+
result[key] = process(value, [...path, key]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
const processedSchema = process(schema);
|
|
45
|
+
return [processedSchema, components];
|
|
46
|
+
}
|
|
47
|
+
function vovkSchemaToOpenAPI({ rootEntry = 'api', schema: fullSchema, configs, segmentName: givenSegmentName, }) {
|
|
48
|
+
const paths = {};
|
|
49
|
+
const components = {};
|
|
50
|
+
const { openAPIObject, samples: samplesConfig, package: packageJson, } = (0, resolveGeneratorConfigValues_1.resolveGeneratorConfigValues)({
|
|
51
|
+
schema: fullSchema,
|
|
52
|
+
configs,
|
|
53
|
+
segmentName: givenSegmentName ?? null,
|
|
54
|
+
});
|
|
55
|
+
for (const [segmentName, segmentSchema] of givenSegmentName
|
|
56
|
+
? [[givenSegmentName, fullSchema.segments[givenSegmentName]]]
|
|
57
|
+
: Object.entries(fullSchema.segments ?? {})) {
|
|
58
|
+
for (const c of Object.values(segmentSchema.controllers)) {
|
|
59
|
+
for (const [handlerName, h] of Object.entries(c.handlers ?? {})) {
|
|
60
|
+
if (h.operationObject) {
|
|
61
|
+
const [queryValidation, queryComponents] = extractComponents(h?.validation?.query);
|
|
62
|
+
const [bodyValidation, bodyComponents] = extractComponents(h?.validation?.body);
|
|
63
|
+
const [paramsValidation, paramsComponents] = extractComponents(h?.validation?.params);
|
|
64
|
+
const [outputValidation, outputComponents] = extractComponents(h?.validation?.output);
|
|
65
|
+
const [iterationValidation, iterationComponents] = extractComponents(h?.validation?.iteration);
|
|
66
|
+
// TODO: Handle name conflicts?
|
|
67
|
+
Object.assign(components, queryComponents, bodyComponents, paramsComponents, outputComponents, iterationComponents);
|
|
68
|
+
const { ts, rs, py } = (0, createCodeSamples_1.createCodeSamples)({
|
|
69
|
+
package: packageJson,
|
|
70
|
+
handlerName,
|
|
71
|
+
handlerSchema: h,
|
|
72
|
+
controllerSchema: c,
|
|
73
|
+
config: samplesConfig,
|
|
74
|
+
});
|
|
75
|
+
const queryParameters = queryValidation && 'type' in queryValidation && 'properties' in queryValidation
|
|
76
|
+
? Object.entries(queryValidation.properties ?? {}).map(([propName, propSchema]) => ({
|
|
77
|
+
name: propName,
|
|
78
|
+
in: 'query',
|
|
79
|
+
required: queryValidation.required ? queryValidation.required.includes(propName) : false,
|
|
80
|
+
schema: propSchema,
|
|
81
|
+
}))
|
|
82
|
+
: null;
|
|
83
|
+
const pathParameters = paramsValidation && 'type' in paramsValidation && 'properties' in paramsValidation
|
|
84
|
+
? Object.entries(paramsValidation.properties ?? {}).map(([propName, propSchema]) => ({
|
|
85
|
+
name: propName,
|
|
86
|
+
in: 'path',
|
|
87
|
+
required: paramsValidation.required ? paramsValidation.required.includes(propName) : false,
|
|
88
|
+
schema: propSchema,
|
|
89
|
+
}))
|
|
90
|
+
: null;
|
|
91
|
+
const path = h.misc?.originalPath ??
|
|
92
|
+
'/' + [rootEntry.replace(/^\/+|\/+$/g, ''), segmentName, c.prefix, h.path].filter(Boolean).join('/');
|
|
93
|
+
paths[path] = paths[path] ?? {};
|
|
94
|
+
const httpMethod = h.httpMethod.toLowerCase();
|
|
95
|
+
paths[path][httpMethod] ??= {};
|
|
96
|
+
paths[path][httpMethod] = {
|
|
97
|
+
...h.operationObject,
|
|
98
|
+
...paths[path][httpMethod],
|
|
99
|
+
'x-codeSamples': [
|
|
100
|
+
...(paths[path][httpMethod]['x-codeSamples'] ?? []),
|
|
101
|
+
...(h.operationObject?.['x-codeSamples'] ?? []),
|
|
102
|
+
{
|
|
103
|
+
label: 'TypeScript RPC',
|
|
104
|
+
lang: 'typescript',
|
|
105
|
+
source: ts,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
label: 'Python RPC',
|
|
109
|
+
lang: 'python',
|
|
110
|
+
source: py,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
label: 'Rust RPC',
|
|
114
|
+
lang: 'rust',
|
|
115
|
+
source: rs,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
...(queryParameters || pathParameters
|
|
119
|
+
? {
|
|
120
|
+
parameters: h.operationObject?.parameters ?? [...(queryParameters || []), ...(pathParameters || [])],
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
...(paths[path][httpMethod].parameters
|
|
124
|
+
? {
|
|
125
|
+
parameters: paths[path][httpMethod].parameters,
|
|
126
|
+
}
|
|
127
|
+
: {}),
|
|
128
|
+
...(outputValidation && 'type' in outputValidation
|
|
129
|
+
? {
|
|
130
|
+
responses: {
|
|
131
|
+
200: {
|
|
132
|
+
description: 'description' in outputValidation ? outputValidation.description : 'Success',
|
|
133
|
+
content: {
|
|
134
|
+
'application/json': {
|
|
135
|
+
schema: outputValidation,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
...h.operationObject?.responses,
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
: {}),
|
|
143
|
+
...(iterationValidation && 'type' in iterationValidation
|
|
144
|
+
? {
|
|
145
|
+
responses: {
|
|
146
|
+
200: {
|
|
147
|
+
description: 'description' in iterationValidation ? iterationValidation.description : 'JSON Lines response',
|
|
148
|
+
content: {
|
|
149
|
+
'application/jsonl': {
|
|
150
|
+
schema: {
|
|
151
|
+
...iterationValidation,
|
|
152
|
+
examples: iterationValidation.examples ?? [
|
|
153
|
+
[
|
|
154
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
155
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
156
|
+
JSON.stringify((0, getJSONSchemaSample_1.getJSONSchemaSample)(iterationValidation)),
|
|
157
|
+
].join('\n'),
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
...h.operationObject?.responses,
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
: {}),
|
|
167
|
+
...(paths[path][httpMethod].responses
|
|
168
|
+
? {
|
|
169
|
+
responses: paths[path][httpMethod].responses,
|
|
170
|
+
}
|
|
171
|
+
: {}),
|
|
172
|
+
...(bodyValidation && 'type' in bodyValidation
|
|
173
|
+
? {
|
|
174
|
+
requestBody: h.operationObject?.requestBody ?? {
|
|
175
|
+
description: 'description' in bodyValidation ? bodyValidation.description : 'Request body',
|
|
176
|
+
required: true,
|
|
177
|
+
content: {
|
|
178
|
+
'application/json': {
|
|
179
|
+
schema: bodyValidation,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
: {}),
|
|
185
|
+
...(paths[path][httpMethod].requestBody
|
|
186
|
+
? {
|
|
187
|
+
requestBody: paths[path][httpMethod].requestBody,
|
|
188
|
+
}
|
|
189
|
+
: {}),
|
|
190
|
+
tags: paths[path][httpMethod].tags ?? h.operationObject?.tags,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
...openAPIObject,
|
|
198
|
+
components: {
|
|
199
|
+
...openAPIObject?.components,
|
|
200
|
+
schemas: {
|
|
201
|
+
...(openAPIObject?.components?.schemas ?? components),
|
|
202
|
+
HttpStatus: {
|
|
203
|
+
type: 'integer',
|
|
204
|
+
description: 'HTTP status code',
|
|
205
|
+
enum: Object.keys(types_1.HttpStatus)
|
|
206
|
+
.map((k) => types_1.HttpStatus[k])
|
|
207
|
+
.filter(Boolean)
|
|
208
|
+
.filter((v) => typeof v === 'number'),
|
|
209
|
+
},
|
|
210
|
+
VovkErrorResponse: {
|
|
211
|
+
type: 'object',
|
|
212
|
+
description: 'Vovk error response',
|
|
213
|
+
properties: {
|
|
214
|
+
cause: {
|
|
215
|
+
description: 'Error cause of any shape',
|
|
216
|
+
},
|
|
217
|
+
statusCode: {
|
|
218
|
+
$ref: '#/components/schemas/HttpStatus',
|
|
219
|
+
},
|
|
220
|
+
message: {
|
|
221
|
+
type: 'string',
|
|
222
|
+
description: 'Error message',
|
|
223
|
+
},
|
|
224
|
+
isError: {
|
|
225
|
+
type: 'boolean',
|
|
226
|
+
const: true,
|
|
227
|
+
description: 'Indicates that this object represents an error',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
required: ['statusCode', 'message', 'isError'],
|
|
231
|
+
additionalProperties: false,
|
|
232
|
+
},
|
|
233
|
+
...openAPIObject?.components?.schemas,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
paths,
|
|
237
|
+
};
|
|
238
|
+
}
|