vovk 3.0.0-beta.4 → 3.0.0-beta.6
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/.DS_Store +0 -0
- package/.npmignore +1 -0
- package/.turbo/turbo-build.log +6 -0
- package/.turbo/turbo-ncu.log +9 -0
- package/HttpException.ts +16 -0
- package/Segment.ts +238 -0
- package/StreamResponse.ts +61 -0
- package/client/clientizeController.ts +141 -0
- package/client/defaultFetcher.ts +60 -0
- package/client/defaultHandler.ts +22 -0
- package/client/defaultStreamHandler.ts +88 -0
- package/client/index.ts +9 -0
- package/client/types.ts +116 -0
- package/createDecorator.ts +65 -0
- package/createSegment.ts +239 -0
- package/dist/.npmignore +1 -0
- package/dist/HttpException.d.ts +7 -0
- package/dist/HttpException.js +15 -0
- package/dist/README.md +111 -0
- package/dist/Segment.d.ts +27 -0
- package/dist/Segment.js +187 -0
- package/dist/StreamResponse.d.ts +16 -0
- package/dist/StreamResponse.js +53 -0
- package/dist/client/clientizeController.d.ts +4 -0
- package/dist/client/clientizeController.js +92 -0
- package/dist/client/defaultFetcher.d.ts +4 -0
- package/dist/client/defaultFetcher.js +49 -0
- package/dist/client/defaultHandler.d.ts +2 -0
- package/dist/client/defaultHandler.js +21 -0
- package/dist/client/defaultStreamHandler.d.ts +4 -0
- package/dist/client/defaultStreamHandler.js +82 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +5 -0
- package/dist/client/types.d.ts +101 -0
- package/dist/client/types.js +2 -0
- package/dist/createDecorator.d.ts +4 -0
- package/dist/createDecorator.js +38 -0
- package/dist/createSegment.d.ts +63 -0
- package/dist/createSegment.js +167 -0
- package/dist/generateStaticAPI.d.ts +3 -0
- package/dist/generateStaticAPI.js +19 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.js +20 -0
- package/dist/package-lock.json +472 -0
- package/dist/package.json +28 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +169 -0
- package/dist/types.js +65 -0
- package/dist/utils/reqMeta.d.ts +3 -0
- package/dist/utils/reqMeta.js +13 -0
- package/dist/utils/reqQuery.d.ts +3 -0
- package/dist/utils/reqQuery.js +25 -0
- package/dist/utils/setClientValidatorsForHandler.d.ts +5 -0
- package/dist/utils/setClientValidatorsForHandler.js +28 -0
- package/dist/utils/shim.d.ts +0 -0
- package/dist/utils/shim.js +17 -0
- package/dist/worker/index.d.ts +3 -0
- package/dist/worker/index.js +7 -0
- package/dist/worker/promisifyWorker.d.ts +2 -0
- package/dist/worker/promisifyWorker.js +142 -0
- package/dist/worker/types.d.ts +31 -0
- package/dist/worker/types.js +2 -0
- package/dist/worker/worker.d.ts +1 -0
- package/dist/worker/worker.js +45 -0
- package/generateStaticAPI.ts +19 -0
- package/index.ts +59 -0
- package/package.json +1 -1
- package/tsconfig.json +9 -0
- package/types.ts +237 -0
- package/utils/reqMeta.ts +17 -0
- package/utils/reqQuery.ts +27 -0
- package/utils/setClientValidatorsForHandler.ts +45 -0
- package/utils/shim.ts +17 -0
- package/worker/index.ts +4 -0
- package/worker/promisifyWorker.ts +159 -0
- package/worker/types.ts +45 -0
- package/worker/worker.ts +55 -0
package/client/types.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
_KnownAny as KnownAny,
|
|
3
|
+
_HttpMethod as HttpMethod,
|
|
4
|
+
_ControllerStaticMethod,
|
|
5
|
+
_VovkControllerBody,
|
|
6
|
+
_VovkControllerQuery,
|
|
7
|
+
_VovkControllerParams,
|
|
8
|
+
} from '../types';
|
|
9
|
+
import { _StreamResponse as StreamResponse } from '../StreamResponse';
|
|
10
|
+
import type { NextResponse } from 'next/server';
|
|
11
|
+
|
|
12
|
+
export type _StaticMethodInput<T extends _ControllerStaticMethod> = (_VovkControllerBody<T> extends undefined | void
|
|
13
|
+
? { body?: undefined }
|
|
14
|
+
: _VovkControllerBody<T> extends null
|
|
15
|
+
? { body?: null }
|
|
16
|
+
: { body: _VovkControllerBody<T> }) &
|
|
17
|
+
(_VovkControllerQuery<T> extends undefined | void ? { query?: undefined } : { query: _VovkControllerQuery<T> }) &
|
|
18
|
+
(_VovkControllerParams<T> extends undefined | void ? { params?: undefined } : { params: _VovkControllerParams<T> });
|
|
19
|
+
|
|
20
|
+
type ToPromise<T> = T extends PromiseLike<unknown> ? T : Promise<T>;
|
|
21
|
+
|
|
22
|
+
export type _StreamAsyncIterator<T> = {
|
|
23
|
+
status: number;
|
|
24
|
+
[Symbol.dispose](): Promise<void> | void;
|
|
25
|
+
[Symbol.asyncDispose](): Promise<void> | void;
|
|
26
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
27
|
+
cancel: () => Promise<void> | void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type StaticMethodReturn<T extends _ControllerStaticMethod> =
|
|
31
|
+
ReturnType<T> extends NextResponse<infer U> | Promise<NextResponse<infer U>>
|
|
32
|
+
? U
|
|
33
|
+
: ReturnType<T> extends Response | Promise<Response>
|
|
34
|
+
? unknown
|
|
35
|
+
: ReturnType<T>;
|
|
36
|
+
|
|
37
|
+
type StaticMethodReturnPromise<T extends _ControllerStaticMethod> = ToPromise<StaticMethodReturn<T>>;
|
|
38
|
+
|
|
39
|
+
type ClientMethod<
|
|
40
|
+
T extends (...args: KnownAny[]) => void | object | StreamResponse<STREAM> | Promise<StreamResponse<STREAM>>,
|
|
41
|
+
OPTS extends Record<string, KnownAny>,
|
|
42
|
+
STREAM extends KnownAny = unknown,
|
|
43
|
+
> = <R>(
|
|
44
|
+
options: (_StaticMethodInput<T> extends { body?: undefined | null; query?: undefined; params?: undefined }
|
|
45
|
+
? unknown
|
|
46
|
+
: Parameters<T>[0] extends void
|
|
47
|
+
? _StaticMethodInput<T>['params'] extends object
|
|
48
|
+
? { params: _StaticMethodInput<T>['params'] }
|
|
49
|
+
: unknown
|
|
50
|
+
: _StaticMethodInput<T>) &
|
|
51
|
+
(Partial<
|
|
52
|
+
OPTS & {
|
|
53
|
+
transform: (staticMethodReturn: Awaited<StaticMethodReturn<T>>) => R;
|
|
54
|
+
}
|
|
55
|
+
> | void)
|
|
56
|
+
) => ReturnType<T> extends
|
|
57
|
+
| Promise<StreamResponse<infer U>>
|
|
58
|
+
| StreamResponse<infer U>
|
|
59
|
+
| Iterator<infer U>
|
|
60
|
+
| AsyncIterator<infer U>
|
|
61
|
+
? Promise<_StreamAsyncIterator<U>>
|
|
62
|
+
: R extends object
|
|
63
|
+
? Promise<R>
|
|
64
|
+
: StaticMethodReturnPromise<T>;
|
|
65
|
+
|
|
66
|
+
type OmitNever<T> = {
|
|
67
|
+
[K in keyof T as T[K] extends never ? never : K]: T[K];
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
type _VovkClientWithNever<T, OPTS extends { [key: string]: KnownAny }> = {
|
|
71
|
+
[K in keyof T]: T[K] extends (...args: KnownAny) => KnownAny ? ClientMethod<T[K], OPTS> : never;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type _VovkClient<T, OPTS extends { [key: string]: KnownAny }> = OmitNever<_VovkClientWithNever<T, OPTS>>;
|
|
75
|
+
|
|
76
|
+
export type _VovkClientFetcher<OPTS extends Record<string, KnownAny> = Record<string, never>, T = KnownAny> = (
|
|
77
|
+
options: {
|
|
78
|
+
name: keyof T;
|
|
79
|
+
httpMethod: HttpMethod;
|
|
80
|
+
getEndpoint: (data: {
|
|
81
|
+
prefix: string;
|
|
82
|
+
segmentName: string;
|
|
83
|
+
params: { [key: string]: string };
|
|
84
|
+
query: { [key: string]: string };
|
|
85
|
+
}) => string;
|
|
86
|
+
validate: (input: { body?: unknown; query?: unknown; endpoint: string }) => void | Promise<void>;
|
|
87
|
+
defaultStreamHandler: (response: Response) => Promise<_StreamAsyncIterator<unknown>>;
|
|
88
|
+
defaultHandler: (response: Response) => Promise<unknown>;
|
|
89
|
+
},
|
|
90
|
+
input: {
|
|
91
|
+
body: unknown;
|
|
92
|
+
query: { [key: string]: string };
|
|
93
|
+
params: { [key: string]: string };
|
|
94
|
+
} & OPTS
|
|
95
|
+
) => KnownAny;
|
|
96
|
+
|
|
97
|
+
// `RequestInit` is the type of options passed to fetch function
|
|
98
|
+
export interface _VovkDefaultFetcherOptions extends Omit<RequestInit, 'body' | 'method'> {
|
|
99
|
+
reactNative?: { textStreaming: boolean };
|
|
100
|
+
prefix?: string;
|
|
101
|
+
segmentName: string;
|
|
102
|
+
disableClientValidation?: boolean;
|
|
103
|
+
validateOnClient?: _VovkValidateOnClient;
|
|
104
|
+
fetcher?: _VovkClientFetcher;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type _VovkValidateOnClient = (
|
|
108
|
+
input: { body?: unknown; query?: unknown; endpoint: string },
|
|
109
|
+
validators: { body?: unknown; query?: unknown }
|
|
110
|
+
) => void | Promise<void>;
|
|
111
|
+
|
|
112
|
+
export type _VovkClientOptions<OPTS extends Record<string, KnownAny> = Record<string, never>> = {
|
|
113
|
+
fetcher?: _VovkClientFetcher<OPTS>;
|
|
114
|
+
validateOnClient?: _VovkValidateOnClient;
|
|
115
|
+
defaultOptions?: Partial<OPTS>;
|
|
116
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
_HandlerMetadata as HandlerMetadata,
|
|
3
|
+
_KnownAny as KnownAny,
|
|
4
|
+
_VovkController as VovkController,
|
|
5
|
+
_VovkRequest as VovkRequest,
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
type Next = () => Promise<unknown>;
|
|
9
|
+
|
|
10
|
+
export function _createDecorator<ARGS extends unknown[], REQUEST = VovkRequest>(
|
|
11
|
+
handler: null | ((this: VovkController, req: REQUEST, next: Next, ...args: ARGS) => unknown),
|
|
12
|
+
initHandler?: (
|
|
13
|
+
this: VovkController,
|
|
14
|
+
...args: ARGS
|
|
15
|
+
) =>
|
|
16
|
+
| Omit<HandlerMetadata, 'path' | 'httpMethod'>
|
|
17
|
+
| ((handlerMetadata: HandlerMetadata | null) => Omit<HandlerMetadata, 'path' | 'httpMethod'>)
|
|
18
|
+
| null
|
|
19
|
+
| undefined
|
|
20
|
+
) {
|
|
21
|
+
return function decoratorCreator(...args: ARGS) {
|
|
22
|
+
return function decorator(target: KnownAny, propertyKey: string) {
|
|
23
|
+
const controller = target as VovkController;
|
|
24
|
+
|
|
25
|
+
const originalMethod = controller[propertyKey] as ((...args: KnownAny) => KnownAny) & {
|
|
26
|
+
_sourceMethod?: (...args: KnownAny) => KnownAny;
|
|
27
|
+
};
|
|
28
|
+
if (typeof originalMethod !== 'function') {
|
|
29
|
+
throw new Error(`Unable to decorate: ${propertyKey} is not a function`);
|
|
30
|
+
}
|
|
31
|
+
const sourceMethod = originalMethod._sourceMethod ?? originalMethod;
|
|
32
|
+
|
|
33
|
+
const handlerMetadata: HandlerMetadata | null = controller._handlers?.[propertyKey] ?? null;
|
|
34
|
+
const initResultReturn = initHandler?.call(controller, ...args);
|
|
35
|
+
const initResult = typeof initResultReturn === 'function' ? initResultReturn(handlerMetadata) : initResultReturn;
|
|
36
|
+
|
|
37
|
+
controller._handlers = {
|
|
38
|
+
...controller._handlers,
|
|
39
|
+
[propertyKey]: {
|
|
40
|
+
...handlerMetadata,
|
|
41
|
+
// avoid override of path and httpMethod
|
|
42
|
+
...(initResult?.clientValidators ? { clientValidators: initResult.clientValidators } : {}),
|
|
43
|
+
...(initResult?.customMetadata ? { customMetadata: initResult.customMetadata } : {}),
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const method = function method(req: REQUEST, params?: unknown) {
|
|
48
|
+
const next: Next = async () => {
|
|
49
|
+
return (await originalMethod.call(controller, req, params)) as unknown;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return handler ? handler.call(controller, req, next, ...args) : next();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
method._controller = controller;
|
|
56
|
+
|
|
57
|
+
// TODO define internal method type
|
|
58
|
+
(originalMethod as unknown as { _controller: VovkController })._controller = controller;
|
|
59
|
+
|
|
60
|
+
controller[propertyKey] = method;
|
|
61
|
+
|
|
62
|
+
method._sourceMethod = sourceMethod;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}
|
package/createSegment.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { _Segment as Segment } from './Segment';
|
|
3
|
+
import {
|
|
4
|
+
_HttpMethod as HttpMethod,
|
|
5
|
+
type _KnownAny as KnownAny,
|
|
6
|
+
type _RouteHandler as RouteHandler,
|
|
7
|
+
type _VovkController as VovkController,
|
|
8
|
+
type _VovkWorker as VovkWorker,
|
|
9
|
+
type _DecoratorOptions as DecoratorOptions,
|
|
10
|
+
type _VovkRequest as VovkRequest,
|
|
11
|
+
type _VovkMetadata as VovkMetadata,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
const trimPath = (path: string) => path.trim().replace(/^\/|\/$/g, '');
|
|
15
|
+
const isClass = (func: unknown) => typeof func === 'function' && /class/.test(func.toString());
|
|
16
|
+
const toKebabCase = (str: string) => {
|
|
17
|
+
return (
|
|
18
|
+
str
|
|
19
|
+
// Insert a hyphen before each uppercase letter, then convert to lowercase
|
|
20
|
+
.replace(/([A-Z])/g, '-$1')
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
// Remove any leading hyphen if the original string started with an uppercase letter
|
|
23
|
+
.replace(/^-/, '')
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function _createSegment() {
|
|
28
|
+
const r = new Segment();
|
|
29
|
+
|
|
30
|
+
const getDecoratorCreator = (httpMethod: HttpMethod) => {
|
|
31
|
+
const assignMetadata = (
|
|
32
|
+
controller: VovkController,
|
|
33
|
+
propertyKey: string,
|
|
34
|
+
path: string,
|
|
35
|
+
options?: DecoratorOptions
|
|
36
|
+
) => {
|
|
37
|
+
if (typeof window !== 'undefined') {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'Decorators are intended for server-side use only. You have probably imported a controller on the client-side.'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (!isClass(controller)) {
|
|
43
|
+
let decoratorName = httpMethod.toLowerCase();
|
|
44
|
+
if (decoratorName === 'delete') decoratorName = 'del';
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Decorator must be used on a static class method. Check the controller method named "${propertyKey}" used with @${decoratorName}.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const methods: Record<string, RouteHandler> = r._routes[httpMethod].get(controller) ?? {};
|
|
51
|
+
r._routes[httpMethod].set(controller, methods);
|
|
52
|
+
|
|
53
|
+
controller._handlers = {
|
|
54
|
+
...controller._handlers,
|
|
55
|
+
[propertyKey]: {
|
|
56
|
+
...(controller._handlers ?? {})[propertyKey],
|
|
57
|
+
path,
|
|
58
|
+
httpMethod,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const originalMethod = controller[propertyKey] as ((...args: KnownAny) => KnownAny) & {
|
|
63
|
+
_controller: VovkController;
|
|
64
|
+
_sourceMethod?: (...args: KnownAny) => KnownAny;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
originalMethod._controller = controller;
|
|
68
|
+
originalMethod._sourceMethod = originalMethod._sourceMethod ?? originalMethod;
|
|
69
|
+
|
|
70
|
+
methods[path] = controller[propertyKey] as RouteHandler;
|
|
71
|
+
methods[path]._options = options;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function decoratorCreator(givenPath = '', options?: DecoratorOptions) {
|
|
75
|
+
const path = trimPath(givenPath);
|
|
76
|
+
|
|
77
|
+
function decorator(givenTarget: KnownAny, propertyKey: string) {
|
|
78
|
+
const target = givenTarget as VovkController;
|
|
79
|
+
assignMetadata(target, propertyKey, path, options);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return decorator;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const auto = (options?: DecoratorOptions) => {
|
|
86
|
+
function decorator(givenTarget: KnownAny, propertyKey: string) {
|
|
87
|
+
const controller = givenTarget as VovkController;
|
|
88
|
+
const methods: Record<string, RouteHandler> = r._routes[httpMethod].get(controller) ?? {};
|
|
89
|
+
r._routes[httpMethod].set(controller, methods);
|
|
90
|
+
|
|
91
|
+
controller._handlers = {
|
|
92
|
+
...controller._handlers,
|
|
93
|
+
[propertyKey]: {
|
|
94
|
+
...(controller._handlers ?? {})[propertyKey],
|
|
95
|
+
httpMethod,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
assignMetadata(controller, propertyKey, toKebabCase(propertyKey), options);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return decorator;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const enhancedDecoratorCreator = decoratorCreator as {
|
|
106
|
+
(...args: Parameters<typeof decoratorCreator>): ReturnType<typeof decoratorCreator>;
|
|
107
|
+
auto: typeof auto;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
enhancedDecoratorCreator.auto = auto;
|
|
111
|
+
|
|
112
|
+
return enhancedDecoratorCreator;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const prefix = (givenPath = '') => {
|
|
116
|
+
const path = trimPath(givenPath);
|
|
117
|
+
|
|
118
|
+
return (givenTarget: KnownAny) => {
|
|
119
|
+
const controller = givenTarget as VovkController;
|
|
120
|
+
controller._prefix = path;
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
122
|
+
return givenTarget;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const getMetadata = (options: {
|
|
127
|
+
emitMetadata?: boolean;
|
|
128
|
+
segmentName?: string;
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
130
|
+
controllers: Record<string, Function>;
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
132
|
+
workers?: Record<string, Function>;
|
|
133
|
+
exposeValidation?: boolean;
|
|
134
|
+
}) => {
|
|
135
|
+
const exposeValidation = options?.exposeValidation ?? true;
|
|
136
|
+
const emitMetadata = options.emitMetadata ?? true;
|
|
137
|
+
const metadata: VovkMetadata = {
|
|
138
|
+
emitMetadata,
|
|
139
|
+
segmentName: options.segmentName ?? '',
|
|
140
|
+
controllers: {},
|
|
141
|
+
workers: {},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (!emitMetadata) return metadata;
|
|
145
|
+
|
|
146
|
+
for (const [controllerName, controller] of Object.entries(options.controllers) as [string, VovkController][]) {
|
|
147
|
+
metadata.controllers[controllerName] = {
|
|
148
|
+
_controllerName: controllerName,
|
|
149
|
+
_prefix: controller._prefix ?? '',
|
|
150
|
+
_handlers: {
|
|
151
|
+
...(exposeValidation
|
|
152
|
+
? controller._handlers
|
|
153
|
+
: Object.fromEntries(
|
|
154
|
+
Object.entries(controller._handlers).map(([key, value]) => [
|
|
155
|
+
key,
|
|
156
|
+
{ ...value, clientValidators: undefined },
|
|
157
|
+
])
|
|
158
|
+
)),
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (const [workerName, worker] of Object.entries(options.workers ?? {}) as [string, VovkWorker][]) {
|
|
164
|
+
metadata.workers[workerName] = {
|
|
165
|
+
_workerName: workerName,
|
|
166
|
+
_handlers: { ...worker._handlers },
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return metadata;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const initVovk = (options: {
|
|
174
|
+
segmentName?: string;
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
176
|
+
controllers: Record<string, Function>;
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
178
|
+
workers?: Record<string, Function>;
|
|
179
|
+
exposeValidation?: boolean;
|
|
180
|
+
emitMetadata?: boolean;
|
|
181
|
+
onError?: (err: Error, req: VovkRequest) => void | Promise<void>;
|
|
182
|
+
onMetadata?: (metadata: VovkMetadata) => void | Promise<void>;
|
|
183
|
+
}) => {
|
|
184
|
+
for (const [controllerName, controller] of Object.entries(options.controllers) as [string, VovkController][]) {
|
|
185
|
+
controller._controllerName = controllerName;
|
|
186
|
+
controller._activated = true;
|
|
187
|
+
controller._onError = options?.onError;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Wait for metadata to be set (it can be set after decorators are called with another setTimeout)
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
const metadata = getMetadata(options);
|
|
193
|
+
|
|
194
|
+
if (process.env.NODE_ENV === 'development') {
|
|
195
|
+
const VOVK_PORT = process.env.VOVK_PORT || (parseInt(process.env.PORT || '3000') + 6969).toString();
|
|
196
|
+
const url = `http://localhost:${VOVK_PORT}/__metadata`;
|
|
197
|
+
void fetch(url, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: { 'Content-Type': 'application/json' },
|
|
200
|
+
body: JSON.stringify({ metadata }),
|
|
201
|
+
})
|
|
202
|
+
.then((resp) => {
|
|
203
|
+
if (!resp.ok) {
|
|
204
|
+
console.error(`🐺 Failed to send metadata to ${url}. Response is not OK. ${resp.statusText}`);
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
.catch((err) => {
|
|
208
|
+
console.error(`🐺 Failed to send metadata to ${url}. ${err}`);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (options?.onMetadata) {
|
|
213
|
+
void options.onMetadata(metadata);
|
|
214
|
+
}
|
|
215
|
+
}, 10);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
GET: r.GET,
|
|
219
|
+
POST: r.POST,
|
|
220
|
+
PUT: r.PUT,
|
|
221
|
+
PATCH: r.PATCH,
|
|
222
|
+
DELETE: r.DELETE,
|
|
223
|
+
HEAD: r.HEAD,
|
|
224
|
+
OPTIONS: r.OPTIONS,
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
get: getDecoratorCreator(HttpMethod.GET),
|
|
230
|
+
post: getDecoratorCreator(HttpMethod.POST),
|
|
231
|
+
put: getDecoratorCreator(HttpMethod.PUT),
|
|
232
|
+
patch: getDecoratorCreator(HttpMethod.PATCH),
|
|
233
|
+
del: getDecoratorCreator(HttpMethod.DELETE),
|
|
234
|
+
head: getDecoratorCreator(HttpMethod.HEAD),
|
|
235
|
+
options: getDecoratorCreator(HttpMethod.OPTIONS),
|
|
236
|
+
prefix,
|
|
237
|
+
initVovk,
|
|
238
|
+
};
|
|
239
|
+
}
|
package/dist/.npmignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!*
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._HttpException = void 0;
|
|
4
|
+
class _HttpException extends Error {
|
|
5
|
+
statusCode;
|
|
6
|
+
message;
|
|
7
|
+
cause;
|
|
8
|
+
constructor(statusCode, message, cause) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.message = message;
|
|
12
|
+
this.cause = cause;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports._HttpException = _HttpException;
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source width="300" media="(prefers-color-scheme: dark)" srcset="https://vovk.dev/vovk-logo-white.svg">
|
|
4
|
+
<source width="300" media="(prefers-color-scheme: light)" srcset="https://vovk.dev/vovk-logo.svg">
|
|
5
|
+
<img width="300" alt="vovk" src="https://vovk.dev/vovk-logo.svg">
|
|
6
|
+
</picture><br>
|
|
7
|
+
<strong>RESTful RPC for Next.js</strong>
|
|
8
|
+
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
Transforms <a href="https://nextjs.org/docs/app">Next.js</a> into a powerful REST API platform with RPC capabilities.
|
|
13
|
+
<br><br>
|
|
14
|
+
ℹ️ Improved syntax for Zod and DTO validation is coming soon. Stay tuned!
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://vovk.dev/">Documentation</a>
|
|
19
|
+
<a href="https://discord.gg/qdT8WEHUuP">Discord</a>
|
|
20
|
+
<a href="https://github.com/finom/vovk-examples">Code Examples</a>
|
|
21
|
+
<a href="https://github.com/finom/vovk-zod">vovk-zod</a>
|
|
22
|
+
<a href="https://github.com/finom/vovk-hello-world">vovk-hello-world</a>
|
|
23
|
+
<a href="https://github.com/finom/vovk-react-native-example">vovk-react-native-example</a>
|
|
24
|
+
</p>
|
|
25
|
+
<p align="center">
|
|
26
|
+
<a href="https://www.npmjs.com/package/vovk"><img src="https://badge.fury.io/js/vovk.svg" alt="npm version" /></a>
|
|
27
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" alt="TypeScript" /></a>
|
|
28
|
+
<a href="https://github.com/finom/vovk/actions/workflows/main.yml"><img src="https://github.com/finom/vovk/actions/workflows/main.yml/badge.svg" alt="Build status" /></a>
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<br />
|
|
33
|
+
|
|
34
|
+
Example back-end Controller Class:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// /src/modules/post/PostController.ts
|
|
38
|
+
import { get, prefix, type VovkRequest } from 'vovk';
|
|
39
|
+
import PostService from './PostService';
|
|
40
|
+
|
|
41
|
+
@prefix('posts')
|
|
42
|
+
export default class PostController {
|
|
43
|
+
/**
|
|
44
|
+
* Create a comment on a post
|
|
45
|
+
* POST /api/posts/:postId/comments
|
|
46
|
+
*/
|
|
47
|
+
@post(':postId/comments')
|
|
48
|
+
static async createComment(
|
|
49
|
+
// decorate NextRequest type with body and query types
|
|
50
|
+
req: VovkRequest<
|
|
51
|
+
{ content: string; userId: string },
|
|
52
|
+
{ notificationType: 'push' | 'email' }
|
|
53
|
+
>,
|
|
54
|
+
{ postId }: { postId: string } // params
|
|
55
|
+
) {
|
|
56
|
+
// use standard Next.js API to get body and query
|
|
57
|
+
const { content, userId } = await req.json();
|
|
58
|
+
const notificationType = req.nextUrl.searchParams.get('notificationType');
|
|
59
|
+
|
|
60
|
+
// perform the request to the database in a custom service
|
|
61
|
+
return PostService.createComment({
|
|
62
|
+
postId, content, userId, notificationType,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Example component that uses the auto-generated client library:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
'use client';
|
|
72
|
+
import { useState } from 'react';
|
|
73
|
+
import { PostController } from 'vovk-client';
|
|
74
|
+
import type { VovkReturnType } from 'vovk';
|
|
75
|
+
|
|
76
|
+
export default function Example() {
|
|
77
|
+
const [response, setResponse] = useState<VovkReturnType<typeof PostController.createComment>>();
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
<button
|
|
82
|
+
onClick={async () => setResponse(
|
|
83
|
+
await PostController.createComment({
|
|
84
|
+
body: {
|
|
85
|
+
content: 'Hello, World!',
|
|
86
|
+
userId: '1',
|
|
87
|
+
},
|
|
88
|
+
params: { postId: '69' },
|
|
89
|
+
query: { notificationType: 'push' }
|
|
90
|
+
})
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
Post a comment
|
|
94
|
+
</button>
|
|
95
|
+
<div>{JSON.stringify(response)}</div>
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Alternatively, the resource can be fetched wit the regular `fetch` function:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
fetch('/api/posts/69?notificationType=push', {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
content: 'Hello, World!',
|
|
108
|
+
userId: '1',
|
|
109
|
+
}),
|
|
110
|
+
})
|
|
111
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { _HttpMethod as HttpMethod, type _RouteHandler as RouteHandler, type _VovkController as VovkController, type _VovkRequest as VovkRequest } from './types';
|
|
2
|
+
export declare class _Segment {
|
|
3
|
+
#private;
|
|
4
|
+
private static getHeadersFromOptions;
|
|
5
|
+
_routes: Record<HttpMethod, Map<VovkController, Record<string, RouteHandler>>>;
|
|
6
|
+
GET: (req: VovkRequest, data: {
|
|
7
|
+
params: Record<string, string[]>;
|
|
8
|
+
}) => Promise<Response>;
|
|
9
|
+
POST: (req: VovkRequest, data: {
|
|
10
|
+
params: Record<string, string[]>;
|
|
11
|
+
}) => Promise<Response>;
|
|
12
|
+
PUT: (req: VovkRequest, data: {
|
|
13
|
+
params: Record<string, string[]>;
|
|
14
|
+
}) => Promise<Response>;
|
|
15
|
+
PATCH: (req: VovkRequest, data: {
|
|
16
|
+
params: Record<string, string[]>;
|
|
17
|
+
}) => Promise<Response>;
|
|
18
|
+
DELETE: (req: VovkRequest, data: {
|
|
19
|
+
params: Record<string, string[]>;
|
|
20
|
+
}) => Promise<Response>;
|
|
21
|
+
HEAD: (req: VovkRequest, data: {
|
|
22
|
+
params: Record<string, string[]>;
|
|
23
|
+
}) => Promise<Response>;
|
|
24
|
+
OPTIONS: (req: VovkRequest, data: {
|
|
25
|
+
params: Record<string, string[]>;
|
|
26
|
+
}) => Promise<Response>;
|
|
27
|
+
}
|