vovk 3.0.0-draft.74 → 3.0.0-draft.75

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.
@@ -1,120 +0,0 @@
1
- import type {
2
- KnownAny,
3
- HttpMethod,
4
- ControllerStaticMethod,
5
- VovkControllerBody,
6
- VovkControllerQuery,
7
- VovkControllerParams,
8
- VovkHandlerSchema,
9
- VovkControllerSchema,
10
- } from '../types';
11
- import type { StreamJSONResponse } from '../StreamJSONResponse';
12
- import type { NextResponse } from 'next/server';
13
-
14
- export type StaticMethodInput<T extends ControllerStaticMethod> = (VovkControllerBody<T> extends undefined | void
15
- ? { body?: undefined }
16
- : VovkControllerBody<T> extends null
17
- ? { body?: null }
18
- : { body: VovkControllerBody<T> }) &
19
- (VovkControllerQuery<T> extends undefined | void ? { query?: undefined } : { query: VovkControllerQuery<T> }) &
20
- (VovkControllerParams<T> extends undefined | void ? { params?: undefined } : { params: VovkControllerParams<T> });
21
-
22
- type ToPromise<T> = T extends PromiseLike<unknown> ? T : Promise<T>;
23
-
24
- export type VovkStreamAsyncIterable<T> = {
25
- status: number;
26
- [Symbol.dispose](): Promise<void> | void;
27
- [Symbol.asyncDispose](): Promise<void> | void;
28
- [Symbol.asyncIterator](): AsyncIterator<T>;
29
- cancel: () => Promise<void> | void;
30
- };
31
-
32
- type StaticMethodReturn<T extends ControllerStaticMethod> =
33
- ReturnType<T> extends NextResponse<infer U> | Promise<NextResponse<infer U>>
34
- ? U
35
- : ReturnType<T> extends Response | Promise<Response>
36
- ? Awaited<ReturnType<T>>
37
- : ReturnType<T>;
38
-
39
- type StaticMethodReturnPromise<T extends ControllerStaticMethod> = ToPromise<StaticMethodReturn<T>>;
40
-
41
- type ClientMethod<
42
- T extends (...args: KnownAny[]) => void | object | StreamJSONResponse<STREAM> | Promise<StreamJSONResponse<STREAM>>,
43
- OPTS extends Record<string, KnownAny>,
44
- STREAM extends KnownAny = unknown,
45
- > = (<R>(
46
- options: (StaticMethodInput<T> extends { body?: undefined | null; query?: undefined; params?: undefined }
47
- ? unknown
48
- : Parameters<T>[0] extends void
49
- ? StaticMethodInput<T>['params'] extends object
50
- ? { params: StaticMethodInput<T>['params'] }
51
- : unknown
52
- : StaticMethodInput<T>) &
53
- (Partial<
54
- OPTS & {
55
- transform: (staticMethodReturn: Awaited<StaticMethodReturn<T>>) => R;
56
- }
57
- > | void)
58
- ) => ReturnType<T> extends
59
- | Promise<StreamJSONResponse<infer U>>
60
- | StreamJSONResponse<infer U>
61
- | Iterator<infer U>
62
- | AsyncIterator<infer U>
63
- ? Promise<VovkStreamAsyncIterable<U>>
64
- : R extends object
65
- ? Promise<R>
66
- : StaticMethodReturnPromise<T>) & {
67
- schema: VovkHandlerSchema;
68
- controllerSchema: VovkControllerSchema;
69
- };
70
-
71
- type OmitNever<T> = {
72
- [K in keyof T as T[K] extends never ? never : K]: T[K];
73
- };
74
-
75
- type VovkClientWithNever<T, OPTS extends { [key: string]: KnownAny }> = {
76
- [K in keyof T]: T[K] extends (...args: KnownAny) => KnownAny ? ClientMethod<T[K], OPTS> : never;
77
- };
78
-
79
- export type VovkClient<T, OPTS extends { [key: string]: KnownAny }> = OmitNever<VovkClientWithNever<T, OPTS>>;
80
-
81
- export type VovkClientFetcher<OPTS extends Record<string, KnownAny> = Record<string, never>, T = KnownAny> = (
82
- options: {
83
- name: keyof T;
84
- httpMethod: HttpMethod;
85
- getEndpoint: (data: {
86
- apiRoot: string;
87
- params: { [key: string]: string };
88
- query: { [key: string]: string };
89
- }) => string;
90
- validate: (input: { body?: unknown; query?: unknown; endpoint: string }) => void | Promise<void>;
91
- defaultStreamHandler: (response: Response) => Promise<VovkStreamAsyncIterable<unknown>>;
92
- defaultHandler: (response: Response) => Promise<unknown>;
93
- },
94
- input: {
95
- body: unknown;
96
- query: { [key: string]: string };
97
- params: { [key: string]: string };
98
- } & OPTS
99
- ) => KnownAny;
100
-
101
- // `RequestInit` is the type of options passed to fetch function
102
- export interface VovkDefaultFetcherOptions extends Omit<RequestInit, 'body' | 'method'> {
103
- reactNative?: { textStreaming: boolean };
104
- apiRoot?: string;
105
- segmentName?: string;
106
- disableClientValidation?: boolean;
107
- validateOnClient?: VovkValidateOnClient;
108
- fetcher?: VovkClientFetcher;
109
- }
110
-
111
- export type VovkValidateOnClient = (
112
- input: { body?: unknown; query?: unknown; endpoint: string },
113
- validators: { body?: unknown; query?: unknown }
114
- ) => void | Promise<void>;
115
-
116
- export type VovkClientOptions<OPTS extends Record<string, KnownAny> = Record<string, never>> = {
117
- fetcher?: VovkClientFetcher<OPTS>;
118
- validateOnClient?: VovkValidateOnClient;
119
- defaultOptions?: Partial<OPTS>;
120
- };
@@ -1,61 +0,0 @@
1
- import type { VovkHandlerSchema, KnownAny, VovkController, VovkRequest } from './types';
2
-
3
- type Next = () => Promise<unknown>;
4
-
5
- export function createDecorator<ARGS extends unknown[], REQUEST = VovkRequest>(
6
- handler: null | ((this: VovkController, req: REQUEST, next: Next, ...args: ARGS) => unknown),
7
- initHandler?: (
8
- this: VovkController,
9
- ...args: ARGS
10
- ) =>
11
- | Omit<VovkHandlerSchema, 'path' | 'httpMethod'>
12
- | ((handlerSchema: VovkHandlerSchema | null) => Omit<Partial<VovkHandlerSchema>, 'path' | 'httpMethod'>)
13
- | null
14
- | undefined
15
- ) {
16
- return function decoratorCreator(...args: ARGS) {
17
- return function decorator(target: KnownAny, propertyKey: string) {
18
- const controller = target as VovkController;
19
-
20
- const originalMethod = controller[propertyKey] as ((...args: KnownAny) => KnownAny) & {
21
- _sourceMethod?: ((...args: KnownAny) => KnownAny);
22
- _onSettled?: (controller: VovkController) => void;
23
- };
24
- if (typeof originalMethod !== 'function') {
25
- throw new Error(`Unable to decorate: ${propertyKey} is not a function`);
26
- }
27
- const sourceMethod = originalMethod._sourceMethod ?? originalMethod;
28
-
29
- const method = function method(req: REQUEST, params?: unknown) {
30
- const next: Next = async () => {
31
- return (await originalMethod.call(controller, req, params)) as unknown;
32
- };
33
-
34
- return handler ? handler.call(controller, req, next, ...args) : next();
35
- };
36
-
37
- controller[propertyKey] = method;
38
-
39
- method._controller = controller;
40
- method._sourceMethod = sourceMethod;
41
- // TODO define internal method type
42
- (originalMethod as unknown as { _controller: VovkController })._controller = controller;
43
- originalMethod?._onSettled?.(controller);
44
-
45
- const handlerSchema: VovkHandlerSchema | null = controller._handlers?.[propertyKey] ?? null;
46
- const initResultReturn = initHandler?.call(controller, ...args);
47
- const initResult = typeof initResultReturn === 'function' ? initResultReturn(handlerSchema) : initResultReturn;
48
-
49
- controller._handlers = {
50
- ...controller._handlers,
51
- [propertyKey]: {
52
- ...handlerSchema,
53
- // avoid override of path and httpMethod
54
- ...(initResult?.validation ? { validation: initResult.validation } : {}),
55
- ...(initResult?.openapi ? { openapi: initResult.openapi } : {}),
56
- ...(initResult?.custom ? { custom: initResult.custom } : {}),
57
- },
58
- };
59
- };
60
- };
61
- }
@@ -1,168 +0,0 @@
1
- import type { NextRequest } from 'next/server';
2
- import { VovkApp as VovkApp } from './VovkApp';
3
- import {
4
- HttpMethod,
5
- type KnownAny,
6
- type RouteHandler,
7
- type VovkController,
8
- type DecoratorOptions,
9
- type VovkRequest,
10
- type StaticClass,
11
- VovkHandlerSchema,
12
- } from './types';
13
- import getSchema from './utils/getSchema';
14
-
15
- const trimPath = (path: string) => path.trim().replace(/^\/|\/$/g, '');
16
- const isClass = (func: unknown) => typeof func === 'function' && /class/.test(func.toString());
17
- const toKebabCase = (str: string) =>
18
- str
19
- .replace(/([A-Z])/g, '-$1')
20
- .toLowerCase()
21
- .replace(/^-/, '');
22
-
23
- export function createVovkApp() {
24
- const vovkApp = new VovkApp();
25
-
26
- const createHTTPDecorator = (httpMethod: HttpMethod) => {
27
- const assignSchema = (
28
- controller: VovkController,
29
- propertyKey: string,
30
- path: string,
31
- options?: DecoratorOptions
32
- ) => {
33
- if (typeof window !== 'undefined') {
34
- throw new Error(
35
- 'Decorators are intended for server-side use only. You have probably imported a controller on the client-side.'
36
- );
37
- }
38
- if (!isClass(controller)) {
39
- let decoratorName = httpMethod.toLowerCase();
40
- if (decoratorName === 'delete') decoratorName = 'del';
41
- throw new Error(
42
- `Decorator must be used on a static class method. Check the controller method named "${propertyKey}" used with @${decoratorName}.`
43
- );
44
- }
45
-
46
- const methods: Record<string, RouteHandler> = vovkApp.routes[httpMethod].get(controller) ?? {};
47
- vovkApp.routes[httpMethod].set(controller, methods);
48
-
49
- controller._handlers = {
50
- ...controller._handlers,
51
- [propertyKey]: {
52
- ...((controller._handlers ?? {})[propertyKey] as Partial<VovkHandlerSchema>),
53
- path,
54
- httpMethod,
55
- },
56
- };
57
-
58
- const originalMethod = controller[propertyKey] as ((...args: KnownAny) => KnownAny) & {
59
- _controller: VovkController;
60
- _sourceMethod?: (...args: KnownAny) => KnownAny;
61
- };
62
-
63
- originalMethod._controller = controller;
64
- originalMethod._sourceMethod = originalMethod._sourceMethod ?? originalMethod;
65
-
66
- methods[path] = controller[propertyKey] as RouteHandler;
67
- methods[path]._options = options;
68
- };
69
-
70
- function decoratorCreator(givenPath = '', options?: DecoratorOptions) {
71
- const path = trimPath(givenPath);
72
-
73
- function decorator(givenTarget: KnownAny, propertyKey: string) {
74
- const target = givenTarget as VovkController;
75
- assignSchema(target, propertyKey, path, options);
76
- }
77
-
78
- return decorator;
79
- }
80
-
81
- const auto = (options?: DecoratorOptions) => {
82
- function decorator(givenTarget: KnownAny, propertyKey: string) {
83
- const controller = givenTarget as VovkController;
84
- const methods: Record<string, RouteHandler> = vovkApp.routes[httpMethod].get(controller) ?? {};
85
- vovkApp.routes[httpMethod].set(controller, methods);
86
-
87
- controller._handlers = {
88
- ...controller._handlers,
89
- [propertyKey]: {
90
- ...(controller._handlers ?? {})[propertyKey],
91
- httpMethod,
92
- },
93
- };
94
-
95
- assignSchema(controller, propertyKey, toKebabCase(propertyKey), options);
96
- }
97
-
98
- return decorator;
99
- };
100
-
101
- const enhancedDecoratorCreator = decoratorCreator as {
102
- (...args: Parameters<typeof decoratorCreator>): ReturnType<typeof decoratorCreator>;
103
- auto: typeof auto;
104
- };
105
-
106
- enhancedDecoratorCreator.auto = auto;
107
-
108
- return enhancedDecoratorCreator;
109
- };
110
-
111
- const prefix = (givenPath = '') => {
112
- const path = trimPath(givenPath);
113
-
114
- return (givenTarget: KnownAny) => {
115
- const controller = givenTarget as VovkController;
116
- controller._prefix = path;
117
-
118
- return givenTarget;
119
- };
120
- };
121
-
122
- const initVovk = (options: {
123
- segmentName?: string;
124
- controllers: Record<string, StaticClass>;
125
- exposeValidation?: boolean;
126
- emitSchema?: boolean;
127
- onError?: (err: Error, req: VovkRequest) => void | Promise<void>;
128
- }) => {
129
- for (const [controllerName, controller] of Object.entries(options.controllers) as [string, VovkController][]) {
130
- controller._controllerName = controllerName;
131
- controller._activated = true;
132
- controller._onError = options?.onError;
133
- }
134
-
135
- async function GET_DEV(req: NextRequest, data: { params: Promise<Record<string, string[]>> }) {
136
- const params = await data.params;
137
- if (params[Object.keys(params)[0]]?.[0] === '_schema_') {
138
- // Wait for schema to be set (it can be set after decorators are called with another setTimeout)
139
- await new Promise((resolve) => setTimeout(resolve, 10));
140
- const schema = getSchema(options);
141
- return vovkApp.respond(200, { schema });
142
- }
143
- return vovkApp.GET(req, data);
144
- }
145
-
146
- return {
147
- GET: process.env.NODE_ENV === 'development' ? GET_DEV : vovkApp.GET,
148
- POST: vovkApp.POST,
149
- PUT: vovkApp.PUT,
150
- PATCH: vovkApp.PATCH,
151
- DELETE: vovkApp.DELETE,
152
- HEAD: vovkApp.HEAD,
153
- OPTIONS: vovkApp.OPTIONS,
154
- };
155
- };
156
-
157
- return {
158
- get: createHTTPDecorator(HttpMethod.GET),
159
- post: createHTTPDecorator(HttpMethod.POST),
160
- put: createHTTPDecorator(HttpMethod.PUT),
161
- patch: createHTTPDecorator(HttpMethod.PATCH),
162
- del: createHTTPDecorator(HttpMethod.DELETE),
163
- head: createHTTPDecorator(HttpMethod.HEAD),
164
- options: createHTTPDecorator(HttpMethod.OPTIONS),
165
- prefix,
166
- initVovk,
167
- };
168
- }
package/src/index.ts DELETED
@@ -1,71 +0,0 @@
1
- import { createVovkApp } from './createVovkApp';
2
- import {
3
- HttpStatus as HttpStatus,
4
- HttpMethod as HttpMethod,
5
- type KnownAny,
6
- type VovkErrorResponse,
7
- type VovkRequest,
8
- type VovkBody,
9
- type VovkQuery,
10
- type VovkParams,
11
- type VovkReturnType,
12
- type VovkYieldType,
13
- type VovkControllerBody,
14
- type VovkControllerQuery,
15
- type VovkControllerParams,
16
- type VovkControllerYieldType,
17
- type VovkSchema,
18
- type VovkControllerSchema,
19
- type VovkHandlerSchema,
20
- } from './types';
21
- import {
22
- type VovkClient,
23
- type VovkClientOptions,
24
- type VovkClientFetcher,
25
- type VovkDefaultFetcherOptions,
26
- type VovkValidateOnClient,
27
- type VovkStreamAsyncIterable,
28
- createRPC,
29
- } from './client';
30
- import { openapi } from './openapi';
31
- import { HttpException } from './HttpException';
32
- import { createDecorator } from './createDecorator';
33
- import { StreamJSONResponse } from './StreamJSONResponse';
34
- import { generateStaticAPI } from './utils/generateStaticAPI';
35
- import { setHandlerValidation } from './utils/setHandlerValidation';
36
-
37
- export {
38
- type KnownAny,
39
- type VovkClient,
40
- type VovkClientFetcher,
41
- type VovkDefaultFetcherOptions,
42
- type VovkStreamAsyncIterable,
43
- type VovkValidateOnClient,
44
- type VovkSchema,
45
- type VovkErrorResponse,
46
- type VovkRequest,
47
- type VovkControllerBody,
48
- type VovkControllerQuery,
49
- type VovkControllerParams,
50
- type VovkControllerYieldType,
51
- type VovkBody,
52
- type VovkQuery,
53
- type VovkParams,
54
- type VovkYieldType,
55
- type VovkReturnType,
56
- type VovkClientOptions,
57
- type VovkControllerSchema,
58
- type VovkHandlerSchema,
59
- StreamJSONResponse,
60
- HttpException,
61
- HttpStatus,
62
- HttpMethod,
63
- createVovkApp,
64
- createDecorator,
65
- createRPC,
66
- generateStaticAPI,
67
- openapi,
68
- setHandlerValidation,
69
- };
70
-
71
- export const { get, post, put, patch, del, head, options, prefix, initVovk } = createVovkApp();
@@ -1,33 +0,0 @@
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
- ...extendWith,
26
- openapi: '3.1.0',
27
- info: extendWith?.info ?? {
28
- title: 'API',
29
- version: '1.0.0',
30
- },
31
- paths,
32
- };
33
- }
@@ -1 +0,0 @@
1
- export { openapi } from './openapi';
@@ -1,85 +0,0 @@
1
- import type { OperationObject } from 'openapi3-ts/oas31';
2
- import { createDecorator } from '../createDecorator';
3
- import { fromSchema } from './fromSchema';
4
-
5
- type OperationObjectWithCustomProperties = OperationObject & {
6
- [key in `${'x' | 'X'}-${string}`]: any;
7
- };
8
-
9
- type SimpleJsonSchema = {
10
- type: 'object';
11
- properties: Record<string, any>;
12
- required?: string[];
13
- };
14
-
15
- const openapiDecorator = createDecorator(null, (openAPIOperationObject: OperationObjectWithCustomProperties = {}) => {
16
- return (handlerSchema) => ({
17
- ...handlerSchema,
18
- openapi: {
19
- ...handlerSchema?.openapi,
20
- ...(handlerSchema?.validation?.output &&
21
- 'type' in handlerSchema.validation.output &&
22
- 'properties' in handlerSchema.validation.output
23
- ? {
24
- responses: {
25
- 200: {
26
- description:
27
- 'description' in handlerSchema.validation.output
28
- ? handlerSchema.validation.output.description
29
- : 'Success',
30
- content: {
31
- 'application/json': {
32
- schema: handlerSchema.validation.output,
33
- },
34
- },
35
- },
36
- },
37
- }
38
- : {}),
39
- ...(handlerSchema?.validation?.body &&
40
- 'type' in handlerSchema.validation.body &&
41
- 'properties' in handlerSchema.validation.body
42
- ? {
43
- requestBody: {
44
- description:
45
- 'description' in handlerSchema.validation.body
46
- ? handlerSchema.validation.body.description
47
- : 'Request body',
48
- required: true,
49
- content: {
50
- 'application/json': {
51
- schema: handlerSchema.validation.body,
52
- },
53
- },
54
- },
55
- }
56
- : {}),
57
- ...(handlerSchema?.validation?.query &&
58
- 'type' in handlerSchema.validation.query &&
59
- 'properties' in handlerSchema.validation.query
60
- ? {
61
- parameters: Object.entries((handlerSchema.validation.query as SimpleJsonSchema).properties)
62
- .filter(([, propSchema]) => propSchema.type === 'string')
63
- .map(([propName, propSchema]) => ({
64
- name: propName,
65
- in: 'query',
66
- description: propSchema.description || '',
67
- required: handlerSchema.validation?.query.required
68
- ? handlerSchema.validation.query.required.includes(propName)
69
- : false,
70
- schema: {
71
- type: 'string',
72
- },
73
- })),
74
- }
75
- : {}),
76
- ...openAPIOperationObject,
77
- },
78
- });
79
- });
80
-
81
- export const openapi = openapiDecorator as typeof openapiDecorator & {
82
- fromSchema: typeof fromSchema;
83
- };
84
-
85
- openapi.fromSchema = fromSchema;