vovk 3.0.0-draft.190 → 3.0.0-draft.192

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/cjs/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createVovkApp } from './createVovkApp.js';
2
- import { HttpStatus, HttpMethod, VovkSchemaIdEnum, type KnownAny, type VovkErrorResponse, type VovkRequest, type VovkBody, type VovkQuery, type VovkParams, type VovkReturnType, type VovkYieldType, type VovkOutput, type VovkIteration, type VovkSegmentSchema, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction } from './types.js';
2
+ import { HttpStatus, HttpMethod, VovkSchemaIdEnum, type KnownAny, type VovkErrorResponse, type VovkRequest, type VovkBody, type VovkQuery, type VovkParams, type VovkReturnType, type VovkYieldType, type VovkOutput, type VovkIteration, type VovkSegmentSchema, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction, type VovkTypedMethod } from './types.js';
3
3
  import { type VovkClient, type VovkClientOptions, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkValidateOnClient, type VovkStreamAsyncIterable, createRPC, fetcher, createFetcher } from './client/index.js';
4
- import { openapi, openAPIToSchema, schemaToOpenAPI } from './openapi/index.js';
4
+ import { openapi, openAPIToVovkSchema, vovkSchemaToOpenAPI } from './openapi/index.js';
5
5
  import { HttpException } from './HttpException.js';
6
6
  import { createDecorator } from './utils/createDecorator.js';
7
7
  import { JSONLinesResponse } from './JSONLinesResponse.js';
@@ -10,7 +10,7 @@ import { withValidation } from './utils/withValidation.js';
10
10
  import { multitenant } from './utils/multitenant.js';
11
11
  import { createLLMFunctions } from './utils/createLLMFunctions.js';
12
12
  import { createCodeExamples } from './utils/createCodeExamples.js';
13
- export { type KnownAny, type VovkClient, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkStreamAsyncIterable, type VovkValidateOnClient, type VovkSegmentSchema, type VovkErrorResponse, type VovkRequest, type VovkOutput, type VovkIteration, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction, VovkSchemaIdEnum, JSONLinesResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, createRPC, fetcher, createFetcher, generateStaticAPI, withValidation, multitenant, createLLMFunctions, createCodeExamples, openapi, openAPIToSchema, schemaToOpenAPI, };
13
+ export { type KnownAny, type VovkClient, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkStreamAsyncIterable, type VovkValidateOnClient, type VovkSegmentSchema, type VovkErrorResponse, type VovkRequest, type VovkOutput, type VovkIteration, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction, type VovkTypedMethod, VovkSchemaIdEnum, JSONLinesResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, createRPC, fetcher, createFetcher, generateStaticAPI, withValidation, multitenant, createLLMFunctions, createCodeExamples, openapi, openAPIToVovkSchema, vovkSchemaToOpenAPI, };
14
14
  export declare const get: {
15
15
  (givenPath?: string | undefined, options?: import("./types.js").DecoratorOptions | undefined): ReturnType<(givenPath?: string, options?: import("./types.js").DecoratorOptions) => (givenTarget: KnownAny, propertyKey: string) => void>;
16
16
  auto: (options?: import("./types.js").DecoratorOptions) => (givenTarget: KnownAny, propertyKey: string) => void;
package/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.initVovk = exports.prefix = exports.options = exports.head = exports.del = exports.patch = exports.put = exports.post = exports.get = exports.schemaToOpenAPI = exports.openAPIToSchema = exports.openapi = exports.createCodeExamples = exports.createLLMFunctions = exports.multitenant = exports.withValidation = exports.generateStaticAPI = exports.createFetcher = exports.fetcher = exports.createRPC = exports.createDecorator = exports.createVovkApp = exports.HttpMethod = exports.HttpStatus = exports.HttpException = exports.JSONLinesResponse = exports.VovkSchemaIdEnum = void 0;
4
+ exports.initVovk = exports.prefix = exports.options = exports.head = exports.del = exports.patch = exports.put = exports.post = exports.get = exports.vovkSchemaToOpenAPI = exports.openAPIToVovkSchema = exports.openapi = exports.createCodeExamples = exports.createLLMFunctions = exports.multitenant = exports.withValidation = exports.generateStaticAPI = exports.createFetcher = exports.fetcher = exports.createRPC = exports.createDecorator = exports.createVovkApp = exports.HttpMethod = exports.HttpStatus = exports.HttpException = exports.JSONLinesResponse = exports.VovkSchemaIdEnum = void 0;
5
5
  const createVovkApp_js_1 = require("./createVovkApp.js");
6
6
  Object.defineProperty(exports, "createVovkApp", { enumerable: true, get: function () { return createVovkApp_js_1.createVovkApp; } });
7
7
  const types_js_1 = require("./types.js");
@@ -14,8 +14,8 @@ Object.defineProperty(exports, "fetcher", { enumerable: true, get: function () {
14
14
  Object.defineProperty(exports, "createFetcher", { enumerable: true, get: function () { return index_js_1.createFetcher; } });
15
15
  const index_js_2 = require("./openapi/index.js");
16
16
  Object.defineProperty(exports, "openapi", { enumerable: true, get: function () { return index_js_2.openapi; } });
17
- Object.defineProperty(exports, "openAPIToSchema", { enumerable: true, get: function () { return index_js_2.openAPIToSchema; } });
18
- Object.defineProperty(exports, "schemaToOpenAPI", { enumerable: true, get: function () { return index_js_2.schemaToOpenAPI; } });
17
+ Object.defineProperty(exports, "openAPIToVovkSchema", { enumerable: true, get: function () { return index_js_2.openAPIToVovkSchema; } });
18
+ Object.defineProperty(exports, "vovkSchemaToOpenAPI", { enumerable: true, get: function () { return index_js_2.vovkSchemaToOpenAPI; } });
19
19
  const HttpException_js_1 = require("./HttpException.js");
20
20
  Object.defineProperty(exports, "HttpException", { enumerable: true, get: function () { return HttpException_js_1.HttpException; } });
21
21
  const createDecorator_js_1 = require("./utils/createDecorator.js");
@@ -1,6 +1,6 @@
1
1
  import type { OperationObject } from 'openapi3-ts/oas31';
2
- import { schemaToOpenAPI } from './schemaToOpenAPI';
3
- import { openAPIToSchema } from './openAPIToSchema';
2
+ import { vovkSchemaToOpenAPI } from './vovkSchemaToOpenAPI';
3
+ import { openAPIToVovkSchema } from './openAPIToVovkSchema';
4
4
  import { error } from './error';
5
5
  import type { KnownAny } from '../types';
6
6
  type OperationObjectWithCustomProperties = OperationObject & {
@@ -10,4 +10,4 @@ export declare const openapiDecorator: (openAPIOperationObject?: OperationObject
10
10
  export declare const openapi: typeof openapiDecorator & {
11
11
  error: typeof error;
12
12
  };
13
- export { schemaToOpenAPI, openAPIToSchema };
13
+ export { vovkSchemaToOpenAPI, openAPIToVovkSchema };
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openAPIToSchema = exports.schemaToOpenAPI = exports.openapi = exports.openapiDecorator = void 0;
4
- const schemaToOpenAPI_1 = require("./schemaToOpenAPI");
5
- Object.defineProperty(exports, "schemaToOpenAPI", { enumerable: true, get: function () { return schemaToOpenAPI_1.schemaToOpenAPI; } });
6
- const openAPIToSchema_1 = require("./openAPIToSchema");
7
- Object.defineProperty(exports, "openAPIToSchema", { enumerable: true, get: function () { return openAPIToSchema_1.openAPIToSchema; } });
3
+ exports.openAPIToVovkSchema = exports.vovkSchemaToOpenAPI = exports.openapi = exports.openapiDecorator = void 0;
4
+ const vovkSchemaToOpenAPI_1 = require("./vovkSchemaToOpenAPI");
5
+ Object.defineProperty(exports, "vovkSchemaToOpenAPI", { enumerable: true, get: function () { return vovkSchemaToOpenAPI_1.vovkSchemaToOpenAPI; } });
6
+ const openAPIToVovkSchema_1 = require("./openAPIToVovkSchema");
7
+ Object.defineProperty(exports, "openAPIToVovkSchema", { enumerable: true, get: function () { return openAPIToVovkSchema_1.openAPIToVovkSchema; } });
8
8
  const error_1 = require("./error");
9
9
  const createDecorator_1 = require("../utils/createDecorator");
10
10
  exports.openapiDecorator = (0, createDecorator_1.createDecorator)(null, (openAPIOperationObject = {}) => {
@@ -7,7 +7,7 @@ declare const defaultGetHandlerInfo: ({ method, path, operation, defaultModuleNa
7
7
  openAPIObject: OpenAPIObject;
8
8
  defaultModuleName: string;
9
9
  }) => [string, string];
10
- export declare function openAPIToSchema({ openAPIObject, getHandlerInfo, defaultModuleName, }: {
10
+ export declare function openAPIToVovkSchema({ openAPIObject, getHandlerInfo, defaultModuleName, }: {
11
11
  openAPIObject: OpenAPIObject;
12
12
  getHandlerInfo?: typeof defaultGetHandlerInfo;
13
13
  defaultModuleName?: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openAPIToSchema = openAPIToSchema;
3
+ exports.openAPIToVovkSchema = openAPIToVovkSchema;
4
4
  const types_1 = require("../types");
5
5
  const generateFnName_1 = require("./generateFnName");
6
6
  function applyComponents(schema, components) {
@@ -66,7 +66,7 @@ const defaultGetHandlerInfo = ({ method, path, operation, defaultModuleName, })
66
66
  : controllerName;
67
67
  return [rpcModuleName, handlerName];
68
68
  };
69
- function openAPIToSchema({ openAPIObject, getHandlerInfo = defaultGetHandlerInfo, defaultModuleName = 'api', }) {
69
+ function openAPIToVovkSchema({ openAPIObject, getHandlerInfo = defaultGetHandlerInfo, defaultModuleName = 'api', }) {
70
70
  const schema = {
71
71
  $schema: types_1.VovkSchemaIdEnum.SCHEMA,
72
72
  segments: {
@@ -0,0 +1,15 @@
1
+ import type { OpenAPIObject, OperationObject } from 'openapi3-ts/oas31';
2
+ import { HttpMethod, type VovkSchema } from '../types';
3
+ declare const defaultGetHandlerInfo: ({ method, path, operation, defaultModuleName, }: {
4
+ method: HttpMethod;
5
+ path: string;
6
+ operation: OperationObject;
7
+ openAPIObject: OpenAPIObject;
8
+ defaultModuleName: string;
9
+ }) => [string, string];
10
+ export declare function openAPIToVovkSchema({ openAPIObject, getHandlerInfo, defaultModuleName, }: {
11
+ openAPIObject: OpenAPIObject;
12
+ getHandlerInfo?: typeof defaultGetHandlerInfo;
13
+ defaultModuleName?: string;
14
+ }): VovkSchema;
15
+ export {};
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openAPIToVovkSchema = openAPIToVovkSchema;
4
+ const types_1 = require("../types");
5
+ const generateFnName_1 = require("./generateFnName");
6
+ function applyComponents(schema, components) {
7
+ if (!components || !Object.keys(components).length)
8
+ return schema;
9
+ // Create a deep copy of the schema
10
+ const result = JSON.parse(JSON.stringify(schema));
11
+ // Initialize $defs if it doesn't exist
12
+ result.$defs = result.$defs || {};
13
+ // Set to track components we've added to $defs
14
+ const addedComponents = new Set();
15
+ // Process a schema object and replace $refs
16
+ function processSchema(obj) {
17
+ if (!obj || typeof obj !== 'object')
18
+ return obj;
19
+ // Create a new object/array to avoid modifying the input
20
+ const newObj = Array.isArray(obj) ? [...obj] : { ...obj };
21
+ // Check for $ref
22
+ if (newObj.$ref && typeof newObj.$ref === 'string' && newObj.$ref.startsWith('#/components/schemas/')) {
23
+ const componentName = newObj.$ref.replace('#/components/schemas/', '');
24
+ newObj.$ref = `#/$defs/${componentName}`;
25
+ // Add the component to $defs if not already added
26
+ if (!addedComponents.has(componentName) && components[componentName]) {
27
+ addedComponents.add(componentName);
28
+ result.$defs[componentName] = processSchema(JSON.parse(JSON.stringify(components[componentName])));
29
+ }
30
+ }
31
+ // Process properties/items recursively
32
+ if (Array.isArray(newObj)) {
33
+ for (let i = 0; i < newObj.length; i++) {
34
+ newObj[i] = processSchema(newObj[i]);
35
+ }
36
+ }
37
+ else {
38
+ for (const key in newObj) {
39
+ if (Object.prototype.hasOwnProperty.call(newObj, key)) {
40
+ newObj[key] = processSchema(newObj[key]);
41
+ }
42
+ }
43
+ }
44
+ return newObj;
45
+ }
46
+ // Process the main schema
47
+ return processSchema(result);
48
+ }
49
+ function snakeToCamel(str) {
50
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
51
+ }
52
+ const defaultGetHandlerInfo = ({ method, path, operation, defaultModuleName, }) => {
53
+ const operationId = operation.operationId?.replace(/[^a-zA-Z0-9_]/g, '_') ?? null;
54
+ const controllerHandlerMatch = operationId?.match(/^([A-Z][a-zA-Z0-9]*)_([a-zA-Z0-9_]+)/);
55
+ const isSnakeCase = operationId && /^[a-z][a-z0-9_]+$/.test(operationId);
56
+ const [controllerName, handlerName] = controllerHandlerMatch?.slice(1, 3) ?? [
57
+ defaultModuleName,
58
+ isSnakeCase
59
+ ? snakeToCamel(operationId)
60
+ : operationId?.match(/^[a-zA-Z][a-zA-Z0-9_]+$/)
61
+ ? operationId
62
+ : (0, generateFnName_1.generateFnName)(method, path),
63
+ ];
64
+ const rpcModuleName = controllerName.endsWith('Controller')
65
+ ? controllerName.replace(/Controller$/, 'RPC')
66
+ : controllerName;
67
+ return [rpcModuleName, handlerName];
68
+ };
69
+ function openAPIToVovkSchema({ openAPIObject, getHandlerInfo = defaultGetHandlerInfo, defaultModuleName = 'api', }) {
70
+ const schema = {
71
+ $schema: types_1.VovkSchemaIdEnum.SCHEMA,
72
+ segments: {
73
+ '': {
74
+ $schema: types_1.VovkSchemaIdEnum.SEGMENT,
75
+ emitSchema: true,
76
+ segmentName: '',
77
+ controllers: {},
78
+ },
79
+ },
80
+ meta: {
81
+ $schema: types_1.VovkSchemaIdEnum.META,
82
+ config: {
83
+ $schema: types_1.VovkSchemaIdEnum.CONFIG,
84
+ },
85
+ openapi: openAPIObject,
86
+ },
87
+ };
88
+ const segment = schema.segments[''];
89
+ return Object.entries(openAPIObject.paths ?? {}).reduce((acc, [path, operations]) => {
90
+ Object.entries(operations).forEach(([method, operation]) => {
91
+ const [rpcModuleName, handlerName] = getHandlerInfo({
92
+ method: method.toUpperCase(),
93
+ path,
94
+ operation,
95
+ openAPIObject,
96
+ defaultModuleName,
97
+ });
98
+ segment.controllers[rpcModuleName] ??= {
99
+ rpcModuleName,
100
+ handlers: {},
101
+ };
102
+ // TODO how to utilize ReferenceObject?
103
+ const queryProperties = operation.parameters?.filter((p) => p.in === 'query') || [];
104
+ const pathProperties = operation.parameters?.filter((p) => p.in === 'path') || [];
105
+ const query = Array.isArray(operation.parameters)
106
+ ? {
107
+ type: 'object',
108
+ properties: Object.fromEntries(queryProperties.map((p) => [p.name, p.schema])),
109
+ required: queryProperties.filter((p) => p.required).map((p) => p.name),
110
+ }
111
+ : null;
112
+ const params = Array.isArray(pathProperties)
113
+ ? {
114
+ type: 'object',
115
+ properties: Object.fromEntries(pathProperties.map((p) => [p.name, p.schema])),
116
+ required: pathProperties.filter((p) => p.required).map((p) => p.name),
117
+ }
118
+ : null;
119
+ // TODO how to utilize ReferenceObject?
120
+ const body = operation.requestBody?.content['application/json']?.schema ?? null;
121
+ const output = operation.responses?.['200']?.content?.['application/json']?.schema ??
122
+ operation.responses?.['201']?.content?.['application/json']?.schema ??
123
+ null;
124
+ segment.controllers[rpcModuleName].handlers[handlerName] = {
125
+ httpMethod: method.toUpperCase(),
126
+ path,
127
+ openapi: operation,
128
+ validation: {
129
+ ...(query && { query: applyComponents(query, openAPIObject.components?.schemas) }),
130
+ ...(params && { params: applyComponents(params, openAPIObject.components?.schemas) }),
131
+ ...(body && { body: applyComponents(body, openAPIObject.components?.schemas) }),
132
+ ...(output && { output: applyComponents(output, openAPIObject.components?.schemas) }),
133
+ },
134
+ };
135
+ });
136
+ return acc;
137
+ }, schema);
138
+ }
@@ -1,7 +1,7 @@
1
1
  import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
2
  import { type CodeSamplePackageJson } from '../utils/createCodeExamples';
3
3
  import { type VovkSchema } from '../types';
4
- export declare function schemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
4
+ export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
5
5
  rootEntry: string;
6
6
  schema: VovkSchema;
7
7
  openAPIObject?: Partial<OpenAPIObject>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.schemaToOpenAPI = schemaToOpenAPI;
3
+ exports.vovkSchemaToOpenAPI = vovkSchemaToOpenAPI;
4
4
  const json_schema_sampler_1 = require("@stoplight/json-schema-sampler");
5
5
  const createCodeExamples_1 = require("../utils/createCodeExamples");
6
6
  const types_1 = require("../types");
@@ -43,7 +43,7 @@ function extractComponents(schema) {
43
43
  const processedSchema = process(schema);
44
44
  return [processedSchema, components];
45
45
  }
46
- function schemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'vovk-client' }, }) {
46
+ function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'vovk-client' }, }) {
47
47
  const paths = {};
48
48
  const components = {};
49
49
  for (const [segmentName, segmentSchema] of Object.entries(fullSchema.segments)) {
@@ -0,0 +1,9 @@
1
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
+ import { type CodeSamplePackageJson } from '../utils/createCodeExamples';
3
+ import { type VovkSchema } from '../types';
4
+ export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
5
+ rootEntry: string;
6
+ schema: VovkSchema;
7
+ openAPIObject?: Partial<OpenAPIObject>;
8
+ package?: CodeSamplePackageJson;
9
+ }): OpenAPIObject;
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.vovkSchemaToOpenAPI = vovkSchemaToOpenAPI;
4
+ const json_schema_sampler_1 = require("@stoplight/json-schema-sampler");
5
+ const createCodeExamples_1 = require("../utils/createCodeExamples");
6
+ const types_1 = require("../types");
7
+ function extractComponents(schema) {
8
+ if (!schema)
9
+ return [undefined, {}];
10
+ const components = {};
11
+ // Function to collect components and replace $refs recursively
12
+ const process = (obj, path = []) => {
13
+ if (!obj || typeof obj !== 'object')
14
+ return obj;
15
+ // Handle arrays
16
+ if (Array.isArray(obj)) {
17
+ return obj.map((item) => process(item, path));
18
+ }
19
+ // Create a copy to modify
20
+ const result = {};
21
+ Object.entries({ ...obj.definitions, ...obj.$defs }).forEach(([key, value]) => {
22
+ components[key] = process(value, [...path, key]);
23
+ });
24
+ // Process all properties
25
+ for (const [key, value] of Object.entries(obj)) {
26
+ // Skip already processed special properties
27
+ if (key === '$defs' || key === 'definitions')
28
+ continue;
29
+ if (key === '$ref' && typeof value === 'string') {
30
+ // Extract the component name from the reference
31
+ const refParts = value.split('/');
32
+ const refName = refParts[refParts.length - 1];
33
+ // Replace with component reference
34
+ result[key] = `#/components/schemas/${refName}`;
35
+ }
36
+ else {
37
+ // Recursively process other properties
38
+ result[key] = process(value, [...path, key]);
39
+ }
40
+ }
41
+ return result;
42
+ };
43
+ const processedSchema = process(schema);
44
+ return [processedSchema, components];
45
+ }
46
+ function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'vovk-client' }, }) {
47
+ const paths = {};
48
+ const components = {};
49
+ for (const [segmentName, segmentSchema] of Object.entries(fullSchema.segments)) {
50
+ for (const c of Object.values(segmentSchema.controllers)) {
51
+ for (const [handlerName, h] of Object.entries(c.handlers)) {
52
+ if (h.openapi) {
53
+ const [queryValidation, queryComponents] = extractComponents(h?.validation?.query);
54
+ const [bodyValidation, bodyComponents] = extractComponents(h?.validation?.body);
55
+ const [paramsValidation, paramsComponents] = extractComponents(h?.validation?.params);
56
+ const [outputValidation, outputComponents] = extractComponents(h?.validation?.output);
57
+ const [iterationValidation, iterationComponents] = extractComponents(h?.validation?.iteration);
58
+ // TODO: Handle name conflicts?
59
+ Object.assign(components, queryComponents, bodyComponents, paramsComponents, outputComponents, iterationComponents);
60
+ const { ts, rs, py } = (0, createCodeExamples_1.createCodeExamples)({
61
+ package: packageJson,
62
+ handlerName,
63
+ handlerSchema: h,
64
+ controllerSchema: c,
65
+ });
66
+ const queryParameters = queryValidation && 'type' in queryValidation && 'properties' in queryValidation
67
+ ? Object.entries(queryValidation.properties).map(([propName, propSchema]) => ({
68
+ name: propName,
69
+ in: 'query',
70
+ required: queryValidation.required ? queryValidation.required.includes(propName) : false,
71
+ schema: propSchema,
72
+ }))
73
+ : null;
74
+ const pathParameters = paramsValidation && 'type' in paramsValidation && 'properties' in paramsValidation
75
+ ? Object.entries(paramsValidation.properties).map(([propName, propSchema]) => ({
76
+ name: propName,
77
+ in: 'path',
78
+ required: paramsValidation.required ? paramsValidation.required.includes(propName) : false,
79
+ schema: propSchema,
80
+ }))
81
+ : null;
82
+ const path = '/' +
83
+ [rootEntry.replace(/^\/+|\/+$/g, ''), segmentName, c.prefix, h.path]
84
+ .filter(Boolean)
85
+ .join('/')
86
+ .replace(/:([a-zA-Z0-9_]+)/g, '{$1}');
87
+ paths[path] = paths[path] ?? {};
88
+ const httpMethod = h.httpMethod.toLowerCase();
89
+ paths[path][httpMethod] ??= {};
90
+ paths[path][httpMethod] = {
91
+ ...h.openapi,
92
+ ...paths[path][httpMethod],
93
+ 'x-codeSamples': [
94
+ ...(paths[path][httpMethod]['x-codeSamples'] ?? []),
95
+ ...(h.openapi['x-codeSamples'] ?? []),
96
+ {
97
+ label: 'TypeScript RPC',
98
+ lang: 'typescript',
99
+ source: ts,
100
+ },
101
+ {
102
+ label: 'Python RPC',
103
+ lang: 'python',
104
+ source: py,
105
+ },
106
+ {
107
+ label: 'Rust RPC',
108
+ lang: 'rust',
109
+ source: rs,
110
+ },
111
+ ],
112
+ ...(queryParameters || pathParameters
113
+ ? {
114
+ parameters: h.openapi.parameters ?? [...(queryParameters || []), ...(pathParameters || [])],
115
+ }
116
+ : {}),
117
+ ...(paths[path][httpMethod].parameters
118
+ ? {
119
+ parameters: paths[path][httpMethod].parameters,
120
+ }
121
+ : {}),
122
+ ...(outputValidation && 'type' in outputValidation && 'properties' in outputValidation
123
+ ? {
124
+ responses: {
125
+ 200: {
126
+ description: 'description' in outputValidation ? outputValidation.description : 'Success',
127
+ content: {
128
+ 'application/json': {
129
+ schema: outputValidation,
130
+ },
131
+ },
132
+ },
133
+ ...h.openapi?.responses,
134
+ },
135
+ }
136
+ : {}),
137
+ ...(iterationValidation && 'type' in iterationValidation && 'properties' in iterationValidation
138
+ ? {
139
+ responses: {
140
+ 200: {
141
+ description: 'description' in iterationValidation ? iterationValidation.description : 'JSON Lines response',
142
+ content: {
143
+ 'application/jsonl': {
144
+ schema: {
145
+ ...iterationValidation,
146
+ examples: iterationValidation.examples ?? [
147
+ [
148
+ JSON.stringify((0, json_schema_sampler_1.sample)(iterationValidation)),
149
+ JSON.stringify((0, json_schema_sampler_1.sample)(iterationValidation)),
150
+ JSON.stringify((0, json_schema_sampler_1.sample)(iterationValidation)),
151
+ ].join('\n'),
152
+ ],
153
+ },
154
+ },
155
+ },
156
+ },
157
+ ...h.openapi?.responses,
158
+ },
159
+ }
160
+ : {}),
161
+ ...(paths[path][httpMethod].responses
162
+ ? {
163
+ responses: paths[path][httpMethod].responses,
164
+ }
165
+ : {}),
166
+ ...(bodyValidation && 'type' in bodyValidation && 'properties' in bodyValidation
167
+ ? {
168
+ requestBody: h.openapi?.requestBody ?? {
169
+ description: 'description' in bodyValidation ? bodyValidation.description : 'Request body',
170
+ required: true,
171
+ content: {
172
+ 'application/json': {
173
+ schema: bodyValidation,
174
+ },
175
+ },
176
+ },
177
+ }
178
+ : {}),
179
+ ...(paths[path][httpMethod].requestBody
180
+ ? {
181
+ requestBody: paths[path][httpMethod].requestBody,
182
+ }
183
+ : {}),
184
+ tags: paths[path][httpMethod].tags ?? h.openapi?.tags,
185
+ };
186
+ }
187
+ }
188
+ }
189
+ }
190
+ return {
191
+ ...openAPIObject,
192
+ openapi: '3.1.0',
193
+ info: {
194
+ title: packageJson?.description ?? 'API',
195
+ version: packageJson?.version ?? '0.0.1',
196
+ ...openAPIObject?.info,
197
+ },
198
+ components: {
199
+ schemas: {
200
+ ...(openAPIObject?.components?.schemas ?? components),
201
+ HttpStatus: {
202
+ type: 'integer',
203
+ description: 'HTTP status code',
204
+ enum: Object.keys(types_1.HttpStatus)
205
+ .map((k) => types_1.HttpStatus[k])
206
+ .filter(Boolean)
207
+ .filter((v) => typeof v === 'number'),
208
+ },
209
+ VovkErrorResponse: {
210
+ type: 'object',
211
+ description: 'Vovk error response',
212
+ properties: {
213
+ cause: {
214
+ description: 'Error cause of any shape',
215
+ },
216
+ statusCode: {
217
+ $ref: '#/components/schemas/HttpStatus',
218
+ },
219
+ message: {
220
+ type: 'string',
221
+ description: 'Error message',
222
+ },
223
+ isError: {
224
+ type: 'boolean',
225
+ const: true,
226
+ description: 'Indicates that this object represents an error',
227
+ },
228
+ },
229
+ required: ['statusCode', 'message', 'isError'],
230
+ additionalProperties: false,
231
+ },
232
+ ...openAPIObject?.components?.schemas,
233
+ },
234
+ },
235
+ paths,
236
+ };
237
+ }
package/cjs/types.d.ts CHANGED
@@ -96,6 +96,16 @@ export type ControllerStaticMethod<REQ extends VovkRequest<KnownAny, KnownAny> =
96
96
  } = KnownAny> = ((req: REQ, params: PARAMS) => unknown) & {
97
97
  _controller?: VovkController;
98
98
  };
99
+ export type VovkTypedMethod<T extends (...args: KnownAny[]) => KnownAny, B = KnownAny, Q = KnownAny, P = KnownAny, O = KnownAny, I = KnownAny> = T & {
100
+ __types: {
101
+ body?: B;
102
+ query?: Q;
103
+ params?: P;
104
+ output?: O;
105
+ iteration?: I;
106
+ };
107
+ isRPC?: boolean;
108
+ };
99
109
  export type VovkControllerBody<T extends (...args: KnownAny) => KnownAny> = Awaited<ReturnType<Parameters<T>[0]['vovk']['body']>>;
100
110
  export type VovkControllerQuery<T extends (...args: KnownAny) => KnownAny> = ReturnType<Parameters<T>[0]['vovk']['query']>;
101
111
  export type VovkControllerParams<T extends (...args: KnownAny) => KnownAny> = Parameters<T>[1] extends object ? Parameters<T>[1] : ReturnType<Parameters<T>[0]['vovk']['params']>;
@@ -1,14 +1,6 @@
1
- import { VovkHandlerSchema, VovkValidationType, type KnownAny, type VovkRequest } from '../types.js';
1
+ import { VovkHandlerSchema, VovkTypedMethod, VovkValidationType, type KnownAny, type VovkRequest } from '../types.js';
2
2
  type VovkRequestAny = VovkRequest<KnownAny, KnownAny, KnownAny>;
3
- export declare function withValidation<T extends ((req: KnownAny, params: KnownAny) => KnownAny) & {
4
- __types: {
5
- body: KnownAny;
6
- query: KnownAny;
7
- params: KnownAny;
8
- output: KnownAny;
9
- iteration: KnownAny;
10
- };
11
- }, BODY_MODEL, QUERY_MODEL, PARAMS_MODEL, OUTPUT_MODEL, ITERATION_MODEL>({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }: {
3
+ export declare function withValidation<T extends VovkTypedMethod<(req: KnownAny, params: KnownAny) => KnownAny>, BODY_MODEL, QUERY_MODEL, PARAMS_MODEL, OUTPUT_MODEL, ITERATION_MODEL>({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }: {
12
4
  disableServerSideValidation?: boolean | VovkValidationType[];
13
5
  skipSchemaEmission?: boolean | VovkValidationType[];
14
6
  validateEachIteration?: boolean;
package/mjs/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createVovkApp } from './createVovkApp.js';
2
- import { HttpStatus, HttpMethod, VovkSchemaIdEnum, type KnownAny, type VovkErrorResponse, type VovkRequest, type VovkBody, type VovkQuery, type VovkParams, type VovkReturnType, type VovkYieldType, type VovkOutput, type VovkIteration, type VovkSegmentSchema, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction } from './types.js';
2
+ import { HttpStatus, HttpMethod, VovkSchemaIdEnum, type KnownAny, type VovkErrorResponse, type VovkRequest, type VovkBody, type VovkQuery, type VovkParams, type VovkReturnType, type VovkYieldType, type VovkOutput, type VovkIteration, type VovkSegmentSchema, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction, type VovkTypedMethod } from './types.js';
3
3
  import { type VovkClient, type VovkClientOptions, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkValidateOnClient, type VovkStreamAsyncIterable, createRPC, fetcher, createFetcher } from './client/index.js';
4
- import { openapi, openAPIToSchema, schemaToOpenAPI } from './openapi/index.js';
4
+ import { openapi, openAPIToVovkSchema, vovkSchemaToOpenAPI } from './openapi/index.js';
5
5
  import { HttpException } from './HttpException.js';
6
6
  import { createDecorator } from './utils/createDecorator.js';
7
7
  import { JSONLinesResponse } from './JSONLinesResponse.js';
@@ -10,7 +10,7 @@ import { withValidation } from './utils/withValidation.js';
10
10
  import { multitenant } from './utils/multitenant.js';
11
11
  import { createLLMFunctions } from './utils/createLLMFunctions.js';
12
12
  import { createCodeExamples } from './utils/createCodeExamples.js';
13
- export { type KnownAny, type VovkClient, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkStreamAsyncIterable, type VovkValidateOnClient, type VovkSegmentSchema, type VovkErrorResponse, type VovkRequest, type VovkOutput, type VovkIteration, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction, VovkSchemaIdEnum, JSONLinesResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, createRPC, fetcher, createFetcher, generateStaticAPI, withValidation, multitenant, createLLMFunctions, createCodeExamples, openapi, openAPIToSchema, schemaToOpenAPI, };
13
+ export { type KnownAny, type VovkClient, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkStreamAsyncIterable, type VovkValidateOnClient, type VovkSegmentSchema, type VovkErrorResponse, type VovkRequest, type VovkOutput, type VovkIteration, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkControllerSchema, type VovkHandlerSchema, type VovkSchema, type VovkConfig, type VovkStrictConfig, type VovkValidationType, type VovkLLMFunction, type VovkTypedMethod, VovkSchemaIdEnum, JSONLinesResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, createRPC, fetcher, createFetcher, generateStaticAPI, withValidation, multitenant, createLLMFunctions, createCodeExamples, openapi, openAPIToVovkSchema, vovkSchemaToOpenAPI, };
14
14
  export declare const get: {
15
15
  (givenPath?: string | undefined, options?: import("./types.js").DecoratorOptions | undefined): ReturnType<(givenPath?: string, options?: import("./types.js").DecoratorOptions) => (givenTarget: KnownAny, propertyKey: string) => void>;
16
16
  auto: (options?: import("./types.js").DecoratorOptions) => (givenTarget: KnownAny, propertyKey: string) => void;
package/mjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.initVovk = exports.prefix = exports.options = exports.head = exports.del = exports.patch = exports.put = exports.post = exports.get = exports.schemaToOpenAPI = exports.openAPIToSchema = exports.openapi = exports.createCodeExamples = exports.createLLMFunctions = exports.multitenant = exports.withValidation = exports.generateStaticAPI = exports.createFetcher = exports.fetcher = exports.createRPC = exports.createDecorator = exports.createVovkApp = exports.HttpMethod = exports.HttpStatus = exports.HttpException = exports.JSONLinesResponse = exports.VovkSchemaIdEnum = void 0;
4
+ exports.initVovk = exports.prefix = exports.options = exports.head = exports.del = exports.patch = exports.put = exports.post = exports.get = exports.vovkSchemaToOpenAPI = exports.openAPIToVovkSchema = exports.openapi = exports.createCodeExamples = exports.createLLMFunctions = exports.multitenant = exports.withValidation = exports.generateStaticAPI = exports.createFetcher = exports.fetcher = exports.createRPC = exports.createDecorator = exports.createVovkApp = exports.HttpMethod = exports.HttpStatus = exports.HttpException = exports.JSONLinesResponse = exports.VovkSchemaIdEnum = void 0;
5
5
  const createVovkApp_js_1 = require("./createVovkApp.js");
6
6
  Object.defineProperty(exports, "createVovkApp", { enumerable: true, get: function () { return createVovkApp_js_1.createVovkApp; } });
7
7
  const types_js_1 = require("./types.js");
@@ -14,8 +14,8 @@ Object.defineProperty(exports, "fetcher", { enumerable: true, get: function () {
14
14
  Object.defineProperty(exports, "createFetcher", { enumerable: true, get: function () { return index_js_1.createFetcher; } });
15
15
  const index_js_2 = require("./openapi/index.js");
16
16
  Object.defineProperty(exports, "openapi", { enumerable: true, get: function () { return index_js_2.openapi; } });
17
- Object.defineProperty(exports, "openAPIToSchema", { enumerable: true, get: function () { return index_js_2.openAPIToSchema; } });
18
- Object.defineProperty(exports, "schemaToOpenAPI", { enumerable: true, get: function () { return index_js_2.schemaToOpenAPI; } });
17
+ Object.defineProperty(exports, "openAPIToVovkSchema", { enumerable: true, get: function () { return index_js_2.openAPIToVovkSchema; } });
18
+ Object.defineProperty(exports, "vovkSchemaToOpenAPI", { enumerable: true, get: function () { return index_js_2.vovkSchemaToOpenAPI; } });
19
19
  const HttpException_js_1 = require("./HttpException.js");
20
20
  Object.defineProperty(exports, "HttpException", { enumerable: true, get: function () { return HttpException_js_1.HttpException; } });
21
21
  const createDecorator_js_1 = require("./utils/createDecorator.js");
@@ -1,6 +1,6 @@
1
1
  import type { OperationObject } from 'openapi3-ts/oas31';
2
- import { schemaToOpenAPI } from './schemaToOpenAPI';
3
- import { openAPIToSchema } from './openAPIToSchema';
2
+ import { vovkSchemaToOpenAPI } from './vovkSchemaToOpenAPI';
3
+ import { openAPIToVovkSchema } from './openAPIToVovkSchema';
4
4
  import { error } from './error';
5
5
  import type { KnownAny } from '../types';
6
6
  type OperationObjectWithCustomProperties = OperationObject & {
@@ -10,4 +10,4 @@ export declare const openapiDecorator: (openAPIOperationObject?: OperationObject
10
10
  export declare const openapi: typeof openapiDecorator & {
11
11
  error: typeof error;
12
12
  };
13
- export { schemaToOpenAPI, openAPIToSchema };
13
+ export { vovkSchemaToOpenAPI, openAPIToVovkSchema };
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openAPIToSchema = exports.schemaToOpenAPI = exports.openapi = exports.openapiDecorator = void 0;
4
- const schemaToOpenAPI_1 = require("./schemaToOpenAPI");
5
- Object.defineProperty(exports, "schemaToOpenAPI", { enumerable: true, get: function () { return schemaToOpenAPI_1.schemaToOpenAPI; } });
6
- const openAPIToSchema_1 = require("./openAPIToSchema");
7
- Object.defineProperty(exports, "openAPIToSchema", { enumerable: true, get: function () { return openAPIToSchema_1.openAPIToSchema; } });
3
+ exports.openAPIToVovkSchema = exports.vovkSchemaToOpenAPI = exports.openapi = exports.openapiDecorator = void 0;
4
+ const vovkSchemaToOpenAPI_1 = require("./vovkSchemaToOpenAPI");
5
+ Object.defineProperty(exports, "vovkSchemaToOpenAPI", { enumerable: true, get: function () { return vovkSchemaToOpenAPI_1.vovkSchemaToOpenAPI; } });
6
+ const openAPIToVovkSchema_1 = require("./openAPIToVovkSchema");
7
+ Object.defineProperty(exports, "openAPIToVovkSchema", { enumerable: true, get: function () { return openAPIToVovkSchema_1.openAPIToVovkSchema; } });
8
8
  const error_1 = require("./error");
9
9
  const createDecorator_1 = require("../utils/createDecorator");
10
10
  exports.openapiDecorator = (0, createDecorator_1.createDecorator)(null, (openAPIOperationObject = {}) => {
@@ -7,7 +7,7 @@ declare const defaultGetHandlerInfo: ({ method, path, operation, defaultModuleNa
7
7
  openAPIObject: OpenAPIObject;
8
8
  defaultModuleName: string;
9
9
  }) => [string, string];
10
- export declare function openAPIToSchema({ openAPIObject, getHandlerInfo, defaultModuleName, }: {
10
+ export declare function openAPIToVovkSchema({ openAPIObject, getHandlerInfo, defaultModuleName, }: {
11
11
  openAPIObject: OpenAPIObject;
12
12
  getHandlerInfo?: typeof defaultGetHandlerInfo;
13
13
  defaultModuleName?: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.openAPIToSchema = openAPIToSchema;
3
+ exports.openAPIToVovkSchema = openAPIToVovkSchema;
4
4
  const types_1 = require("../types");
5
5
  const generateFnName_1 = require("./generateFnName");
6
6
  function applyComponents(schema, components) {
@@ -66,7 +66,7 @@ const defaultGetHandlerInfo = ({ method, path, operation, defaultModuleName, })
66
66
  : controllerName;
67
67
  return [rpcModuleName, handlerName];
68
68
  };
69
- function openAPIToSchema({ openAPIObject, getHandlerInfo = defaultGetHandlerInfo, defaultModuleName = 'api', }) {
69
+ function openAPIToVovkSchema({ openAPIObject, getHandlerInfo = defaultGetHandlerInfo, defaultModuleName = 'api', }) {
70
70
  const schema = {
71
71
  $schema: types_1.VovkSchemaIdEnum.SCHEMA,
72
72
  segments: {
@@ -0,0 +1,15 @@
1
+ import type { OpenAPIObject, OperationObject } from 'openapi3-ts/oas31';
2
+ import { HttpMethod, type VovkSchema } from '../types';
3
+ declare const defaultGetHandlerInfo: ({ method, path, operation, defaultModuleName, }: {
4
+ method: HttpMethod;
5
+ path: string;
6
+ operation: OperationObject;
7
+ openAPIObject: OpenAPIObject;
8
+ defaultModuleName: string;
9
+ }) => [string, string];
10
+ export declare function openAPIToVovkSchema({ openAPIObject, getHandlerInfo, defaultModuleName, }: {
11
+ openAPIObject: OpenAPIObject;
12
+ getHandlerInfo?: typeof defaultGetHandlerInfo;
13
+ defaultModuleName?: string;
14
+ }): VovkSchema;
15
+ export {};
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openAPIToVovkSchema = openAPIToVovkSchema;
4
+ const types_1 = require("../types");
5
+ const generateFnName_1 = require("./generateFnName");
6
+ function applyComponents(schema, components) {
7
+ if (!components || !Object.keys(components).length)
8
+ return schema;
9
+ // Create a deep copy of the schema
10
+ const result = JSON.parse(JSON.stringify(schema));
11
+ // Initialize $defs if it doesn't exist
12
+ result.$defs = result.$defs || {};
13
+ // Set to track components we've added to $defs
14
+ const addedComponents = new Set();
15
+ // Process a schema object and replace $refs
16
+ function processSchema(obj) {
17
+ if (!obj || typeof obj !== 'object')
18
+ return obj;
19
+ // Create a new object/array to avoid modifying the input
20
+ const newObj = Array.isArray(obj) ? [...obj] : { ...obj };
21
+ // Check for $ref
22
+ if (newObj.$ref && typeof newObj.$ref === 'string' && newObj.$ref.startsWith('#/components/schemas/')) {
23
+ const componentName = newObj.$ref.replace('#/components/schemas/', '');
24
+ newObj.$ref = `#/$defs/${componentName}`;
25
+ // Add the component to $defs if not already added
26
+ if (!addedComponents.has(componentName) && components[componentName]) {
27
+ addedComponents.add(componentName);
28
+ result.$defs[componentName] = processSchema(JSON.parse(JSON.stringify(components[componentName])));
29
+ }
30
+ }
31
+ // Process properties/items recursively
32
+ if (Array.isArray(newObj)) {
33
+ for (let i = 0; i < newObj.length; i++) {
34
+ newObj[i] = processSchema(newObj[i]);
35
+ }
36
+ }
37
+ else {
38
+ for (const key in newObj) {
39
+ if (Object.prototype.hasOwnProperty.call(newObj, key)) {
40
+ newObj[key] = processSchema(newObj[key]);
41
+ }
42
+ }
43
+ }
44
+ return newObj;
45
+ }
46
+ // Process the main schema
47
+ return processSchema(result);
48
+ }
49
+ function snakeToCamel(str) {
50
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
51
+ }
52
+ const defaultGetHandlerInfo = ({ method, path, operation, defaultModuleName, }) => {
53
+ const operationId = operation.operationId?.replace(/[^a-zA-Z0-9_]/g, '_') ?? null;
54
+ const controllerHandlerMatch = operationId?.match(/^([A-Z][a-zA-Z0-9]*)_([a-zA-Z0-9_]+)/);
55
+ const isSnakeCase = operationId && /^[a-z][a-z0-9_]+$/.test(operationId);
56
+ const [controllerName, handlerName] = controllerHandlerMatch?.slice(1, 3) ?? [
57
+ defaultModuleName,
58
+ isSnakeCase
59
+ ? snakeToCamel(operationId)
60
+ : operationId?.match(/^[a-zA-Z][a-zA-Z0-9_]+$/)
61
+ ? operationId
62
+ : (0, generateFnName_1.generateFnName)(method, path),
63
+ ];
64
+ const rpcModuleName = controllerName.endsWith('Controller')
65
+ ? controllerName.replace(/Controller$/, 'RPC')
66
+ : controllerName;
67
+ return [rpcModuleName, handlerName];
68
+ };
69
+ function openAPIToVovkSchema({ openAPIObject, getHandlerInfo = defaultGetHandlerInfo, defaultModuleName = 'api', }) {
70
+ const schema = {
71
+ $schema: types_1.VovkSchemaIdEnum.SCHEMA,
72
+ segments: {
73
+ '': {
74
+ $schema: types_1.VovkSchemaIdEnum.SEGMENT,
75
+ emitSchema: true,
76
+ segmentName: '',
77
+ controllers: {},
78
+ },
79
+ },
80
+ meta: {
81
+ $schema: types_1.VovkSchemaIdEnum.META,
82
+ config: {
83
+ $schema: types_1.VovkSchemaIdEnum.CONFIG,
84
+ },
85
+ openapi: openAPIObject,
86
+ },
87
+ };
88
+ const segment = schema.segments[''];
89
+ return Object.entries(openAPIObject.paths ?? {}).reduce((acc, [path, operations]) => {
90
+ Object.entries(operations).forEach(([method, operation]) => {
91
+ const [rpcModuleName, handlerName] = getHandlerInfo({
92
+ method: method.toUpperCase(),
93
+ path,
94
+ operation,
95
+ openAPIObject,
96
+ defaultModuleName,
97
+ });
98
+ segment.controllers[rpcModuleName] ??= {
99
+ rpcModuleName,
100
+ handlers: {},
101
+ };
102
+ // TODO how to utilize ReferenceObject?
103
+ const queryProperties = operation.parameters?.filter((p) => p.in === 'query') || [];
104
+ const pathProperties = operation.parameters?.filter((p) => p.in === 'path') || [];
105
+ const query = Array.isArray(operation.parameters)
106
+ ? {
107
+ type: 'object',
108
+ properties: Object.fromEntries(queryProperties.map((p) => [p.name, p.schema])),
109
+ required: queryProperties.filter((p) => p.required).map((p) => p.name),
110
+ }
111
+ : null;
112
+ const params = Array.isArray(pathProperties)
113
+ ? {
114
+ type: 'object',
115
+ properties: Object.fromEntries(pathProperties.map((p) => [p.name, p.schema])),
116
+ required: pathProperties.filter((p) => p.required).map((p) => p.name),
117
+ }
118
+ : null;
119
+ // TODO how to utilize ReferenceObject?
120
+ const body = operation.requestBody?.content['application/json']?.schema ?? null;
121
+ const output = operation.responses?.['200']?.content?.['application/json']?.schema ??
122
+ operation.responses?.['201']?.content?.['application/json']?.schema ??
123
+ null;
124
+ segment.controllers[rpcModuleName].handlers[handlerName] = {
125
+ httpMethod: method.toUpperCase(),
126
+ path,
127
+ openapi: operation,
128
+ validation: {
129
+ ...(query && { query: applyComponents(query, openAPIObject.components?.schemas) }),
130
+ ...(params && { params: applyComponents(params, openAPIObject.components?.schemas) }),
131
+ ...(body && { body: applyComponents(body, openAPIObject.components?.schemas) }),
132
+ ...(output && { output: applyComponents(output, openAPIObject.components?.schemas) }),
133
+ },
134
+ };
135
+ });
136
+ return acc;
137
+ }, schema);
138
+ }
@@ -1,7 +1,7 @@
1
1
  import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
2
  import { type CodeSamplePackageJson } from '../utils/createCodeExamples';
3
3
  import { type VovkSchema } from '../types';
4
- export declare function schemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
4
+ export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
5
5
  rootEntry: string;
6
6
  schema: VovkSchema;
7
7
  openAPIObject?: Partial<OpenAPIObject>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.schemaToOpenAPI = schemaToOpenAPI;
3
+ exports.vovkSchemaToOpenAPI = vovkSchemaToOpenAPI;
4
4
  const json_schema_sampler_1 = require("@stoplight/json-schema-sampler");
5
5
  const createCodeExamples_1 = require("../utils/createCodeExamples");
6
6
  const types_1 = require("../types");
@@ -43,7 +43,7 @@ function extractComponents(schema) {
43
43
  const processedSchema = process(schema);
44
44
  return [processedSchema, components];
45
45
  }
46
- function schemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'vovk-client' }, }) {
46
+ function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'vovk-client' }, }) {
47
47
  const paths = {};
48
48
  const components = {};
49
49
  for (const [segmentName, segmentSchema] of Object.entries(fullSchema.segments)) {
@@ -0,0 +1,9 @@
1
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
+ import { type CodeSamplePackageJson } from '../utils/createCodeExamples';
3
+ import { type VovkSchema } from '../types';
4
+ export declare function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject, package: packageJson, }: {
5
+ rootEntry: string;
6
+ schema: VovkSchema;
7
+ openAPIObject?: Partial<OpenAPIObject>;
8
+ package?: CodeSamplePackageJson;
9
+ }): OpenAPIObject;
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.vovkSchemaToOpenAPI = vovkSchemaToOpenAPI;
4
+ const json_schema_sampler_1 = require("@stoplight/json-schema-sampler");
5
+ const createCodeExamples_1 = require("../utils/createCodeExamples");
6
+ const types_1 = require("../types");
7
+ function extractComponents(schema) {
8
+ if (!schema)
9
+ return [undefined, {}];
10
+ const components = {};
11
+ // Function to collect components and replace $refs recursively
12
+ const process = (obj, path = []) => {
13
+ if (!obj || typeof obj !== 'object')
14
+ return obj;
15
+ // Handle arrays
16
+ if (Array.isArray(obj)) {
17
+ return obj.map((item) => process(item, path));
18
+ }
19
+ // Create a copy to modify
20
+ const result = {};
21
+ Object.entries({ ...obj.definitions, ...obj.$defs }).forEach(([key, value]) => {
22
+ components[key] = process(value, [...path, key]);
23
+ });
24
+ // Process all properties
25
+ for (const [key, value] of Object.entries(obj)) {
26
+ // Skip already processed special properties
27
+ if (key === '$defs' || key === 'definitions')
28
+ continue;
29
+ if (key === '$ref' && typeof value === 'string') {
30
+ // Extract the component name from the reference
31
+ const refParts = value.split('/');
32
+ const refName = refParts[refParts.length - 1];
33
+ // Replace with component reference
34
+ result[key] = `#/components/schemas/${refName}`;
35
+ }
36
+ else {
37
+ // Recursively process other properties
38
+ result[key] = process(value, [...path, key]);
39
+ }
40
+ }
41
+ return result;
42
+ };
43
+ const processedSchema = process(schema);
44
+ return [processedSchema, components];
45
+ }
46
+ function vovkSchemaToOpenAPI({ rootEntry, schema: fullSchema, openAPIObject = {}, package: packageJson = { name: 'vovk-client' }, }) {
47
+ const paths = {};
48
+ const components = {};
49
+ for (const [segmentName, segmentSchema] of Object.entries(fullSchema.segments)) {
50
+ for (const c of Object.values(segmentSchema.controllers)) {
51
+ for (const [handlerName, h] of Object.entries(c.handlers)) {
52
+ if (h.openapi) {
53
+ const [queryValidation, queryComponents] = extractComponents(h?.validation?.query);
54
+ const [bodyValidation, bodyComponents] = extractComponents(h?.validation?.body);
55
+ const [paramsValidation, paramsComponents] = extractComponents(h?.validation?.params);
56
+ const [outputValidation, outputComponents] = extractComponents(h?.validation?.output);
57
+ const [iterationValidation, iterationComponents] = extractComponents(h?.validation?.iteration);
58
+ // TODO: Handle name conflicts?
59
+ Object.assign(components, queryComponents, bodyComponents, paramsComponents, outputComponents, iterationComponents);
60
+ const { ts, rs, py } = (0, createCodeExamples_1.createCodeExamples)({
61
+ package: packageJson,
62
+ handlerName,
63
+ handlerSchema: h,
64
+ controllerSchema: c,
65
+ });
66
+ const queryParameters = queryValidation && 'type' in queryValidation && 'properties' in queryValidation
67
+ ? Object.entries(queryValidation.properties).map(([propName, propSchema]) => ({
68
+ name: propName,
69
+ in: 'query',
70
+ required: queryValidation.required ? queryValidation.required.includes(propName) : false,
71
+ schema: propSchema,
72
+ }))
73
+ : null;
74
+ const pathParameters = paramsValidation && 'type' in paramsValidation && 'properties' in paramsValidation
75
+ ? Object.entries(paramsValidation.properties).map(([propName, propSchema]) => ({
76
+ name: propName,
77
+ in: 'path',
78
+ required: paramsValidation.required ? paramsValidation.required.includes(propName) : false,
79
+ schema: propSchema,
80
+ }))
81
+ : null;
82
+ const path = '/' +
83
+ [rootEntry.replace(/^\/+|\/+$/g, ''), segmentName, c.prefix, h.path]
84
+ .filter(Boolean)
85
+ .join('/')
86
+ .replace(/:([a-zA-Z0-9_]+)/g, '{$1}');
87
+ paths[path] = paths[path] ?? {};
88
+ const httpMethod = h.httpMethod.toLowerCase();
89
+ paths[path][httpMethod] ??= {};
90
+ paths[path][httpMethod] = {
91
+ ...h.openapi,
92
+ ...paths[path][httpMethod],
93
+ 'x-codeSamples': [
94
+ ...(paths[path][httpMethod]['x-codeSamples'] ?? []),
95
+ ...(h.openapi['x-codeSamples'] ?? []),
96
+ {
97
+ label: 'TypeScript RPC',
98
+ lang: 'typescript',
99
+ source: ts,
100
+ },
101
+ {
102
+ label: 'Python RPC',
103
+ lang: 'python',
104
+ source: py,
105
+ },
106
+ {
107
+ label: 'Rust RPC',
108
+ lang: 'rust',
109
+ source: rs,
110
+ },
111
+ ],
112
+ ...(queryParameters || pathParameters
113
+ ? {
114
+ parameters: h.openapi.parameters ?? [...(queryParameters || []), ...(pathParameters || [])],
115
+ }
116
+ : {}),
117
+ ...(paths[path][httpMethod].parameters
118
+ ? {
119
+ parameters: paths[path][httpMethod].parameters,
120
+ }
121
+ : {}),
122
+ ...(outputValidation && 'type' in outputValidation && 'properties' in outputValidation
123
+ ? {
124
+ responses: {
125
+ 200: {
126
+ description: 'description' in outputValidation ? outputValidation.description : 'Success',
127
+ content: {
128
+ 'application/json': {
129
+ schema: outputValidation,
130
+ },
131
+ },
132
+ },
133
+ ...h.openapi?.responses,
134
+ },
135
+ }
136
+ : {}),
137
+ ...(iterationValidation && 'type' in iterationValidation && 'properties' in iterationValidation
138
+ ? {
139
+ responses: {
140
+ 200: {
141
+ description: 'description' in iterationValidation ? iterationValidation.description : 'JSON Lines response',
142
+ content: {
143
+ 'application/jsonl': {
144
+ schema: {
145
+ ...iterationValidation,
146
+ examples: iterationValidation.examples ?? [
147
+ [
148
+ JSON.stringify((0, json_schema_sampler_1.sample)(iterationValidation)),
149
+ JSON.stringify((0, json_schema_sampler_1.sample)(iterationValidation)),
150
+ JSON.stringify((0, json_schema_sampler_1.sample)(iterationValidation)),
151
+ ].join('\n'),
152
+ ],
153
+ },
154
+ },
155
+ },
156
+ },
157
+ ...h.openapi?.responses,
158
+ },
159
+ }
160
+ : {}),
161
+ ...(paths[path][httpMethod].responses
162
+ ? {
163
+ responses: paths[path][httpMethod].responses,
164
+ }
165
+ : {}),
166
+ ...(bodyValidation && 'type' in bodyValidation && 'properties' in bodyValidation
167
+ ? {
168
+ requestBody: h.openapi?.requestBody ?? {
169
+ description: 'description' in bodyValidation ? bodyValidation.description : 'Request body',
170
+ required: true,
171
+ content: {
172
+ 'application/json': {
173
+ schema: bodyValidation,
174
+ },
175
+ },
176
+ },
177
+ }
178
+ : {}),
179
+ ...(paths[path][httpMethod].requestBody
180
+ ? {
181
+ requestBody: paths[path][httpMethod].requestBody,
182
+ }
183
+ : {}),
184
+ tags: paths[path][httpMethod].tags ?? h.openapi?.tags,
185
+ };
186
+ }
187
+ }
188
+ }
189
+ }
190
+ return {
191
+ ...openAPIObject,
192
+ openapi: '3.1.0',
193
+ info: {
194
+ title: packageJson?.description ?? 'API',
195
+ version: packageJson?.version ?? '0.0.1',
196
+ ...openAPIObject?.info,
197
+ },
198
+ components: {
199
+ schemas: {
200
+ ...(openAPIObject?.components?.schemas ?? components),
201
+ HttpStatus: {
202
+ type: 'integer',
203
+ description: 'HTTP status code',
204
+ enum: Object.keys(types_1.HttpStatus)
205
+ .map((k) => types_1.HttpStatus[k])
206
+ .filter(Boolean)
207
+ .filter((v) => typeof v === 'number'),
208
+ },
209
+ VovkErrorResponse: {
210
+ type: 'object',
211
+ description: 'Vovk error response',
212
+ properties: {
213
+ cause: {
214
+ description: 'Error cause of any shape',
215
+ },
216
+ statusCode: {
217
+ $ref: '#/components/schemas/HttpStatus',
218
+ },
219
+ message: {
220
+ type: 'string',
221
+ description: 'Error message',
222
+ },
223
+ isError: {
224
+ type: 'boolean',
225
+ const: true,
226
+ description: 'Indicates that this object represents an error',
227
+ },
228
+ },
229
+ required: ['statusCode', 'message', 'isError'],
230
+ additionalProperties: false,
231
+ },
232
+ ...openAPIObject?.components?.schemas,
233
+ },
234
+ },
235
+ paths,
236
+ };
237
+ }
package/mjs/types.d.ts CHANGED
@@ -96,6 +96,16 @@ export type ControllerStaticMethod<REQ extends VovkRequest<KnownAny, KnownAny> =
96
96
  } = KnownAny> = ((req: REQ, params: PARAMS) => unknown) & {
97
97
  _controller?: VovkController;
98
98
  };
99
+ export type VovkTypedMethod<T extends (...args: KnownAny[]) => KnownAny, B = KnownAny, Q = KnownAny, P = KnownAny, O = KnownAny, I = KnownAny> = T & {
100
+ __types: {
101
+ body?: B;
102
+ query?: Q;
103
+ params?: P;
104
+ output?: O;
105
+ iteration?: I;
106
+ };
107
+ isRPC?: boolean;
108
+ };
99
109
  export type VovkControllerBody<T extends (...args: KnownAny) => KnownAny> = Awaited<ReturnType<Parameters<T>[0]['vovk']['body']>>;
100
110
  export type VovkControllerQuery<T extends (...args: KnownAny) => KnownAny> = ReturnType<Parameters<T>[0]['vovk']['query']>;
101
111
  export type VovkControllerParams<T extends (...args: KnownAny) => KnownAny> = Parameters<T>[1] extends object ? Parameters<T>[1] : ReturnType<Parameters<T>[0]['vovk']['params']>;
@@ -1,14 +1,6 @@
1
- import { VovkHandlerSchema, VovkValidationType, type KnownAny, type VovkRequest } from '../types.js';
1
+ import { VovkHandlerSchema, VovkTypedMethod, VovkValidationType, type KnownAny, type VovkRequest } from '../types.js';
2
2
  type VovkRequestAny = VovkRequest<KnownAny, KnownAny, KnownAny>;
3
- export declare function withValidation<T extends ((req: KnownAny, params: KnownAny) => KnownAny) & {
4
- __types: {
5
- body: KnownAny;
6
- query: KnownAny;
7
- params: KnownAny;
8
- output: KnownAny;
9
- iteration: KnownAny;
10
- };
11
- }, BODY_MODEL, QUERY_MODEL, PARAMS_MODEL, OUTPUT_MODEL, ITERATION_MODEL>({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }: {
3
+ export declare function withValidation<T extends VovkTypedMethod<(req: KnownAny, params: KnownAny) => KnownAny>, BODY_MODEL, QUERY_MODEL, PARAMS_MODEL, OUTPUT_MODEL, ITERATION_MODEL>({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }: {
12
4
  disableServerSideValidation?: boolean | VovkValidationType[];
13
5
  skipSchemaEmission?: boolean | VovkValidationType[];
14
6
  validateEachIteration?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk",
3
- "version": "3.0.0-draft.190",
3
+ "version": "3.0.0-draft.192",
4
4
  "main": "./cjs/index.js",
5
5
  "module": "./mjs/index.js",
6
6
  "types": "./mjs/index.d.ts",