vovk 3.0.0-draft.13 → 3.0.0-draft.135

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 (127) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -96
  3. package/{HttpException.d.ts → cjs/HttpException.d.ts} +2 -2
  4. package/{HttpException.js → cjs/HttpException.js} +3 -3
  5. package/cjs/JSONLinesResponse.d.ts +14 -0
  6. package/{StreamResponse.js → cjs/JSONLinesResponse.js} +15 -9
  7. package/{Segment.d.ts → cjs/VovkApp.d.ts} +11 -10
  8. package/cjs/VovkApp.js +189 -0
  9. package/cjs/client/createRPC.d.ts +3 -0
  10. package/cjs/client/createRPC.js +86 -0
  11. package/{client → cjs/client}/defaultHandler.d.ts +1 -1
  12. package/cjs/client/defaultHandler.js +22 -0
  13. package/cjs/client/defaultStreamHandler.d.ts +4 -0
  14. package/{client → cjs/client}/defaultStreamHandler.js +8 -8
  15. package/cjs/client/fetcher.d.ts +8 -0
  16. package/cjs/client/fetcher.js +88 -0
  17. package/cjs/client/index.d.ts +3 -0
  18. package/cjs/client/index.js +8 -0
  19. package/cjs/client/types.d.ts +113 -0
  20. package/cjs/createDecorator.d.ts +6 -0
  21. package/{createDecorator.js → cjs/createDecorator.js} +21 -16
  22. package/{createSegment.d.ts → cjs/createVovkApp.d.ts} +10 -10
  23. package/cjs/createVovkApp.js +129 -0
  24. package/cjs/index.d.ts +60 -0
  25. package/cjs/index.js +25 -0
  26. package/cjs/types.d.ts +277 -0
  27. package/cjs/types.js +72 -0
  28. package/cjs/utils/generateStaticAPI.d.ts +4 -0
  29. package/cjs/utils/generateStaticAPI.js +30 -0
  30. package/cjs/utils/getSchema.d.ts +20 -0
  31. package/cjs/utils/getSchema.js +35 -0
  32. package/cjs/utils/parseQuery.d.ts +25 -0
  33. package/cjs/utils/parseQuery.js +156 -0
  34. package/cjs/utils/reqForm.d.ts +2 -0
  35. package/cjs/utils/reqForm.js +32 -0
  36. package/{utils → cjs/utils}/reqMeta.d.ts +1 -2
  37. package/{utils → cjs/utils}/reqQuery.d.ts +1 -2
  38. package/cjs/utils/reqQuery.js +10 -0
  39. package/cjs/utils/serializeQuery.d.ts +13 -0
  40. package/cjs/utils/serializeQuery.js +65 -0
  41. package/cjs/utils/setHandlerSchema.d.ts +4 -0
  42. package/cjs/utils/setHandlerSchema.js +15 -0
  43. package/cjs/utils/withValidation.d.ts +21 -0
  44. package/cjs/utils/withValidation.js +88 -0
  45. package/mjs/HttpException.d.ts +7 -0
  46. package/mjs/HttpException.js +15 -0
  47. package/mjs/JSONLinesResponse.d.ts +14 -0
  48. package/mjs/JSONLinesResponse.js +59 -0
  49. package/mjs/VovkApp.d.ts +29 -0
  50. package/mjs/VovkApp.js +189 -0
  51. package/mjs/client/createRPC.d.ts +3 -0
  52. package/mjs/client/createRPC.js +86 -0
  53. package/mjs/client/defaultHandler.d.ts +2 -0
  54. package/mjs/client/defaultHandler.js +22 -0
  55. package/mjs/client/defaultStreamHandler.d.ts +4 -0
  56. package/mjs/client/defaultStreamHandler.js +82 -0
  57. package/mjs/client/fetcher.d.ts +8 -0
  58. package/mjs/client/fetcher.js +88 -0
  59. package/mjs/client/index.d.ts +3 -0
  60. package/mjs/client/index.js +8 -0
  61. package/mjs/client/types.d.ts +113 -0
  62. package/mjs/createDecorator.d.ts +6 -0
  63. package/mjs/createDecorator.js +43 -0
  64. package/mjs/createVovkApp.d.ts +62 -0
  65. package/mjs/createVovkApp.js +129 -0
  66. package/mjs/index.d.ts +60 -0
  67. package/mjs/index.js +25 -0
  68. package/mjs/types.d.ts +277 -0
  69. package/mjs/types.js +72 -0
  70. package/mjs/utils/generateStaticAPI.d.ts +4 -0
  71. package/mjs/utils/generateStaticAPI.js +30 -0
  72. package/mjs/utils/getSchema.d.ts +20 -0
  73. package/mjs/utils/getSchema.js +35 -0
  74. package/mjs/utils/parseQuery.d.ts +25 -0
  75. package/mjs/utils/parseQuery.js +156 -0
  76. package/mjs/utils/reqForm.d.ts +2 -0
  77. package/mjs/utils/reqForm.js +32 -0
  78. package/mjs/utils/reqMeta.d.ts +2 -0
  79. package/mjs/utils/reqMeta.js +13 -0
  80. package/mjs/utils/reqQuery.d.ts +2 -0
  81. package/mjs/utils/reqQuery.js +10 -0
  82. package/mjs/utils/serializeQuery.d.ts +13 -0
  83. package/mjs/utils/serializeQuery.js +65 -0
  84. package/mjs/utils/setHandlerSchema.d.ts +4 -0
  85. package/mjs/utils/setHandlerSchema.js +15 -0
  86. package/mjs/utils/shim.d.ts +1 -0
  87. package/mjs/utils/shim.js +18 -0
  88. package/mjs/utils/withValidation.d.ts +21 -0
  89. package/mjs/utils/withValidation.js +88 -0
  90. package/package.json +15 -5
  91. package/.npmignore +0 -2
  92. package/Segment.js +0 -182
  93. package/StreamResponse.d.ts +0 -16
  94. package/client/clientizeController.d.ts +0 -4
  95. package/client/clientizeController.js +0 -93
  96. package/client/defaultFetcher.d.ts +0 -4
  97. package/client/defaultFetcher.js +0 -49
  98. package/client/defaultHandler.js +0 -21
  99. package/client/defaultStreamHandler.d.ts +0 -4
  100. package/client/index.d.ts +0 -4
  101. package/client/index.js +0 -5
  102. package/client/types.d.ts +0 -100
  103. package/createDecorator.d.ts +0 -4
  104. package/createSegment.js +0 -118
  105. package/generateStaticAPI.d.ts +0 -4
  106. package/generateStaticAPI.js +0 -18
  107. package/index.d.ts +0 -60
  108. package/index.js +0 -20
  109. package/types.d.ts +0 -186
  110. package/types.js +0 -65
  111. package/utils/getSchema.d.ts +0 -8
  112. package/utils/getSchema.js +0 -38
  113. package/utils/reqQuery.js +0 -25
  114. package/utils/setClientValidatorsForHandler.d.ts +0 -5
  115. package/utils/setClientValidatorsForHandler.js +0 -28
  116. package/worker/index.d.ts +0 -3
  117. package/worker/index.js +0 -7
  118. package/worker/promisifyWorker.d.ts +0 -2
  119. package/worker/promisifyWorker.js +0 -143
  120. package/worker/types.d.ts +0 -31
  121. package/worker/worker.d.ts +0 -1
  122. package/worker/worker.js +0 -44
  123. /package/{client → cjs/client}/types.js +0 -0
  124. /package/{utils → cjs/utils}/reqMeta.js +0 -0
  125. /package/{utils → cjs/utils}/shim.d.ts +0 -0
  126. /package/{utils → cjs/utils}/shim.js +0 -0
  127. /package/{worker → mjs/client}/types.js +0 -0
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = reqQuery;
7
+ const parseQuery_js_1 = __importDefault(require("./parseQuery.js"));
8
+ function reqQuery(req) {
9
+ return (0, parseQuery_js_1.default)(req.nextUrl.search);
10
+ }
@@ -0,0 +1,13 @@
1
+ import type { KnownAny } from '../types.js';
2
+ /**
3
+ * Serialize a nested object (including arrays, arrays of objects, etc.)
4
+ * into a bracket-based query string.
5
+ *
6
+ * @example
7
+ * serializeQuery({ x: 'xx', y: [1, 2], z: { f: 'x' } })
8
+ * => "x=xx&y[0]=1&y[1]=2&z[f]=x"
9
+ *
10
+ * @param obj - The input object to be serialized
11
+ * @returns - A bracket-based query string (without leading "?")
12
+ */
13
+ export default function serializeQuery(obj: Record<string, KnownAny>): string;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = serializeQuery;
4
+ /**
5
+ * Recursively build query parameters from an object.
6
+ *
7
+ * @param key - The query key so far (e.g. 'user', 'user[0]', 'user[0][name]')
8
+ * @param value - The current value to serialize
9
+ * @returns - An array of `key=value` strings
10
+ */
11
+ function buildParams(key, value) {
12
+ if (value === null || value === undefined) {
13
+ return []; // skip null/undefined values entirely
14
+ }
15
+ // If value is an object or array, we need to recurse
16
+ if (typeof value === 'object') {
17
+ // Array case
18
+ if (Array.isArray(value)) {
19
+ /**
20
+ * We use index-based bracket notation here:
21
+ * e.g. for value = ['aa', 'bb'] and key = 'foo'
22
+ * => "foo[0]=aa&foo[1]=bb"
23
+ *
24
+ * If you prefer "foo[]=aa&foo[]=bb" style, replace:
25
+ * `${key}[${i}]`
26
+ * with:
27
+ * `${key}[]`
28
+ */
29
+ return value.flatMap((v, i) => {
30
+ const newKey = `${key}[${i}]`;
31
+ return buildParams(newKey, v);
32
+ });
33
+ }
34
+ // Plain object case
35
+ return Object.keys(value).flatMap((k) => {
36
+ const newKey = `${key}[${k}]`;
37
+ return buildParams(newKey, value[k]);
38
+ });
39
+ }
40
+ return [`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`];
41
+ }
42
+ /**
43
+ * Serialize a nested object (including arrays, arrays of objects, etc.)
44
+ * into a bracket-based query string.
45
+ *
46
+ * @example
47
+ * serializeQuery({ x: 'xx', y: [1, 2], z: { f: 'x' } })
48
+ * => "x=xx&y[0]=1&y[1]=2&z[f]=x"
49
+ *
50
+ * @param obj - The input object to be serialized
51
+ * @returns - A bracket-based query string (without leading "?")
52
+ */
53
+ function serializeQuery(obj) {
54
+ if (!obj || typeof obj !== 'object')
55
+ return '';
56
+ // Collect query segments
57
+ const segments = [];
58
+ for (const key in obj) {
59
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
60
+ const value = obj[key];
61
+ segments.push(...buildParams(key, value));
62
+ }
63
+ }
64
+ return segments.join('&');
65
+ }
@@ -0,0 +1,4 @@
1
+ import type { KnownAny, VovkController, VovkHandlerSchema } from '../types.js';
2
+ export declare function setHandlerSchema(h: ((...args: KnownAny[]) => KnownAny) & {
3
+ _getSchema?: (controller: VovkController) => Omit<VovkHandlerSchema, 'httpMethod' | 'path'>;
4
+ }, schema: Omit<VovkHandlerSchema, 'httpMethod' | 'path'>): Promise<void>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setHandlerSchema = setHandlerSchema;
4
+ async function setHandlerSchema(h, schema) {
5
+ h._getSchema = (controller) => {
6
+ if (!controller) {
7
+ throw new Error('Error setting client validators. Controller not found. Did you forget to use an HTTP decorator?');
8
+ }
9
+ const handlerName = Object.getOwnPropertyNames(controller).find((key) => controller[key]._sourceMethod === h);
10
+ if (!handlerName) {
11
+ throw new Error('Error setting client validators. Handler not found.');
12
+ }
13
+ return schema;
14
+ };
15
+ }
@@ -0,0 +1,21 @@
1
+ import { VovkValidationType, type KnownAny, type VovkRequest } from '../types.js';
2
+ export declare function withValidation<T extends (req: KnownAny, params: KnownAny) => KnownAny, BODY_MODEL, QUERY_MODEL, PARAMS_MODEL, OUTPUT_MODEL, ITERATION_MODEL>({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }: {
3
+ disableServerSideValidation?: boolean | VovkValidationType[];
4
+ skipSchemaEmission?: boolean | VovkValidationType[];
5
+ validateEachIteration?: boolean;
6
+ body?: BODY_MODEL;
7
+ query?: QUERY_MODEL;
8
+ params?: PARAMS_MODEL;
9
+ output?: OUTPUT_MODEL;
10
+ iteration?: ITERATION_MODEL;
11
+ handle: T;
12
+ getJSONSchemaFromModel?: (model: NonNullable<BODY_MODEL | QUERY_MODEL | PARAMS_MODEL | OUTPUT_MODEL | ITERATION_MODEL>, meta: {
13
+ type: VovkValidationType;
14
+ }) => KnownAny;
15
+ validate: (data: KnownAny, model: NonNullable<BODY_MODEL | QUERY_MODEL | PARAMS_MODEL | OUTPUT_MODEL | ITERATION_MODEL>, meta: {
16
+ type: VovkValidationType;
17
+ req: VovkRequest<KnownAny, KnownAny>;
18
+ status?: number;
19
+ i?: number;
20
+ }) => KnownAny;
21
+ }): T;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withValidation = withValidation;
4
+ const HttpException_js_1 = require("../HttpException.js");
5
+ const types_js_1 = require("../types.js");
6
+ const setHandlerSchema_js_1 = require("./setHandlerSchema.js");
7
+ const validationTypes = ['body', 'query', 'params', 'output', 'iteration'];
8
+ function withValidation({ disableServerSideValidation, skipSchemaEmission, validateEachIteration, body, query, params, output, iteration, handle, getJSONSchemaFromModel, validate, }) {
9
+ const disableServerSideValidationKeys = disableServerSideValidation === false
10
+ ? []
11
+ : disableServerSideValidation === true
12
+ ? validationTypes
13
+ : (disableServerSideValidation ?? []);
14
+ const skipSchemaEmissionKeys = skipSchemaEmission === false ? [] : skipSchemaEmission === true ? validationTypes : (skipSchemaEmission ?? []);
15
+ const outputHandler = async (req, handlerParams) => {
16
+ const data = await handle(req, handlerParams);
17
+ if (output && iteration) {
18
+ throw new HttpException_js_1.HttpException(types_js_1.HttpStatus.INTERNAL_SERVER_ERROR, "Output and iteration are mutually exclusive. You can't use them together.");
19
+ }
20
+ if (output && !disableServerSideValidationKeys.includes('output')) {
21
+ if (!data) {
22
+ throw new HttpException_js_1.HttpException(types_js_1.HttpStatus.INTERNAL_SERVER_ERROR, 'Output is required. You probably forgot to return something from your handler.');
23
+ }
24
+ await validate(data, output, { type: 'output', req });
25
+ }
26
+ if (iteration && !disableServerSideValidationKeys.includes('iteration')) {
27
+ // We assume `data` is an async iterable here; you might want to check that:
28
+ if (!data || typeof data[Symbol.asyncIterator] !== 'function') {
29
+ throw new HttpException_js_1.HttpException(types_js_1.HttpStatus.INTERNAL_SERVER_ERROR, 'Data is not an async iterable but iteration validation is defined.');
30
+ }
31
+ // Return a brand-new async generator that yields validated items
32
+ return (async function* () {
33
+ let i = 0;
34
+ for await (const item of data) {
35
+ if (validateEachIteration || i === 0) {
36
+ await validate(item, iteration, { type: 'iteration', req, status: 200, i });
37
+ }
38
+ i++;
39
+ yield item;
40
+ }
41
+ })();
42
+ }
43
+ else if (validateEachIteration) {
44
+ throw new HttpException_js_1.HttpException(types_js_1.HttpStatus.INTERNAL_SERVER_ERROR, 'validateEachIteration is set but iteration is not defined.');
45
+ }
46
+ return data;
47
+ };
48
+ const resultHandler = async (req, handlerParams) => {
49
+ if (body && !disableServerSideValidationKeys.includes('body')) {
50
+ const data = await req.json();
51
+ const instance = (await validate(data, body, { type: 'body', req })) ?? data;
52
+ // redeclare to add ability to call req.json() again
53
+ req.json = () => Promise.resolve(data);
54
+ req.vovk.body = () => Promise.resolve(instance);
55
+ }
56
+ if (query && !disableServerSideValidationKeys.includes('query')) {
57
+ const data = req.vovk.query();
58
+ const instance = (await validate(data, query, { type: 'query', req })) ?? data;
59
+ req.vovk.query = () => instance;
60
+ }
61
+ if (params && !disableServerSideValidationKeys.includes('params')) {
62
+ const data = req.vovk.params();
63
+ const instance = (await validate(data, params, { type: 'params', req })) ?? data;
64
+ req.vovk.params = () => instance;
65
+ }
66
+ return outputHandler(req, handlerParams);
67
+ };
68
+ if (getJSONSchemaFromModel) {
69
+ const validation = {};
70
+ if (body && !skipSchemaEmissionKeys.includes('body')) {
71
+ validation.body = getJSONSchemaFromModel(body, { type: 'body' });
72
+ }
73
+ if (query && !skipSchemaEmissionKeys.includes('query')) {
74
+ validation.query = getJSONSchemaFromModel(query, { type: 'query' });
75
+ }
76
+ if (params && !skipSchemaEmissionKeys.includes('params')) {
77
+ validation.params = getJSONSchemaFromModel(params, { type: 'params' });
78
+ }
79
+ if (output && !skipSchemaEmissionKeys.includes('output')) {
80
+ validation.output = getJSONSchemaFromModel(output, { type: 'output' });
81
+ }
82
+ if (iteration && !skipSchemaEmissionKeys.includes('iteration')) {
83
+ validation.iteration = getJSONSchemaFromModel(iteration, { type: 'iteration' });
84
+ }
85
+ (0, setHandlerSchema_js_1.setHandlerSchema)(resultHandler, { validation });
86
+ }
87
+ return resultHandler;
88
+ }
@@ -0,0 +1,7 @@
1
+ import type { HttpStatus } from './types.js';
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;
@@ -0,0 +1,14 @@
1
+ import type { headers } from 'next/headers';
2
+ import type { KnownAny, StreamAbortMessage } from './types.js';
3
+ import './utils/shim.js';
4
+ export declare class JSONLinesResponse<T> extends Response {
5
+ isClosed: boolean;
6
+ controller?: ReadableStreamDefaultController;
7
+ readonly encoder: TextEncoder;
8
+ readonly readableStream: ReadableStream;
9
+ constructor(requestHeaders: Awaited<ReturnType<typeof headers>>, init?: ResponseInit);
10
+ send(data: T | StreamAbortMessage): void;
11
+ close(): void;
12
+ throw(e: KnownAny): void;
13
+ [Symbol.dispose](): void;
14
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JSONLinesResponse = void 0;
4
+ require("./utils/shim.js");
5
+ class JSONLinesResponse extends Response {
6
+ isClosed = false;
7
+ controller;
8
+ encoder;
9
+ readableStream;
10
+ constructor(requestHeaders, init) {
11
+ const encoder = new TextEncoder();
12
+ let readableController;
13
+ const readableStream = new ReadableStream({
14
+ cancel: () => {
15
+ this.isClosed = true;
16
+ },
17
+ start: (controller) => {
18
+ readableController = controller;
19
+ },
20
+ });
21
+ if (!requestHeaders) {
22
+ throw new Error('Request headers are required');
23
+ }
24
+ const accept = requestHeaders.get('accept');
25
+ super(readableStream, {
26
+ ...init,
27
+ headers: {
28
+ ...init?.headers,
29
+ 'Content-Type': accept?.includes('application/jsonl')
30
+ ? 'application/jsonl; charset=utf-8'
31
+ : 'text/plain; charset=utf-8',
32
+ },
33
+ });
34
+ this.readableStream = readableStream;
35
+ this.encoder = encoder;
36
+ this.controller = readableController;
37
+ }
38
+ send(data) {
39
+ const { controller, encoder } = this;
40
+ if (this.isClosed)
41
+ return;
42
+ return controller?.enqueue(encoder.encode(JSON.stringify(data) + '\n'));
43
+ }
44
+ close() {
45
+ const { controller } = this;
46
+ if (this.isClosed)
47
+ return;
48
+ this.isClosed = true;
49
+ controller?.close();
50
+ }
51
+ throw(e) {
52
+ this.send({ isError: true, reason: e instanceof Error ? e.message : e });
53
+ return this.close();
54
+ }
55
+ [Symbol.dispose]() {
56
+ this.close();
57
+ }
58
+ }
59
+ exports.JSONLinesResponse = JSONLinesResponse;
@@ -0,0 +1,29 @@
1
+ import type { NextRequest } from 'next/server';
2
+ import { HttpMethod, HttpStatus, type RouteHandler, type VovkController, type DecoratorOptions } from './types.js';
3
+ export declare class VovkApp {
4
+ #private;
5
+ private static getHeadersFromOptions;
6
+ routes: Record<HttpMethod, Map<VovkController, Record<string, RouteHandler>>>;
7
+ GET: (req: NextRequest, data: {
8
+ params: Promise<Record<string, string[]>>;
9
+ }) => Promise<Response>;
10
+ POST: (req: NextRequest, data: {
11
+ params: Promise<Record<string, string[]>>;
12
+ }) => Promise<Response>;
13
+ PUT: (req: NextRequest, data: {
14
+ params: Promise<Record<string, string[]>>;
15
+ }) => Promise<Response>;
16
+ PATCH: (req: NextRequest, data: {
17
+ params: Promise<Record<string, string[]>>;
18
+ }) => Promise<Response>;
19
+ DELETE: (req: NextRequest, data: {
20
+ params: Promise<Record<string, string[]>>;
21
+ }) => Promise<Response>;
22
+ HEAD: (req: NextRequest, data: {
23
+ params: Promise<Record<string, string[]>>;
24
+ }) => Promise<Response>;
25
+ OPTIONS: (req: NextRequest, data: {
26
+ params: Promise<Record<string, string[]>>;
27
+ }) => Promise<Response>;
28
+ respond: (status: HttpStatus, body: unknown, options?: DecoratorOptions) => Response;
29
+ }
package/mjs/VovkApp.js ADDED
@@ -0,0 +1,189 @@
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_js_1 = require("./types.js");
9
+ const HttpException_js_1 = require("./HttpException.js");
10
+ const JSONLinesResponse_js_1 = require("./JSONLinesResponse.js");
11
+ const reqQuery_js_1 = __importDefault(require("./utils/reqQuery.js"));
12
+ const reqMeta_js_1 = __importDefault(require("./utils/reqMeta.js"));
13
+ const reqForm_js_1 = __importDefault(require("./utils/reqForm.js"));
14
+ const headers_1 = require("next/headers");
15
+ class VovkApp {
16
+ static getHeadersFromOptions(options) {
17
+ if (!options)
18
+ return {};
19
+ const corsHeaders = {
20
+ 'access-control-allow-origin': '*',
21
+ 'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS, HEAD',
22
+ 'access-control-allow-headers': 'content-type, authorization',
23
+ };
24
+ const headers = {
25
+ ...(options.cors ? corsHeaders : {}),
26
+ ...(options.headers ?? {}),
27
+ };
28
+ return headers;
29
+ }
30
+ routes = {
31
+ GET: new Map(),
32
+ POST: new Map(),
33
+ PUT: new Map(),
34
+ PATCH: new Map(),
35
+ DELETE: new Map(),
36
+ HEAD: new Map(),
37
+ OPTIONS: new Map(),
38
+ };
39
+ GET = async (req, data) => this.#callMethod(types_js_1.HttpMethod.GET, req, await data.params);
40
+ POST = async (req, data) => this.#callMethod(types_js_1.HttpMethod.POST, req, await data.params);
41
+ PUT = async (req, data) => this.#callMethod(types_js_1.HttpMethod.PUT, req, await data.params);
42
+ PATCH = async (req, data) => this.#callMethod(types_js_1.HttpMethod.PATCH, req, await data.params);
43
+ DELETE = async (req, data) => this.#callMethod(types_js_1.HttpMethod.DELETE, req, await data.params);
44
+ HEAD = async (req, data) => this.#callMethod(types_js_1.HttpMethod.HEAD, req, await data.params);
45
+ OPTIONS = async (req, data) => this.#callMethod(types_js_1.HttpMethod.OPTIONS, req, await data.params);
46
+ respond = (status, body, options) => {
47
+ return new Response(JSON.stringify(body), {
48
+ status,
49
+ headers: {
50
+ 'content-type': 'application/json',
51
+ ..._a.getHeadersFromOptions(options),
52
+ },
53
+ });
54
+ };
55
+ #respondWithError = (statusCode, message, options, cause) => {
56
+ return this.respond(statusCode, {
57
+ cause,
58
+ statusCode,
59
+ message,
60
+ isError: true,
61
+ }, options);
62
+ };
63
+ #getHandler = ({ handlers, path, params, }) => {
64
+ const methodParams = {};
65
+ if (Object.keys(params).length === 0) {
66
+ return { handler: handlers[''], methodParams };
67
+ }
68
+ const allMethodKeys = Object.keys(handlers);
69
+ let methodKeys = [];
70
+ const pathStr = path.join('/');
71
+ methodKeys = allMethodKeys
72
+ // First, try to match literal routes exactly.
73
+ .filter((p) => {
74
+ if (p.includes(':'))
75
+ return false; // Skip parameterized paths
76
+ return p === pathStr;
77
+ });
78
+ if (!methodKeys.length) {
79
+ methodKeys = allMethodKeys.filter((p) => {
80
+ const routeSegments = p.split('/');
81
+ if (routeSegments.length !== path.length)
82
+ return false;
83
+ for (let i = 0; i < routeSegments.length; i++) {
84
+ const routeSegment = routeSegments[i];
85
+ const pathSegment = path[i];
86
+ if (routeSegment.startsWith(':')) {
87
+ const parameter = routeSegment.slice(1);
88
+ if (parameter in methodParams) {
89
+ throw new HttpException_js_1.HttpException(types_js_1.HttpStatus.INTERNAL_SERVER_ERROR, `Duplicate parameter "${parameter}" at ${p}`);
90
+ }
91
+ // If it's a parameterized segment, capture the parameter value.
92
+ methodParams[parameter] = pathSegment;
93
+ }
94
+ else if (routeSegment !== pathSegment) {
95
+ // If it's a literal segment and it does not match the corresponding path segment, return false.
96
+ return false;
97
+ }
98
+ }
99
+ return true;
100
+ });
101
+ }
102
+ if (methodKeys.length > 1) {
103
+ throw new HttpException_js_1.HttpException(types_js_1.HttpStatus.INTERNAL_SERVER_ERROR, `Conflicting routes found: ${methodKeys.join(', ')}`);
104
+ }
105
+ const [methodKey] = methodKeys;
106
+ if (methodKey) {
107
+ return { handler: handlers[methodKey], methodParams };
108
+ }
109
+ return { handler: null, methodParams };
110
+ };
111
+ #callMethod = async (httpMethod, nextReq, params) => {
112
+ const req = nextReq;
113
+ const controllers = this.routes[httpMethod];
114
+ const path = params[Object.keys(params)[0]];
115
+ const handlers = {};
116
+ controllers.forEach((staticMethods, controller) => {
117
+ const prefix = controller._prefix ?? '';
118
+ if (!controller._activated) {
119
+ throw new HttpException_js_1.HttpException(types_js_1.HttpStatus.INTERNAL_SERVER_ERROR, `Controller "${controller.name}" found but not activated`);
120
+ }
121
+ Object.entries(staticMethods).forEach(([path, staticMethod]) => {
122
+ const fullPath = [prefix, path].filter(Boolean).join('/');
123
+ handlers[fullPath] = { staticMethod, controller };
124
+ });
125
+ });
126
+ const { handler, methodParams } = this.#getHandler({ handlers, path, params });
127
+ if (!handler) {
128
+ return this.#respondWithError(types_js_1.HttpStatus.NOT_FOUND, `${Object.keys(handlers)} - Route ${path.join('/')} is not found`);
129
+ }
130
+ const { staticMethod, controller } = handler;
131
+ req.vovk = {
132
+ body: () => req.json(),
133
+ query: () => (0, reqQuery_js_1.default)(req),
134
+ meta: (meta) => (0, reqMeta_js_1.default)(req, meta),
135
+ form: () => (0, reqForm_js_1.default)(req),
136
+ params: () => methodParams,
137
+ };
138
+ try {
139
+ await staticMethod._options?.before?.call(controller, req);
140
+ const result = await staticMethod.call(controller, req, methodParams);
141
+ const isIterator = typeof result === 'object' &&
142
+ !!result &&
143
+ ((Reflect.has(result, Symbol.iterator) &&
144
+ typeof result[Symbol.iterator] === 'function') ||
145
+ (Reflect.has(result, Symbol.asyncIterator) &&
146
+ typeof result[Symbol.asyncIterator] === 'function'));
147
+ if (isIterator && !(result instanceof Array)) {
148
+ const streamResponse = new JSONLinesResponse_js_1.JSONLinesResponse(await (0, headers_1.headers)(), {
149
+ headers: {
150
+ ..._a.getHeadersFromOptions(staticMethod._options),
151
+ },
152
+ });
153
+ void (async () => {
154
+ try {
155
+ for await (const chunk of result) {
156
+ streamResponse.send(chunk);
157
+ }
158
+ }
159
+ catch (e) {
160
+ return streamResponse.throw(e);
161
+ }
162
+ return streamResponse.close();
163
+ })();
164
+ return streamResponse;
165
+ }
166
+ if (result instanceof Response) {
167
+ return result;
168
+ }
169
+ return this.respond(200, result ?? null, staticMethod._options);
170
+ }
171
+ catch (e) {
172
+ const err = e;
173
+ try {
174
+ await controller._onError?.(err, req);
175
+ }
176
+ catch (onErrorError) {
177
+ // eslint-disable-next-line no-console
178
+ console.error(onErrorError);
179
+ }
180
+ if (err.message !== 'NEXT_REDIRECT' && err.message !== 'NEXT_NOT_FOUND') {
181
+ const statusCode = err.statusCode || types_js_1.HttpStatus.INTERNAL_SERVER_ERROR;
182
+ return this.#respondWithError(statusCode, err.message, staticMethod._options, err.cause);
183
+ }
184
+ throw e; // if NEXT_REDIRECT or NEXT_NOT_FOUND, rethrow it
185
+ }
186
+ };
187
+ }
188
+ exports.VovkApp = VovkApp;
189
+ _a = VovkApp;
@@ -0,0 +1,3 @@
1
+ import type { KnownAny, VovkSchema } from '../types.js';
2
+ import type { VovkClientOptions, VovkClient, VovkDefaultFetcherOptions } from './types.js';
3
+ export declare const createRPC: <T, OPTS extends Record<string, KnownAny> = VovkDefaultFetcherOptions<Record<string, never>>>(schema: VovkSchema, segmentName: string, rpcModuleName: string, options?: VovkClientOptions<OPTS>) => VovkClient<T, OPTS>;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createRPC = void 0;
7
+ const fetcher_js_1 = require("./fetcher.js");
8
+ const defaultHandler_js_1 = require("./defaultHandler.js");
9
+ const defaultStreamHandler_js_1 = require("./defaultStreamHandler.js");
10
+ const serializeQuery_js_1 = __importDefault(require("../utils/serializeQuery.js"));
11
+ const trimPath = (path) => path.trim().replace(/^\/|\/$/g, '');
12
+ const getHandlerPath = (endpoint, params, query) => {
13
+ let result = endpoint;
14
+ const queryStr = query ? (0, serializeQuery_js_1.default)(query) : null;
15
+ for (const [key, value] of Object.entries(params ?? {})) {
16
+ result = result.replace(`:${key}`, value);
17
+ }
18
+ return `${result}${queryStr ? '?' : ''}${queryStr}`;
19
+ };
20
+ const createRPC = (schema, segmentName, rpcModuleName, options) => {
21
+ const segmentSchema = schema.segments[segmentName];
22
+ if (!segmentSchema)
23
+ throw new Error(`Unable to create RPC object. Segment schema is missing. Check client template.`);
24
+ const controllerSchema = schema.segments[segmentName]?.controllers[rpcModuleName];
25
+ const client = {};
26
+ if (!controllerSchema)
27
+ throw new Error(`Unable to create RPC object. Controller schema is missing. Check client template.`);
28
+ const controllerPrefix = trimPath(controllerSchema.prefix ?? '');
29
+ const { fetcher: settingsFetcher = fetcher_js_1.fetcher } = options ?? {};
30
+ for (const [staticMethodName, handlerSchema] of Object.entries(controllerSchema.handlers)) {
31
+ const { path, httpMethod, validation } = handlerSchema;
32
+ const getEndpoint = ({ apiRoot, params, query, }) => {
33
+ const mainPrefix = (apiRoot.startsWith('http://') || apiRoot.startsWith('https://') || apiRoot.startsWith('/') ? '' : '/') +
34
+ (apiRoot.endsWith('/') ? apiRoot : `${apiRoot}/`) +
35
+ (segmentName ? `${segmentName}/` : '');
36
+ return mainPrefix + getHandlerPath([controllerPrefix, path].filter(Boolean).join('/'), params, query);
37
+ };
38
+ const handler = (input = {}) => {
39
+ const fetcher = input.fetcher ?? settingsFetcher;
40
+ const validate = async ({ body, query, params, endpoint, }) => {
41
+ const validateOnClient = input.validateOnClient ?? options?.validateOnClient;
42
+ if (validateOnClient && validation) {
43
+ if (typeof validateOnClient !== 'function') {
44
+ throw new Error('validateOnClient must be a function');
45
+ }
46
+ await validateOnClient({ body, query, params, endpoint }, validation, schema);
47
+ }
48
+ };
49
+ const internalOptions = {
50
+ name: staticMethodName,
51
+ httpMethod: httpMethod,
52
+ getEndpoint,
53
+ validate,
54
+ defaultHandler: defaultHandler_js_1.defaultHandler,
55
+ defaultStreamHandler: defaultStreamHandler_js_1.defaultStreamHandler,
56
+ };
57
+ const internalInput = {
58
+ ...options?.defaultOptions,
59
+ ...input,
60
+ body: input.body ?? null,
61
+ query: input.query ?? {},
62
+ params: input.params ?? {},
63
+ // TS workaround
64
+ fetcher: undefined,
65
+ validateOnClient: undefined,
66
+ };
67
+ delete internalInput.fetcher;
68
+ delete internalInput.validateOnClient;
69
+ if (!fetcher)
70
+ throw new Error('Fetcher is not provided');
71
+ const fetcherPromise = fetcher(internalOptions, internalInput);
72
+ if (!(fetcherPromise instanceof Promise))
73
+ return Promise.resolve(fetcherPromise);
74
+ return input.transform ? fetcherPromise.then(input.transform) : fetcherPromise;
75
+ };
76
+ handler.schema = handlerSchema;
77
+ handler.controllerSchema = controllerSchema;
78
+ handler.segmentSchema = segmentSchema;
79
+ handler.fullSchema = schema;
80
+ handler.isRPC = true;
81
+ // @ts-expect-error TODO
82
+ client[staticMethodName] = handler;
83
+ }
84
+ return client;
85
+ };
86
+ exports.createRPC = createRPC;
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_ERROR_MESSAGE = "Unknown error at defaultHandler";
2
+ export declare const defaultHandler: (response: Response) => Promise<unknown>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultHandler = exports.DEFAULT_ERROR_MESSAGE = void 0;
4
+ const HttpException_js_1 = require("../HttpException.js");
5
+ exports.DEFAULT_ERROR_MESSAGE = 'Unknown error at defaultHandler';
6
+ const defaultHandler = async (response) => {
7
+ let result;
8
+ try {
9
+ result = await response.json();
10
+ }
11
+ catch (e) {
12
+ // handle parsing errors
13
+ throw new HttpException_js_1.HttpException(response.status, e?.message ?? exports.DEFAULT_ERROR_MESSAGE);
14
+ }
15
+ if (!response.ok) {
16
+ // handle server errors
17
+ const errorResponse = result;
18
+ throw new HttpException_js_1.HttpException(response.status, errorResponse?.message ?? exports.DEFAULT_ERROR_MESSAGE, errorResponse?.cause);
19
+ }
20
+ return result;
21
+ };
22
+ exports.defaultHandler = defaultHandler;