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.
Files changed (77) hide show
  1. package/.DS_Store +0 -0
  2. package/.npmignore +1 -0
  3. package/.turbo/turbo-build.log +6 -0
  4. package/.turbo/turbo-ncu.log +9 -0
  5. package/HttpException.ts +16 -0
  6. package/Segment.ts +238 -0
  7. package/StreamResponse.ts +61 -0
  8. package/client/clientizeController.ts +141 -0
  9. package/client/defaultFetcher.ts +60 -0
  10. package/client/defaultHandler.ts +22 -0
  11. package/client/defaultStreamHandler.ts +88 -0
  12. package/client/index.ts +9 -0
  13. package/client/types.ts +116 -0
  14. package/createDecorator.ts +65 -0
  15. package/createSegment.ts +239 -0
  16. package/dist/.npmignore +1 -0
  17. package/dist/HttpException.d.ts +7 -0
  18. package/dist/HttpException.js +15 -0
  19. package/dist/README.md +111 -0
  20. package/dist/Segment.d.ts +27 -0
  21. package/dist/Segment.js +187 -0
  22. package/dist/StreamResponse.d.ts +16 -0
  23. package/dist/StreamResponse.js +53 -0
  24. package/dist/client/clientizeController.d.ts +4 -0
  25. package/dist/client/clientizeController.js +92 -0
  26. package/dist/client/defaultFetcher.d.ts +4 -0
  27. package/dist/client/defaultFetcher.js +49 -0
  28. package/dist/client/defaultHandler.d.ts +2 -0
  29. package/dist/client/defaultHandler.js +21 -0
  30. package/dist/client/defaultStreamHandler.d.ts +4 -0
  31. package/dist/client/defaultStreamHandler.js +82 -0
  32. package/dist/client/index.d.ts +4 -0
  33. package/dist/client/index.js +5 -0
  34. package/dist/client/types.d.ts +101 -0
  35. package/dist/client/types.js +2 -0
  36. package/dist/createDecorator.d.ts +4 -0
  37. package/dist/createDecorator.js +38 -0
  38. package/dist/createSegment.d.ts +63 -0
  39. package/dist/createSegment.js +167 -0
  40. package/dist/generateStaticAPI.d.ts +3 -0
  41. package/dist/generateStaticAPI.js +19 -0
  42. package/dist/index.d.ts +61 -0
  43. package/dist/index.js +20 -0
  44. package/dist/package-lock.json +472 -0
  45. package/dist/package.json +28 -0
  46. package/dist/tsconfig.tsbuildinfo +1 -0
  47. package/dist/types.d.ts +169 -0
  48. package/dist/types.js +65 -0
  49. package/dist/utils/reqMeta.d.ts +3 -0
  50. package/dist/utils/reqMeta.js +13 -0
  51. package/dist/utils/reqQuery.d.ts +3 -0
  52. package/dist/utils/reqQuery.js +25 -0
  53. package/dist/utils/setClientValidatorsForHandler.d.ts +5 -0
  54. package/dist/utils/setClientValidatorsForHandler.js +28 -0
  55. package/dist/utils/shim.d.ts +0 -0
  56. package/dist/utils/shim.js +17 -0
  57. package/dist/worker/index.d.ts +3 -0
  58. package/dist/worker/index.js +7 -0
  59. package/dist/worker/promisifyWorker.d.ts +2 -0
  60. package/dist/worker/promisifyWorker.js +142 -0
  61. package/dist/worker/types.d.ts +31 -0
  62. package/dist/worker/types.js +2 -0
  63. package/dist/worker/worker.d.ts +1 -0
  64. package/dist/worker/worker.js +45 -0
  65. package/generateStaticAPI.ts +19 -0
  66. package/index.ts +59 -0
  67. package/package.json +1 -1
  68. package/tsconfig.json +9 -0
  69. package/types.ts +237 -0
  70. package/utils/reqMeta.ts +17 -0
  71. package/utils/reqQuery.ts +27 -0
  72. package/utils/setClientValidatorsForHandler.ts +45 -0
  73. package/utils/shim.ts +17 -0
  74. package/worker/index.ts +4 -0
  75. package/worker/promisifyWorker.ts +159 -0
  76. package/worker/types.ts +45 -0
  77. package/worker/worker.ts +55 -0
@@ -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
+ }
@@ -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
+ }
@@ -0,0 +1 @@
1
+ !*
@@ -0,0 +1,7 @@
1
+ import type { _HttpStatus as HttpStatus } from './types';
2
+ export declare class _HttpException extends Error {
3
+ statusCode: HttpStatus;
4
+ message: string;
5
+ cause?: unknown;
6
+ constructor(statusCode: HttpStatus, message: string, cause?: unknown);
7
+ }
@@ -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>&nbsp;&nbsp;&nbsp;&nbsp;
19
+ <a href="https://discord.gg/qdT8WEHUuP">Discord</a>&nbsp;&nbsp;&nbsp;&nbsp;
20
+ <a href="https://github.com/finom/vovk-examples">Code Examples</a>&nbsp;&nbsp;&nbsp;&nbsp;
21
+ <a href="https://github.com/finom/vovk-zod">vovk-zod</a>&nbsp;&nbsp;&nbsp;&nbsp;
22
+ <a href="https://github.com/finom/vovk-hello-world">vovk-hello-world</a>&nbsp;&nbsp;&nbsp;&nbsp;
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>&nbsp;
27
+ <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" alt="TypeScript" /></a>&nbsp;
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
+ }