vovk 3.0.0-draft.64 → 3.0.0-draft.66

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/VovkApp.d.ts CHANGED
@@ -1,27 +1,28 @@
1
- import { HttpMethod, HttpStatus, type RouteHandler, type VovkController, type DecoratorOptions, type VovkRequest } from './types';
1
+ import type { NextRequest } from 'next/server';
2
+ import { HttpMethod, HttpStatus, type RouteHandler, type VovkController, type DecoratorOptions } from './types';
2
3
  export declare class VovkApp {
3
4
  #private;
4
5
  private static getHeadersFromOptions;
5
6
  routes: Record<HttpMethod, Map<VovkController, Record<string, RouteHandler>>>;
6
- GET: (req: VovkRequest, data: {
7
+ GET: (req: NextRequest, data: {
7
8
  params: Promise<Record<string, string[]>>;
8
9
  }) => Promise<Response>;
9
- POST: (req: VovkRequest, data: {
10
+ POST: (req: NextRequest, data: {
10
11
  params: Promise<Record<string, string[]>>;
11
12
  }) => Promise<Response>;
12
- PUT: (req: VovkRequest, data: {
13
+ PUT: (req: NextRequest, data: {
13
14
  params: Promise<Record<string, string[]>>;
14
15
  }) => Promise<Response>;
15
- PATCH: (req: VovkRequest, data: {
16
+ PATCH: (req: NextRequest, data: {
16
17
  params: Promise<Record<string, string[]>>;
17
18
  }) => Promise<Response>;
18
- DELETE: (req: VovkRequest, data: {
19
+ DELETE: (req: NextRequest, data: {
19
20
  params: Promise<Record<string, string[]>>;
20
21
  }) => Promise<Response>;
21
- HEAD: (req: VovkRequest, data: {
22
+ HEAD: (req: NextRequest, data: {
22
23
  params: Promise<Record<string, string[]>>;
23
24
  }) => Promise<Response>;
24
- OPTIONS: (req: VovkRequest, data: {
25
+ OPTIONS: (req: NextRequest, data: {
25
26
  params: Promise<Record<string, string[]>>;
26
27
  }) => Promise<Response>;
27
28
  respond: (status: HttpStatus, body: unknown, options?: DecoratorOptions) => Response;
package/dist/VovkApp.js CHANGED
@@ -59,7 +59,8 @@ class VovkApp {
59
59
  isError: true,
60
60
  }, options);
61
61
  };
62
- #callMethod = async (httpMethod, req, params) => {
62
+ #callMethod = async (httpMethod, nextReq, params) => {
63
+ const req = nextReq;
63
64
  const controllers = this.routes[httpMethod];
64
65
  const methodParams = {};
65
66
  const path = params[Object.keys(params)[0]];
@@ -17,7 +17,12 @@ const defaultFetcher = async ({ httpMethod, getEndpoint, validate, defaultHandle
17
17
  if (e instanceof HttpException_1.HttpException)
18
18
  throw e;
19
19
  // otherwise, throw HttpException with status 0
20
- throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, e.message ?? exports.DEFAULT_ERROR_MESSAGE, { endpoint });
20
+ throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, e.message ?? exports.DEFAULT_ERROR_MESSAGE, {
21
+ body,
22
+ query,
23
+ params,
24
+ endpoint,
25
+ });
21
26
  }
22
27
  }
23
28
  const init = {
@@ -36,7 +41,12 @@ const defaultFetcher = async ({ httpMethod, getEndpoint, validate, defaultHandle
36
41
  }
37
42
  catch (e) {
38
43
  // handle network errors
39
- throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, e?.message ?? exports.DEFAULT_ERROR_MESSAGE, { endpoint });
44
+ throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, e?.message ?? exports.DEFAULT_ERROR_MESSAGE, {
45
+ body,
46
+ query,
47
+ params,
48
+ endpoint,
49
+ });
40
50
  }
41
51
  const contentType = response.headers.get('content-type');
42
52
  if (contentType?.startsWith('application/json')) {
@@ -19,6 +19,7 @@ function createDecorator(handler, initHandler) {
19
19
  ...handlerSchema,
20
20
  // avoid override of path and httpMethod
21
21
  ...(initResult?.validation ? { validation: initResult.validation } : {}),
22
+ ...(initResult?.openapi ? { openapi: initResult.openapi } : {}),
22
23
  ...(initResult?.custom ? { custom: initResult.custom } : {}),
23
24
  },
24
25
  };
@@ -1,3 +1,4 @@
1
+ import type { NextRequest } from 'next/server';
1
2
  import { type KnownAny, type DecoratorOptions, type VovkRequest, type StaticClass } from './types';
2
3
  export declare function createVovkApp(): {
3
4
  get: {
@@ -36,25 +37,25 @@ export declare function createVovkApp(): {
36
37
  emitSchema?: boolean;
37
38
  onError?: (err: Error, req: VovkRequest) => void | Promise<void>;
38
39
  }) => {
39
- GET: (req: VovkRequest, data: {
40
+ GET: (req: NextRequest, data: {
40
41
  params: Promise<Record<string, string[]>>;
41
42
  }) => Promise<Response>;
42
- POST: (req: VovkRequest, data: {
43
+ POST: (req: NextRequest, data: {
43
44
  params: Promise<Record<string, string[]>>;
44
45
  }) => Promise<Response>;
45
- PUT: (req: VovkRequest, data: {
46
+ PUT: (req: NextRequest, data: {
46
47
  params: Promise<Record<string, string[]>>;
47
48
  }) => Promise<Response>;
48
- PATCH: (req: VovkRequest, data: {
49
+ PATCH: (req: NextRequest, data: {
49
50
  params: Promise<Record<string, string[]>>;
50
51
  }) => Promise<Response>;
51
- DELETE: (req: VovkRequest, data: {
52
+ DELETE: (req: NextRequest, data: {
52
53
  params: Promise<Record<string, string[]>>;
53
54
  }) => Promise<Response>;
54
- HEAD: (req: VovkRequest, data: {
55
+ HEAD: (req: NextRequest, data: {
55
56
  params: Promise<Record<string, string[]>>;
56
57
  }) => Promise<Response>;
57
- OPTIONS: (req: VovkRequest, data: {
58
+ OPTIONS: (req: NextRequest, data: {
58
59
  params: Promise<Record<string, string[]>>;
59
60
  }) => Promise<Response>;
60
61
  };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { createVovkApp } from './createVovkApp';
2
2
  import { HttpStatus as HttpStatus, HttpMethod as HttpMethod, type KnownAny, type VovkErrorResponse, type VovkRequest, type VovkBody, type VovkQuery, type VovkParams, type VovkReturnType, type VovkYieldType, type VovkControllerBody, type VovkControllerQuery, type VovkControllerParams, type VovkControllerYieldType, type VovkSchema, type VovkControllerSchema, type VovkHandlerSchema } from './types';
3
3
  import { type VovkClient, type VovkClientOptions, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkValidateOnClient, type VovkStreamAsyncIterable, createRPC } from './client';
4
+ import { openapi } from './openapi';
4
5
  import { HttpException } from './HttpException';
5
6
  import { createDecorator } from './createDecorator';
6
7
  import { StreamJSONResponse } from './StreamJSONResponse';
7
8
  import { generateStaticAPI } from './utils/generateStaticAPI';
8
9
  import { setClientValidatorsForHandler } from './utils/setClientValidatorsForHandler';
9
- export { type KnownAny, type VovkClient, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkStreamAsyncIterable, type VovkValidateOnClient, type VovkSchema, type VovkErrorResponse, type VovkRequest, type VovkControllerBody, type VovkControllerQuery, type VovkControllerParams, type VovkControllerYieldType, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkControllerSchema, type VovkHandlerSchema, StreamJSONResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, createRPC, generateStaticAPI, setClientValidatorsForHandler, };
10
+ export { type KnownAny, type VovkClient, type VovkClientFetcher, type VovkDefaultFetcherOptions, type VovkStreamAsyncIterable, type VovkValidateOnClient, type VovkSchema, type VovkErrorResponse, type VovkRequest, type VovkControllerBody, type VovkControllerQuery, type VovkControllerParams, type VovkControllerYieldType, type VovkBody, type VovkQuery, type VovkParams, type VovkYieldType, type VovkReturnType, type VovkClientOptions, type VovkControllerSchema, type VovkHandlerSchema, StreamJSONResponse, HttpException, HttpStatus, HttpMethod, createVovkApp, createDecorator, createRPC, generateStaticAPI, openapi, setClientValidatorsForHandler, };
10
11
  export declare const get: {
11
12
  (givenPath?: string | undefined, options?: import("./types").DecoratorOptions | undefined): ReturnType<(givenPath?: string, options?: import("./types").DecoratorOptions) => (givenTarget: KnownAny, propertyKey: string) => void>;
12
13
  auto: (options?: import("./types").DecoratorOptions) => (givenTarget: KnownAny, propertyKey: string) => void;
@@ -35,25 +36,25 @@ export declare const get: {
35
36
  emitSchema?: boolean;
36
37
  onError?: (err: Error, req: VovkRequest) => void | Promise<void>;
37
38
  }) => {
38
- GET: (req: VovkRequest, data: {
39
+ GET: (req: import("next/server").NextRequest, data: {
39
40
  params: Promise<Record<string, string[]>>;
40
41
  }) => Promise<Response>;
41
- POST: (req: VovkRequest, data: {
42
+ POST: (req: import("next/server").NextRequest, data: {
42
43
  params: Promise<Record<string, string[]>>;
43
44
  }) => Promise<Response>;
44
- PUT: (req: VovkRequest, data: {
45
+ PUT: (req: import("next/server").NextRequest, data: {
45
46
  params: Promise<Record<string, string[]>>;
46
47
  }) => Promise<Response>;
47
- PATCH: (req: VovkRequest, data: {
48
+ PATCH: (req: import("next/server").NextRequest, data: {
48
49
  params: Promise<Record<string, string[]>>;
49
50
  }) => Promise<Response>;
50
- DELETE: (req: VovkRequest, data: {
51
+ DELETE: (req: import("next/server").NextRequest, data: {
51
52
  params: Promise<Record<string, string[]>>;
52
53
  }) => Promise<Response>;
53
- HEAD: (req: VovkRequest, data: {
54
+ HEAD: (req: import("next/server").NextRequest, data: {
54
55
  params: Promise<Record<string, string[]>>;
55
56
  }) => Promise<Response>;
56
- OPTIONS: (req: VovkRequest, data: {
57
+ OPTIONS: (req: import("next/server").NextRequest, data: {
57
58
  params: Promise<Record<string, string[]>>;
58
59
  }) => Promise<Response>;
59
60
  };
package/dist/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.setClientValidatorsForHandler = exports.generateStaticAPI = exports.createRPC = exports.createDecorator = exports.createVovkApp = exports.HttpMethod = exports.HttpStatus = exports.HttpException = exports.StreamJSONResponse = void 0;
4
+ exports.initVovk = exports.prefix = exports.options = exports.head = exports.del = exports.patch = exports.put = exports.post = exports.get = exports.setClientValidatorsForHandler = exports.openapi = exports.generateStaticAPI = exports.createRPC = exports.createDecorator = exports.createVovkApp = exports.HttpMethod = exports.HttpStatus = exports.HttpException = exports.StreamJSONResponse = void 0;
5
5
  const createVovkApp_1 = require("./createVovkApp");
6
6
  Object.defineProperty(exports, "createVovkApp", { enumerable: true, get: function () { return createVovkApp_1.createVovkApp; } });
7
7
  const types_1 = require("./types");
@@ -9,6 +9,8 @@ Object.defineProperty(exports, "HttpStatus", { enumerable: true, get: function (
9
9
  Object.defineProperty(exports, "HttpMethod", { enumerable: true, get: function () { return types_1.HttpMethod; } });
10
10
  const client_1 = require("./client");
11
11
  Object.defineProperty(exports, "createRPC", { enumerable: true, get: function () { return client_1.createRPC; } });
12
+ const openapi_1 = require("./openapi");
13
+ Object.defineProperty(exports, "openapi", { enumerable: true, get: function () { return openapi_1.openapi; } });
12
14
  const HttpException_1 = require("./HttpException");
13
15
  Object.defineProperty(exports, "HttpException", { enumerable: true, get: function () { return HttpException_1.HttpException; } });
14
16
  const createDecorator_1 = require("./createDecorator");
@@ -0,0 +1,3 @@
1
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
+ import type { VovkSchema } from '../types';
3
+ export declare function fromSchema(apiRoot: string, vovkSchema: Record<string, VovkSchema>, extendWith?: Partial<OpenAPIObject>): OpenAPIObject;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fromSchema = fromSchema;
4
+ function fromSchema(apiRoot, vovkSchema, extendWith) {
5
+ const paths = {};
6
+ for (const [segmentName, schema] of Object.entries(vovkSchema)) {
7
+ for (const c of Object.values(schema.controllers)) {
8
+ for (const h of Object.values(c.handlers)) {
9
+ if (h.openapi) {
10
+ const path = '/' + [apiRoot.replace(/^\/+|\/+$/g, ''), segmentName, c.prefix, h.path].filter(Boolean).join('/');
11
+ paths[path] = paths[path] ?? {};
12
+ paths[path][h.httpMethod.toLowerCase()] = { ...h.openapi };
13
+ }
14
+ }
15
+ }
16
+ }
17
+ return {
18
+ openapi: '3.1.0',
19
+ paths,
20
+ info: extendWith?.info ?? {
21
+ title: 'API',
22
+ version: '1.0.0',
23
+ },
24
+ ...extendWith,
25
+ };
26
+ }
@@ -0,0 +1 @@
1
+ export { openapi } from './openapi';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openapi = void 0;
4
+ var openapi_1 = require("./openapi");
5
+ Object.defineProperty(exports, "openapi", { enumerable: true, get: function () { return openapi_1.openapi; } });
@@ -0,0 +1,7 @@
1
+ import type { OperationObject } from 'openapi3-ts/oas31';
2
+ import { fromSchema } from './fromSchema';
3
+ declare const openapiDecorator: (openAPIOperationObject?: OperationObject | undefined) => (target: import("..").KnownAny, propertyKey: string) => void;
4
+ export declare const openapi: typeof openapiDecorator & {
5
+ fromSchema: typeof fromSchema;
6
+ };
7
+ export {};
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openapi = void 0;
4
+ const createDecorator_1 = require("../createDecorator");
5
+ const fromSchema_1 = require("./fromSchema");
6
+ const openapiDecorator = (0, createDecorator_1.createDecorator)(null, (openAPIOperationObject = {}) => {
7
+ return (handlerSchema) => ({
8
+ ...handlerSchema,
9
+ openapi: {
10
+ ...handlerSchema?.openapi,
11
+ ...openAPIOperationObject,
12
+ },
13
+ });
14
+ });
15
+ exports.openapi = openapiDecorator;
16
+ exports.openapi.fromSchema = fromSchema_1.fromSchema;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { NextRequest } from 'next/server';
2
+ import type { OperationObject } from 'openapi3-ts/oas31';
2
3
  import type { StreamJSONResponse } from './StreamJSONResponse';
3
4
  import { VovkStreamAsyncIterable } from './client/types';
4
5
  export type KnownAny = any;
@@ -10,6 +11,7 @@ export type VovkHandlerSchema = {
10
11
  query?: KnownAny;
11
12
  body?: KnownAny;
12
13
  };
14
+ openapi?: OperationObject;
13
15
  custom?: Record<string, KnownAny>;
14
16
  };
15
17
  export type VovkControllerSchema = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk",
3
- "version": "3.0.0-draft.64",
3
+ "version": "3.0.0-draft.66",
4
4
  "main": "dist/index.js",
5
5
  "description": "RESTful RPC for Next.js - Transforms Next.js into a powerful REST API platform with RPC capabilities.",
6
6
  "repository": {
@@ -27,5 +27,8 @@
27
27
  "homepage": "https://vovk.dev",
28
28
  "peerDependencies": {
29
29
  "next": "*"
30
+ },
31
+ "optionalDependencies": {
32
+ "openapi3-ts": "^4.4.0"
30
33
  }
31
34
  }
package/src/VovkApp.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { NextRequest } from 'next/server';
1
2
  import {
2
3
  HttpMethod,
3
4
  HttpStatus,
@@ -5,10 +6,10 @@ import {
5
6
  type VovkErrorResponse,
6
7
  type VovkController,
7
8
  type DecoratorOptions,
8
- type VovkRequest,
9
9
  type KnownAny,
10
+ type VovkRequest,
10
11
  } from './types';
11
- import { HttpException as HttpException } from './HttpException';
12
+ import { HttpException } from './HttpException';
12
13
  import { StreamJSONResponse } from './StreamJSONResponse';
13
14
  import reqQuery from './utils/reqQuery';
14
15
  import reqMeta from './utils/reqMeta';
@@ -42,25 +43,25 @@ export class VovkApp {
42
43
  OPTIONS: new Map(),
43
44
  };
44
45
 
45
- GET = async (req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) =>
46
+ GET = async (req: NextRequest, data: { params: Promise<Record<string, string[]>> }) =>
46
47
  this.#callMethod(HttpMethod.GET, req, await data.params);
47
48
 
48
- POST = async (req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) =>
49
+ POST = async (req: NextRequest, data: { params: Promise<Record<string, string[]>> }) =>
49
50
  this.#callMethod(HttpMethod.POST, req, await data.params);
50
51
 
51
- PUT = async (req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) =>
52
+ PUT = async (req: NextRequest, data: { params: Promise<Record<string, string[]>> }) =>
52
53
  this.#callMethod(HttpMethod.PUT, req, await data.params);
53
54
 
54
- PATCH = async (req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) =>
55
+ PATCH = async (req: NextRequest, data: { params: Promise<Record<string, string[]>> }) =>
55
56
  this.#callMethod(HttpMethod.PATCH, req, await data.params);
56
57
 
57
- DELETE = async (req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) =>
58
+ DELETE = async (req: NextRequest, data: { params: Promise<Record<string, string[]>> }) =>
58
59
  this.#callMethod(HttpMethod.DELETE, req, await data.params);
59
60
 
60
- HEAD = async (req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) =>
61
+ HEAD = async (req: NextRequest, data: { params: Promise<Record<string, string[]>> }) =>
61
62
  this.#callMethod(HttpMethod.HEAD, req, await data.params);
62
63
 
63
- OPTIONS = async (req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) =>
64
+ OPTIONS = async (req: NextRequest, data: { params: Promise<Record<string, string[]>> }) =>
64
65
  this.#callMethod(HttpMethod.OPTIONS, req, await data.params);
65
66
 
66
67
  respond = (status: HttpStatus, body: unknown, options?: DecoratorOptions) => {
@@ -86,11 +87,8 @@ export class VovkApp {
86
87
  );
87
88
  };
88
89
 
89
- #callMethod = async (
90
- httpMethod: HttpMethod,
91
- req: VovkRequest<KnownAny, KnownAny>,
92
- params: Record<string, string[]>
93
- ) => {
90
+ #callMethod = async (httpMethod: HttpMethod, nextReq: NextRequest, params: Record<string, string[]>) => {
91
+ const req = nextReq as unknown as VovkRequest;
94
92
  const controllers = this.routes[httpMethod];
95
93
  const methodParams: Record<string, string> = {};
96
94
  const path = params[Object.keys(params)[0]];
@@ -178,7 +176,7 @@ export class VovkApp {
178
176
 
179
177
  req.vovk = {
180
178
  body: () => req.json(),
181
- query: () => reqQuery(req),
179
+ query: () => reqQuery(req as VovkRequest),
182
180
  meta: <T = KnownAny>(meta?: T | null) => reqMeta<T>(req, meta),
183
181
  form: <T = KnownAny>() => reqForm<T>(req),
184
182
  };
@@ -19,7 +19,12 @@ const defaultFetcher: VovkClientFetcher<VovkDefaultFetcherOptions> = async (
19
19
  // if HttpException is thrown, rethrow it
20
20
  if (e instanceof HttpException) throw e;
21
21
  // otherwise, throw HttpException with status 0
22
- throw new HttpException(HttpStatus.NULL, (e as Error).message ?? DEFAULT_ERROR_MESSAGE, { endpoint });
22
+ throw new HttpException(HttpStatus.NULL, (e as Error).message ?? DEFAULT_ERROR_MESSAGE, {
23
+ body,
24
+ query,
25
+ params,
26
+ endpoint,
27
+ });
23
28
  }
24
29
  }
25
30
 
@@ -40,7 +45,12 @@ const defaultFetcher: VovkClientFetcher<VovkDefaultFetcherOptions> = async (
40
45
  response = await fetch(endpoint, init);
41
46
  } catch (e) {
42
47
  // handle network errors
43
- throw new HttpException(HttpStatus.NULL, (e as Error)?.message ?? DEFAULT_ERROR_MESSAGE, { endpoint });
48
+ throw new HttpException(HttpStatus.NULL, (e as Error)?.message ?? DEFAULT_ERROR_MESSAGE, {
49
+ body,
50
+ query,
51
+ params,
52
+ endpoint,
53
+ });
44
54
  }
45
55
 
46
56
  const contentType = response.headers.get('content-type');
@@ -35,6 +35,7 @@ export function createDecorator<ARGS extends unknown[], REQUEST = VovkRequest>(
35
35
  ...handlerSchema,
36
36
  // avoid override of path and httpMethod
37
37
  ...(initResult?.validation ? { validation: initResult.validation } : {}),
38
+ ...(initResult?.openapi ? { openapi: initResult.openapi } : {}),
38
39
  ...(initResult?.custom ? { custom: initResult.custom } : {}),
39
40
  },
40
41
  };
@@ -1,3 +1,4 @@
1
+ import type { NextRequest } from 'next/server';
1
2
  import { VovkApp as VovkApp } from './VovkApp';
2
3
  import {
3
4
  HttpMethod,
@@ -131,7 +132,7 @@ export function createVovkApp() {
131
132
  controller._onError = options?.onError;
132
133
  }
133
134
 
134
- async function GET_DEV(req: VovkRequest, data: { params: Promise<Record<string, string[]>> }) {
135
+ async function GET_DEV(req: NextRequest, data: { params: Promise<Record<string, string[]>> }) {
135
136
  const params = await data.params;
136
137
  if (params[Object.keys(params)[0]]?.[0] === '_schema_') {
137
138
  // Wait for schema to be set (it can be set after decorators are called with another setTimeout)
package/src/index.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  type VovkStreamAsyncIterable,
28
28
  createRPC,
29
29
  } from './client';
30
+ import { openapi } from './openapi';
30
31
  import { HttpException } from './HttpException';
31
32
  import { createDecorator } from './createDecorator';
32
33
  import { StreamJSONResponse } from './StreamJSONResponse';
@@ -63,6 +64,7 @@ export {
63
64
  createDecorator,
64
65
  createRPC,
65
66
  generateStaticAPI,
67
+ openapi,
66
68
  setClientValidatorsForHandler,
67
69
  };
68
70
 
@@ -0,0 +1,33 @@
1
+ import type { OpenAPIObject, PathsObject } from 'openapi3-ts/oas31';
2
+ import type { HttpMethod, VovkSchema } from '../types';
3
+
4
+ export function fromSchema(
5
+ apiRoot: string,
6
+ vovkSchema: Record<string, VovkSchema>,
7
+ extendWith?: Partial<OpenAPIObject>
8
+ ): OpenAPIObject {
9
+ const paths: PathsObject = {};
10
+
11
+ for (const [segmentName, schema] of Object.entries(vovkSchema)) {
12
+ for (const c of Object.values(schema.controllers)) {
13
+ for (const h of Object.values(c.handlers)) {
14
+ if (h.openapi) {
15
+ const path =
16
+ '/' + [apiRoot.replace(/^\/+|\/+$/g, ''), segmentName, c.prefix, h.path].filter(Boolean).join('/');
17
+ paths[path] = paths[path] ?? {};
18
+ paths[path][h.httpMethod.toLowerCase() as Lowercase<HttpMethod>] = { ...h.openapi };
19
+ }
20
+ }
21
+ }
22
+ }
23
+
24
+ return {
25
+ openapi: '3.1.0',
26
+ paths,
27
+ info: extendWith?.info ?? {
28
+ title: 'API',
29
+ version: '1.0.0',
30
+ },
31
+ ...extendWith,
32
+ };
33
+ }
@@ -0,0 +1 @@
1
+ export { openapi } from './openapi';
@@ -0,0 +1,19 @@
1
+ import type { OperationObject } from 'openapi3-ts/oas31';
2
+ import { createDecorator } from '../createDecorator';
3
+ import { fromSchema } from './fromSchema';
4
+
5
+ const openapiDecorator = createDecorator(null, (openAPIOperationObject: OperationObject = {}) => {
6
+ return (handlerSchema) => ({
7
+ ...handlerSchema,
8
+ openapi: {
9
+ ...handlerSchema?.openapi,
10
+ ...openAPIOperationObject,
11
+ },
12
+ });
13
+ });
14
+
15
+ export const openapi = openapiDecorator as typeof openapiDecorator & {
16
+ fromSchema: typeof fromSchema;
17
+ };
18
+
19
+ openapi.fromSchema = fromSchema;
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { NextRequest } from 'next/server';
2
+ import type { OperationObject } from 'openapi3-ts/oas31';
2
3
  import type { StreamJSONResponse } from './StreamJSONResponse';
3
4
  import { VovkStreamAsyncIterable } from './client/types';
4
5
 
@@ -10,6 +11,7 @@ export type VovkHandlerSchema = {
10
11
  path: string;
11
12
  httpMethod: HttpMethod;
12
13
  validation?: { query?: KnownAny; body?: KnownAny };
14
+ openapi?: OperationObject;
13
15
  custom?: Record<string, KnownAny>;
14
16
  };
15
17