vovk 3.0.0-draft.46 → 3.0.0-draft.460

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 (207) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -96
  3. package/bin/index.mjs +8 -0
  4. package/cjs/JSONLinesResponse.d.ts +19 -0
  5. package/cjs/JSONLinesResponse.js +95 -0
  6. package/{VovkApp.d.ts → cjs/VovkApp.d.ts} +14 -8
  7. package/cjs/VovkApp.js +304 -0
  8. package/cjs/client/createRPC.d.ts +5 -0
  9. package/cjs/client/createRPC.js +116 -0
  10. package/cjs/client/defaultHandler.d.ts +6 -0
  11. package/{client → cjs/client}/defaultHandler.js +9 -2
  12. package/cjs/client/defaultStreamHandler.d.ts +9 -0
  13. package/{client → cjs/client}/defaultStreamHandler.js +49 -26
  14. package/cjs/client/fetcher.d.ts +18 -0
  15. package/cjs/client/fetcher.js +97 -0
  16. package/cjs/client/index.d.ts +4 -0
  17. package/cjs/client/index.js +10 -0
  18. package/cjs/client/progressive.d.ts +9 -0
  19. package/cjs/client/progressive.js +54 -0
  20. package/cjs/client/types.d.ts +120 -0
  21. package/{createVovkApp.d.ts → cjs/createVovkApp.d.ts} +19 -16
  22. package/cjs/createVovkApp.js +146 -0
  23. package/cjs/index.d.ts +69 -0
  24. package/cjs/index.js +42 -0
  25. package/cjs/openapi/error.d.ts +2 -0
  26. package/cjs/openapi/error.js +100 -0
  27. package/cjs/openapi/index.d.ts +8 -0
  28. package/cjs/openapi/index.js +21 -0
  29. package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
  30. package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
  31. package/cjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
  32. package/cjs/openapi/openAPIToVovkSchema/index.js +141 -0
  33. package/cjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
  34. package/cjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
  35. package/cjs/openapi/vovkSchemaToOpenAPI.d.ts +8 -0
  36. package/cjs/openapi/vovkSchemaToOpenAPI.js +238 -0
  37. package/cjs/types.d.ts +466 -0
  38. package/{types.js → cjs/types.js} +14 -2
  39. package/cjs/utils/camelCase.d.ts +6 -0
  40. package/cjs/utils/camelCase.js +37 -0
  41. package/cjs/utils/createCodeSamples.d.ts +20 -0
  42. package/cjs/utils/createCodeSamples.js +279 -0
  43. package/cjs/utils/createDecorator.d.ts +6 -0
  44. package/{createDecorator.js → cjs/utils/createDecorator.js} +24 -14
  45. package/cjs/utils/createLLMTools.d.ts +50 -0
  46. package/cjs/utils/createLLMTools.js +168 -0
  47. package/cjs/utils/createStandardValidation.d.ts +82 -0
  48. package/cjs/utils/createStandardValidation.js +34 -0
  49. package/cjs/utils/createValidateOnClient.d.ts +7 -0
  50. package/cjs/utils/createValidateOnClient.js +19 -0
  51. package/cjs/utils/deepExtend.d.ts +54 -0
  52. package/cjs/utils/deepExtend.js +134 -0
  53. package/{utils → cjs/utils}/generateStaticAPI.d.ts +2 -2
  54. package/cjs/utils/generateStaticAPI.js +30 -0
  55. package/cjs/utils/getJSONSchemaExample.d.ts +11 -0
  56. package/cjs/utils/getJSONSchemaExample.js +264 -0
  57. package/cjs/utils/getJSONSchemaSample.d.ts +2 -0
  58. package/cjs/utils/getJSONSchemaSample.js +167 -0
  59. package/cjs/utils/getSampleFromObject.d.ts +9 -0
  60. package/cjs/utils/getSampleFromObject.js +41 -0
  61. package/cjs/utils/getSchema.d.ts +21 -0
  62. package/cjs/utils/getSchema.js +38 -0
  63. package/cjs/utils/multitenant.d.ts +24 -0
  64. package/cjs/utils/multitenant.js +131 -0
  65. package/cjs/utils/parseQuery.d.ts +25 -0
  66. package/cjs/utils/parseQuery.js +156 -0
  67. package/cjs/utils/reqForm.d.ts +2 -0
  68. package/cjs/utils/reqForm.js +33 -0
  69. package/cjs/utils/reqMeta.d.ts +2 -0
  70. package/cjs/utils/reqQuery.d.ts +2 -0
  71. package/cjs/utils/reqQuery.js +10 -0
  72. package/cjs/utils/resolveGeneratorConfigValues.d.ts +18 -0
  73. package/cjs/utils/resolveGeneratorConfigValues.js +82 -0
  74. package/cjs/utils/serializeQuery.d.ts +13 -0
  75. package/cjs/utils/serializeQuery.js +65 -0
  76. package/cjs/utils/setHandlerSchema.d.ts +4 -0
  77. package/cjs/utils/setHandlerSchema.js +15 -0
  78. package/cjs/utils/upperFirst.d.ts +1 -0
  79. package/cjs/utils/upperFirst.js +6 -0
  80. package/cjs/utils/withValidationLibrary.d.ts +77 -0
  81. package/cjs/utils/withValidationLibrary.js +124 -0
  82. package/mjs/HttpException.d.ts +7 -0
  83. package/mjs/HttpException.js +15 -0
  84. package/mjs/JSONLinesResponse.d.ts +19 -0
  85. package/mjs/JSONLinesResponse.js +95 -0
  86. package/mjs/VovkApp.d.ts +34 -0
  87. package/mjs/VovkApp.js +304 -0
  88. package/mjs/client/createRPC.d.ts +5 -0
  89. package/mjs/client/createRPC.js +116 -0
  90. package/mjs/client/defaultHandler.d.ts +6 -0
  91. package/mjs/client/defaultHandler.js +29 -0
  92. package/mjs/client/defaultStreamHandler.d.ts +9 -0
  93. package/mjs/client/defaultStreamHandler.js +105 -0
  94. package/mjs/client/fetcher.d.ts +18 -0
  95. package/mjs/client/fetcher.js +97 -0
  96. package/mjs/client/index.d.ts +4 -0
  97. package/mjs/client/index.js +10 -0
  98. package/mjs/client/progressive.d.ts +9 -0
  99. package/mjs/client/progressive.js +54 -0
  100. package/mjs/client/types.d.ts +120 -0
  101. package/mjs/createVovkApp.d.ts +65 -0
  102. package/mjs/createVovkApp.js +146 -0
  103. package/mjs/index.d.ts +69 -0
  104. package/mjs/index.js +42 -0
  105. package/mjs/openapi/error.d.ts +2 -0
  106. package/mjs/openapi/error.js +100 -0
  107. package/mjs/openapi/index.d.ts +8 -0
  108. package/mjs/openapi/index.js +21 -0
  109. package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
  110. package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
  111. package/mjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
  112. package/mjs/openapi/openAPIToVovkSchema/index.js +141 -0
  113. package/mjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
  114. package/mjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
  115. package/mjs/openapi/vovkSchemaToOpenAPI.d.ts +8 -0
  116. package/mjs/openapi/vovkSchemaToOpenAPI.js +238 -0
  117. package/mjs/types.d.ts +466 -0
  118. package/mjs/types.js +77 -0
  119. package/mjs/utils/camelCase.d.ts +6 -0
  120. package/mjs/utils/camelCase.js +37 -0
  121. package/mjs/utils/createCodeSamples.d.ts +20 -0
  122. package/mjs/utils/createCodeSamples.js +279 -0
  123. package/mjs/utils/createDecorator.d.ts +6 -0
  124. package/mjs/utils/createDecorator.js +48 -0
  125. package/mjs/utils/createLLMTools.d.ts +50 -0
  126. package/mjs/utils/createLLMTools.js +168 -0
  127. package/mjs/utils/createStandardValidation.d.ts +82 -0
  128. package/mjs/utils/createStandardValidation.js +34 -0
  129. package/mjs/utils/createValidateOnClient.d.ts +7 -0
  130. package/mjs/utils/createValidateOnClient.js +19 -0
  131. package/mjs/utils/deepExtend.d.ts +54 -0
  132. package/mjs/utils/deepExtend.js +134 -0
  133. package/mjs/utils/generateStaticAPI.d.ts +4 -0
  134. package/mjs/utils/generateStaticAPI.js +30 -0
  135. package/mjs/utils/getJSONSchemaExample.d.ts +11 -0
  136. package/mjs/utils/getJSONSchemaExample.js +264 -0
  137. package/mjs/utils/getJSONSchemaSample.d.ts +2 -0
  138. package/mjs/utils/getJSONSchemaSample.js +167 -0
  139. package/mjs/utils/getSampleFromObject.d.ts +9 -0
  140. package/mjs/utils/getSampleFromObject.js +41 -0
  141. package/mjs/utils/getSchema.d.ts +21 -0
  142. package/mjs/utils/getSchema.js +38 -0
  143. package/mjs/utils/multitenant.d.ts +24 -0
  144. package/mjs/utils/multitenant.js +131 -0
  145. package/mjs/utils/parseQuery.d.ts +25 -0
  146. package/mjs/utils/parseQuery.js +156 -0
  147. package/mjs/utils/reqForm.d.ts +2 -0
  148. package/mjs/utils/reqForm.js +33 -0
  149. package/mjs/utils/reqMeta.d.ts +2 -0
  150. package/mjs/utils/reqMeta.js +13 -0
  151. package/mjs/utils/reqQuery.d.ts +2 -0
  152. package/mjs/utils/reqQuery.js +10 -0
  153. package/mjs/utils/resolveGeneratorConfigValues.d.ts +18 -0
  154. package/mjs/utils/resolveGeneratorConfigValues.js +82 -0
  155. package/mjs/utils/serializeQuery.d.ts +13 -0
  156. package/mjs/utils/serializeQuery.js +65 -0
  157. package/mjs/utils/setHandlerSchema.d.ts +4 -0
  158. package/mjs/utils/setHandlerSchema.js +15 -0
  159. package/mjs/utils/shim.d.ts +1 -0
  160. package/mjs/utils/shim.js +18 -0
  161. package/mjs/utils/upperFirst.d.ts +1 -0
  162. package/mjs/utils/upperFirst.js +6 -0
  163. package/mjs/utils/withValidationLibrary.d.ts +77 -0
  164. package/mjs/utils/withValidationLibrary.js +124 -0
  165. package/package.json +28 -6
  166. package/.npmignore +0 -2
  167. package/StreamJSONResponse.d.ts +0 -17
  168. package/StreamJSONResponse.js +0 -54
  169. package/VovkApp.js +0 -185
  170. package/client/clientizeController.d.ts +0 -4
  171. package/client/clientizeController.js +0 -93
  172. package/client/defaultFetcher.d.ts +0 -4
  173. package/client/defaultFetcher.js +0 -49
  174. package/client/defaultHandler.d.ts +0 -2
  175. package/client/defaultStreamHandler.d.ts +0 -4
  176. package/client/index.d.ts +0 -4
  177. package/client/index.js +0 -5
  178. package/client/types.d.ts +0 -100
  179. package/createDecorator.d.ts +0 -4
  180. package/createVovkApp.js +0 -118
  181. package/index.d.ts +0 -60
  182. package/index.js +0 -20
  183. package/types.d.ts +0 -157
  184. package/utils/generateStaticAPI.js +0 -18
  185. package/utils/getSchema.d.ts +0 -8
  186. package/utils/getSchema.js +0 -38
  187. package/utils/reqForm.d.ts +0 -3
  188. package/utils/reqForm.js +0 -13
  189. package/utils/reqMeta.d.ts +0 -3
  190. package/utils/reqQuery.d.ts +0 -3
  191. package/utils/reqQuery.js +0 -25
  192. package/utils/setClientValidatorsForHandler.d.ts +0 -5
  193. package/utils/setClientValidatorsForHandler.js +0 -25
  194. package/worker/index.d.ts +0 -3
  195. package/worker/index.js +0 -7
  196. package/worker/promisifyWorker.d.ts +0 -2
  197. package/worker/promisifyWorker.js +0 -141
  198. package/worker/types.d.ts +0 -31
  199. package/worker/worker.d.ts +0 -1
  200. package/worker/worker.js +0 -43
  201. /package/{HttpException.d.ts → cjs/HttpException.d.ts} +0 -0
  202. /package/{HttpException.js → cjs/HttpException.js} +0 -0
  203. /package/{client → cjs/client}/types.js +0 -0
  204. /package/{utils → cjs/utils}/reqMeta.js +0 -0
  205. /package/{utils → cjs/utils}/shim.d.ts +0 -0
  206. /package/{utils → cjs/utils}/shim.js +0 -0
  207. /package/{worker → mjs/client}/types.js +0 -0
package/LICENSE CHANGED
@@ -16,7 +16,7 @@
16
16
  "nextjs",
17
17
  "router"
18
18
  ],
19
- "author": "Andrii Gubanov",
19
+ "author": "Andrey Gubanov",
20
20
  "license": "MIT",
21
21
  "bugs": {
22
22
  "url": "https://github.com/finom/vovk/issues"
package/README.md CHANGED
@@ -4,109 +4,21 @@
4
4
  <source width="300" media="(prefers-color-scheme: light)" srcset="https://vovk.dev/vovk-logo.svg">
5
5
  <img width="300" alt="vovk" src="https://vovk.dev/vovk-logo.svg">
6
6
  </picture><br>
7
- <strong>RESTful RPC for Next.js</strong>
8
-
7
+ <strong>RESTful + RPC = ♥️</strong>
9
8
  </p>
10
9
 
11
10
  <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!
11
+ Back-end meta-framework for <a href="https://nextjs.org/docs/app">Next.js</a>
15
12
  </p>
16
13
 
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>
14
+ ---
30
15
 
31
- <br />
16
+ ## vovk [![npm version](https://badge.fury.io/js/vovk.svg)](https://www.npmjs.com/package/vovk)
32
17
 
33
- Example back-end Controller Class:
18
+ The main library with [zero dependencies](https://bundlephobia.com/result?p=vovk) that's going to be used in production. It provides a wrapper for Next.js API routes, internal RPC API, utilities and types.
34
19
 
35
- ```ts
36
- // /src/modules/post/PostController.ts
37
- import { get, prefix, type VovkRequest } from 'vovk';
38
- import PostService from './PostService';
39
-
40
- @prefix('posts')
41
- export default class PostController {
42
- /**
43
- * Create a comment on a post
44
- * POST /api/posts/:postId/comments
45
- */
46
- @post(':postId/comments')
47
- static async createComment(
48
- // decorate NextRequest type with body and query types
49
- req: VovkRequest<{ content: string; userId: string }, { notificationType: 'push' | 'email' }>,
50
- { postId }: { postId: string } // params
51
- ) {
52
- // use standard Next.js API to get body and query
53
- const { content, userId } = await req.json();
54
- const notificationType = req.nextUrl.searchParams.get('notificationType');
55
-
56
- // perform the request to the database in a custom service
57
- return PostService.createComment({
58
- postId,
59
- content,
60
- userId,
61
- notificationType,
62
- });
63
- }
64
- }
20
+ ```sh
21
+ npm install vovk
65
22
  ```
66
23
 
67
- Example component that uses the auto-generated client library:
68
-
69
- ```tsx
70
- 'use client';
71
- import { useState } from 'react';
72
- import { PostController } from 'vovk-client';
73
- import type { VovkReturnType } from 'vovk';
74
-
75
- export default function Example() {
76
- const [response, setResponse] = useState<VovkReturnType<typeof PostController.createComment>>();
77
-
78
- return (
79
- <>
80
- <button
81
- onClick={async () =>
82
- 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
- >
94
- Post a comment
95
- </button>
96
- <div>{JSON.stringify(response)}</div>
97
- </>
98
- );
99
- }
100
- ```
101
-
102
- Alternatively, the resource can be fetched wit the regular `fetch` function:
103
-
104
- ```ts
105
- fetch('/api/posts/69?notificationType=push', {
106
- method: 'POST',
107
- body: JSON.stringify({
108
- content: 'Hello, World!',
109
- userId: '1',
110
- }),
111
- });
112
- ```
24
+ For more information, please visit the [getting started guide](https://vovk.dev/getting-started) or check out the [Vovk.ts examples](https://vovk-examples.vercel.app/).
package/bin/index.mjs ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+
4
+ console.warn(`Vovk CLI requires vovk-cli package. Running "npx vovk-cli ${process.argv.slice(2).join(' ')}" instead.`);
5
+
6
+ spawn('npx', ['vovk-cli', ...process.argv.slice(2)], { stdio: 'inherit' }).on('exit', (code) => {
7
+ process.exit(code);
8
+ });
@@ -0,0 +1,19 @@
1
+ import type { KnownAny, StreamAbortMessage } from './types';
2
+ import './utils/shim';
3
+ export declare class JSONLinesResponse<T> extends Response {
4
+ isClosed: boolean;
5
+ controller?: ReadableStreamDefaultController | null;
6
+ readonly encoder: TextEncoder | null;
7
+ readonly readableStream: ReadableStream | null;
8
+ private iteratorQueue;
9
+ private iteratorResolvers;
10
+ constructor(request?: Request, init?: ResponseInit);
11
+ send: (data: T | StreamAbortMessage) => void;
12
+ close: () => void;
13
+ throw: (e: KnownAny) => void;
14
+ [Symbol.dispose]: () => void;
15
+ [Symbol.asyncDispose]: () => void;
16
+ [Symbol.asyncIterator]: () => {
17
+ next: () => Promise<IteratorResult<T | StreamAbortMessage>>;
18
+ };
19
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JSONLinesResponse = void 0;
4
+ require("./utils/shim");
5
+ class JSONLinesResponse extends Response {
6
+ isClosed = false;
7
+ controller;
8
+ encoder;
9
+ readableStream;
10
+ iteratorQueue = [];
11
+ iteratorResolvers = [];
12
+ constructor(request, init) {
13
+ const encoder = new TextEncoder();
14
+ let readableController;
15
+ const readableStream = new ReadableStream({
16
+ cancel: () => {
17
+ this.isClosed = true;
18
+ },
19
+ start: (controller) => {
20
+ readableController = controller;
21
+ },
22
+ });
23
+ const accept = request?.headers?.get('accept');
24
+ super(readableStream, {
25
+ ...init,
26
+ headers: {
27
+ 'content-type': !request || accept?.includes('application/jsonl')
28
+ ? 'application/jsonl; charset=utf-8'
29
+ : 'text/plain; charset=utf-8',
30
+ ...init?.headers,
31
+ },
32
+ });
33
+ this.readableStream = request ? readableStream : null;
34
+ this.encoder = request ? encoder : null;
35
+ this.controller = request ? readableController : null;
36
+ request?.signal?.addEventListener('abort', this.close, { once: true });
37
+ // this will make promise on the client-side to resolve immediately
38
+ this.controller?.enqueue(encoder?.encode(''));
39
+ }
40
+ send = (data) => {
41
+ const { controller, encoder } = this;
42
+ if (this.isClosed)
43
+ return;
44
+ // Enqueue to the ReadableStream
45
+ controller?.enqueue(encoder?.encode(JSON.stringify(data) + '\n'));
46
+ // Handle async iterator consumers
47
+ if (this.iteratorResolvers.length > 0) {
48
+ // If there's a pending next() call, resolve it immediately
49
+ const resolve = this.iteratorResolvers.shift();
50
+ resolve({ value: data, done: false });
51
+ }
52
+ else {
53
+ // Otherwise, queue the value for later consumption
54
+ this.iteratorQueue.push(data);
55
+ }
56
+ };
57
+ close = () => {
58
+ const { controller } = this;
59
+ if (this.isClosed)
60
+ return;
61
+ this.isClosed = true;
62
+ controller?.close();
63
+ // Resolve all pending iterator next() calls with done: true
64
+ while (this.iteratorResolvers.length > 0) {
65
+ const resolve = this.iteratorResolvers.shift();
66
+ resolve({ done: true, value: undefined });
67
+ }
68
+ };
69
+ throw = (e) => {
70
+ this.send({ isError: true, reason: e instanceof Error ? e.message : e });
71
+ return this.close();
72
+ };
73
+ [Symbol.dispose] = () => this.close();
74
+ [Symbol.asyncDispose] = () => this.close();
75
+ [Symbol.asyncIterator] = () => {
76
+ return {
77
+ next: async () => {
78
+ // If we have queued values, return them immediately
79
+ if (this.iteratorQueue.length > 0) {
80
+ const value = this.iteratorQueue.shift();
81
+ return { value, done: false };
82
+ }
83
+ // If the stream is closed and no more values, we're done
84
+ if (this.isClosed) {
85
+ return { done: true, value: undefined };
86
+ }
87
+ // Otherwise, wait for the next value or close
88
+ return new Promise((resolve) => {
89
+ this.iteratorResolvers.push(resolve);
90
+ });
91
+ },
92
+ };
93
+ };
94
+ }
95
+ exports.JSONLinesResponse = JSONLinesResponse;
@@ -1,28 +1,34 @@
1
+ import type { NextRequest } from 'next/server';
1
2
  import { HttpMethod, HttpStatus, type RouteHandler, type VovkController, type DecoratorOptions, type VovkRequest } 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
- respond: (status: HttpStatus, body: unknown, options?: DecoratorOptions) => Response;
28
+ respond: ({ statusCode, responseBody, options, }: {
29
+ req: VovkRequest;
30
+ statusCode: HttpStatus;
31
+ responseBody: unknown;
32
+ options?: DecoratorOptions;
33
+ }) => Promise<Response>;
28
34
  }
package/cjs/VovkApp.js ADDED
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ var _a;
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.VovkApp = void 0;
8
+ const types_1 = require("./types");
9
+ const HttpException_1 = require("./HttpException");
10
+ const JSONLinesResponse_1 = require("./JSONLinesResponse");
11
+ const reqQuery_1 = __importDefault(require("./utils/reqQuery"));
12
+ const reqMeta_1 = __importDefault(require("./utils/reqMeta"));
13
+ const reqForm_1 = __importDefault(require("./utils/reqForm"));
14
+ class VovkApp {
15
+ static getHeadersFromOptions(options) {
16
+ if (!options)
17
+ return {};
18
+ const corsHeaders = {
19
+ 'access-control-allow-origin': '*',
20
+ 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS, HEAD',
21
+ 'access-control-allow-headers': 'content-type, authorization',
22
+ };
23
+ const headers = {
24
+ ...(options.cors ? corsHeaders : {}),
25
+ ...(options.headers ?? {}),
26
+ };
27
+ return headers;
28
+ }
29
+ routes = {
30
+ GET: new Map(),
31
+ POST: new Map(),
32
+ PUT: new Map(),
33
+ PATCH: new Map(),
34
+ DELETE: new Map(),
35
+ HEAD: new Map(),
36
+ OPTIONS: new Map(),
37
+ };
38
+ GET = async (req, data) => this.#callMethod({ httpMethod: types_1.HttpMethod.GET, req, params: await data.params });
39
+ POST = async (req, data) => this.#callMethod({ httpMethod: types_1.HttpMethod.POST, req, params: await data.params });
40
+ PUT = async (req, data) => this.#callMethod({ httpMethod: types_1.HttpMethod.PUT, req, params: await data.params });
41
+ PATCH = async (req, data) => this.#callMethod({ httpMethod: types_1.HttpMethod.PATCH, req, params: await data.params });
42
+ DELETE = async (req, data) => this.#callMethod({ httpMethod: types_1.HttpMethod.DELETE, req, params: await data.params });
43
+ HEAD = async (req, data) => this.#callMethod({ httpMethod: types_1.HttpMethod.HEAD, req, params: await data.params });
44
+ OPTIONS = async (req, data) => this.#callMethod({ httpMethod: types_1.HttpMethod.OPTIONS, req, params: await data.params });
45
+ respond = async ({ statusCode, responseBody, options, }) => {
46
+ const response = new Response(JSON.stringify(responseBody), {
47
+ status: statusCode,
48
+ headers: {
49
+ 'content-type': 'application/json',
50
+ ..._a.getHeadersFromOptions(options),
51
+ },
52
+ });
53
+ return response;
54
+ };
55
+ #respondWithError = ({ req, statusCode, message, options, cause, }) => {
56
+ return this.respond({
57
+ req,
58
+ statusCode,
59
+ responseBody: {
60
+ cause,
61
+ statusCode,
62
+ message,
63
+ isError: true,
64
+ },
65
+ options,
66
+ });
67
+ };
68
+ #routeRegexCache = new Map();
69
+ #routeSegmentsCache = new Map();
70
+ #routeParamPositionsCache = new Map();
71
+ #routeMatchCache = new Map();
72
+ #getHandler = ({ handlers, path, params, }) => {
73
+ let methodParams = {};
74
+ if (Object.keys(params).length === 0) {
75
+ return { handler: handlers[''], methodParams };
76
+ }
77
+ const pathStr = path.join('/');
78
+ // Fast path: Check if this exact path has been matched before
79
+ const cachedMatch = this.#routeMatchCache.get(pathStr);
80
+ if (cachedMatch) {
81
+ return {
82
+ handler: handlers[cachedMatch.route],
83
+ methodParams: cachedMatch.params,
84
+ };
85
+ }
86
+ // Check for direct static route match
87
+ let methodKey = handlers[pathStr] ? pathStr : null;
88
+ if (!methodKey) {
89
+ const methodKeys = [];
90
+ const pathLength = path.length;
91
+ // First pass: group routes by length for quick filtering
92
+ const routesByLength = new Map();
93
+ for (const p of Object.keys(handlers)) {
94
+ let routeSegments = this.#routeSegmentsCache.get(p);
95
+ if (!routeSegments) {
96
+ routeSegments = p.split('/');
97
+ this.#routeSegmentsCache.set(p, routeSegments);
98
+ // Pre-compute parameter positions for routes with parameters
99
+ if (p.includes('{')) {
100
+ const paramPositions = [];
101
+ for (let i = 0; i < routeSegments.length; i++) {
102
+ const segment = routeSegments[i];
103
+ if (segment.includes('{')) {
104
+ const paramMatch = segment.match(/\{(\w+)\}/);
105
+ if (paramMatch) {
106
+ paramPositions.push({ index: i, paramName: paramMatch[1] });
107
+ }
108
+ }
109
+ }
110
+ this.#routeParamPositionsCache.set(p, paramPositions);
111
+ }
112
+ }
113
+ const segmentLength = routeSegments.length;
114
+ if (segmentLength !== pathLength)
115
+ continue;
116
+ const lengthRoutes = routesByLength.get(segmentLength) || [];
117
+ lengthRoutes.push(p);
118
+ routesByLength.set(segmentLength, lengthRoutes);
119
+ }
120
+ // Only process routes with matching segment count
121
+ const candidateRoutes = routesByLength.get(pathLength) || [];
122
+ for (const p of candidateRoutes) {
123
+ const routeSegments = this.#routeSegmentsCache.get(p);
124
+ const params = {};
125
+ // Fast path for routes with parameters
126
+ const paramPositions = this.#routeParamPositionsCache.get(p);
127
+ if (paramPositions) {
128
+ let isMatch = true;
129
+ // First check all non-parameter segments for a quick fail
130
+ for (let i = 0; i < routeSegments.length; i++) {
131
+ const routeSegment = routeSegments[i];
132
+ if (!routeSegment.includes('{') && routeSegment !== path[i]) {
133
+ isMatch = false;
134
+ break;
135
+ }
136
+ }
137
+ if (!isMatch)
138
+ continue;
139
+ // Now process parameter segments
140
+ for (const { index, paramName } of paramPositions) {
141
+ const routeSegment = routeSegments[index];
142
+ const pathSegment = path[index];
143
+ let regex = this.#routeRegexCache.get(routeSegment);
144
+ if (!regex) {
145
+ const regexPattern = routeSegment
146
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
147
+ .replace(/\\{(\w+)\\}/g, '(?<$1>[^/]+)');
148
+ regex = new RegExp(`^${regexPattern}$`);
149
+ this.#routeRegexCache.set(routeSegment, regex);
150
+ }
151
+ const values = pathSegment.match(regex)?.groups;
152
+ if (!values) {
153
+ isMatch = false;
154
+ break;
155
+ }
156
+ if (paramName in params) {
157
+ throw new HttpException_1.HttpException(types_1.HttpStatus.INTERNAL_SERVER_ERROR, `Duplicate parameter "${paramName}" at ${p}`);
158
+ }
159
+ params[paramName] = values[paramName];
160
+ }
161
+ if (isMatch) {
162
+ methodParams = params;
163
+ methodKeys.push(p);
164
+ }
165
+ }
166
+ else {
167
+ // Static route - simple equality comparison for all segments
168
+ let isMatch = true;
169
+ for (let i = 0; i < routeSegments.length; i++) {
170
+ if (routeSegments[i] !== path[i]) {
171
+ isMatch = false;
172
+ break;
173
+ }
174
+ }
175
+ if (isMatch) {
176
+ methodKeys.push(p);
177
+ }
178
+ }
179
+ }
180
+ if (methodKeys.length > 1) {
181
+ throw new HttpException_1.HttpException(types_1.HttpStatus.INTERNAL_SERVER_ERROR, `Conflicting routes found: ${methodKeys.join(', ')}`);
182
+ }
183
+ [methodKey] = methodKeys;
184
+ // Cache successful matches
185
+ if (methodKey) {
186
+ this.#routeMatchCache.set(pathStr, { route: methodKey, params: methodParams });
187
+ }
188
+ }
189
+ if (methodKey) {
190
+ return { handler: handlers[methodKey], methodParams };
191
+ }
192
+ return { handler: null, methodParams };
193
+ };
194
+ #allHandlers = {};
195
+ #collectHandlers = (httpMethod) => {
196
+ const controllers = this.routes[httpMethod];
197
+ const handlers = {};
198
+ controllers.forEach((staticMethods, controller) => {
199
+ const prefix = controller._prefix ?? '';
200
+ Object.entries(staticMethods ?? {}).forEach(([path, staticMethod]) => {
201
+ const fullPath = [prefix, path].filter(Boolean).join('/');
202
+ handlers[fullPath] = { staticMethod, controller };
203
+ });
204
+ });
205
+ return handlers;
206
+ };
207
+ #callMethod = async ({ httpMethod, req: nextReq, params, }) => {
208
+ const req = nextReq;
209
+ const path = params[Object.keys(params)[0]];
210
+ const handlers = this.#allHandlers[httpMethod] ?? this.#collectHandlers(httpMethod);
211
+ this.#allHandlers[httpMethod] = handlers;
212
+ let headerList;
213
+ try {
214
+ headerList = nextReq.headers;
215
+ }
216
+ catch {
217
+ // this is static rendering environment, headers are not available
218
+ headerList = null;
219
+ }
220
+ const xMeta = headerList?.get('x-meta');
221
+ const xMetaHeader = xMeta && JSON.parse(xMeta);
222
+ if (xMetaHeader)
223
+ (0, reqMeta_1.default)(req, { xMetaHeader });
224
+ const { handler, methodParams } = this.#getHandler({ handlers, path, params });
225
+ if (!handler) {
226
+ return this.#respondWithError({
227
+ req,
228
+ statusCode: types_1.HttpStatus.NOT_FOUND,
229
+ message: `${Object.keys(handlers)} - Route ${path.join('/')} is not found`,
230
+ });
231
+ }
232
+ const { staticMethod, controller } = handler;
233
+ const { _onSuccess: onSuccess, _onBefore: onBefore } = controller;
234
+ req.vovk = {
235
+ body: () => req.json(),
236
+ query: () => (0, reqQuery_1.default)(req),
237
+ meta: (meta) => (0, reqMeta_1.default)(req, meta),
238
+ form: () => (0, reqForm_1.default)(req),
239
+ params: () => methodParams,
240
+ };
241
+ try {
242
+ await staticMethod._options?.before?.call(controller, req);
243
+ await onBefore?.(req);
244
+ const result = await staticMethod.call(controller, req, methodParams);
245
+ if (result instanceof Response) {
246
+ await onSuccess?.(result, req);
247
+ return result;
248
+ }
249
+ const isIterator = typeof result === 'object' &&
250
+ !!result &&
251
+ !(result instanceof Array) &&
252
+ ((Reflect.has(result, Symbol.iterator) &&
253
+ typeof result[Symbol.iterator] === 'function') ||
254
+ (Reflect.has(result, Symbol.asyncIterator) &&
255
+ typeof result[Symbol.asyncIterator] === 'function'));
256
+ if (isIterator) {
257
+ const streamResponse = new JSONLinesResponse_1.JSONLinesResponse(req, {
258
+ headers: {
259
+ ..._a.getHeadersFromOptions(staticMethod._options),
260
+ },
261
+ });
262
+ void (async () => {
263
+ try {
264
+ for await (const chunk of result) {
265
+ streamResponse.send(chunk);
266
+ }
267
+ }
268
+ catch (e) {
269
+ return streamResponse.throw(e);
270
+ }
271
+ return streamResponse.close();
272
+ })();
273
+ await onSuccess?.(streamResponse, req);
274
+ return streamResponse;
275
+ }
276
+ const responseBody = result ?? null;
277
+ await onSuccess?.(responseBody, req);
278
+ return this.respond({ req, statusCode: 200, responseBody, options: staticMethod._options });
279
+ }
280
+ catch (e) {
281
+ const err = e;
282
+ try {
283
+ await controller._onError?.(err, req);
284
+ }
285
+ catch (onErrorError) {
286
+ // eslint-disable-next-line no-console
287
+ console.error(onErrorError);
288
+ }
289
+ if (err.message !== 'NEXT_REDIRECT' && err.message !== 'NEXT_NOT_FOUND') {
290
+ const statusCode = err.statusCode || types_1.HttpStatus.INTERNAL_SERVER_ERROR;
291
+ return this.#respondWithError({
292
+ req,
293
+ statusCode,
294
+ message: err.message,
295
+ options: staticMethod._options,
296
+ cause: err.cause,
297
+ });
298
+ }
299
+ throw e; // if NEXT_REDIRECT or NEXT_NOT_FOUND, rethrow it
300
+ }
301
+ };
302
+ }
303
+ exports.VovkApp = VovkApp;
304
+ _a = VovkApp;
@@ -0,0 +1,5 @@
1
+ import type { KnownAny } from '../types';
2
+ import type { VovkRPCModule, VovkFetcher, VovkFetcherOptions } from './types';
3
+ export declare const createRPC: <T, OPTS extends Record<string, KnownAny> = Record<string, never>>(givenSchema: unknown, segmentName: string, rpcModuleName: string, givenFetcher?: VovkFetcher<OPTS> | Promise<{
4
+ fetcher: VovkFetcher<OPTS>;
5
+ }>, options?: VovkFetcherOptions<OPTS>) => VovkRPCModule<T, OPTS>;