vovk 3.0.0-draft.46 → 3.0.0-draft.461
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/LICENSE +1 -1
- package/README.md +8 -96
- package/bin/index.mjs +8 -0
- package/cjs/JSONLinesResponse.d.ts +19 -0
- package/cjs/JSONLinesResponse.js +95 -0
- package/{VovkApp.d.ts → cjs/VovkApp.d.ts} +14 -8
- package/cjs/VovkApp.js +304 -0
- package/cjs/client/createRPC.d.ts +5 -0
- package/cjs/client/createRPC.js +116 -0
- package/cjs/client/defaultHandler.d.ts +6 -0
- package/{client → cjs/client}/defaultHandler.js +9 -2
- package/cjs/client/defaultStreamHandler.d.ts +9 -0
- package/{client → cjs/client}/defaultStreamHandler.js +49 -26
- package/cjs/client/fetcher.d.ts +18 -0
- package/cjs/client/fetcher.js +97 -0
- package/cjs/client/index.d.ts +4 -0
- package/cjs/client/index.js +10 -0
- package/cjs/client/progressive.d.ts +9 -0
- package/cjs/client/progressive.js +54 -0
- package/cjs/client/types.d.ts +120 -0
- package/{createVovkApp.d.ts → cjs/createVovkApp.d.ts} +19 -16
- package/cjs/createVovkApp.js +146 -0
- package/cjs/index.d.ts +69 -0
- package/cjs/index.js +42 -0
- package/cjs/openapi/error.d.ts +2 -0
- package/cjs/openapi/error.js +100 -0
- package/cjs/openapi/index.d.ts +8 -0
- package/cjs/openapi/index.js +21 -0
- package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
- package/cjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
- package/cjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
- package/cjs/openapi/openAPIToVovkSchema/index.js +141 -0
- package/cjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
- package/cjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
- package/cjs/openapi/vovkSchemaToOpenAPI.d.ts +8 -0
- package/cjs/openapi/vovkSchemaToOpenAPI.js +238 -0
- package/cjs/types.d.ts +466 -0
- package/{types.js → cjs/types.js} +14 -2
- package/cjs/utils/camelCase.d.ts +6 -0
- package/cjs/utils/camelCase.js +37 -0
- package/cjs/utils/createCodeSamples.d.ts +20 -0
- package/cjs/utils/createCodeSamples.js +279 -0
- package/cjs/utils/createDecorator.d.ts +6 -0
- package/{createDecorator.js → cjs/utils/createDecorator.js} +24 -14
- package/cjs/utils/createLLMTools.d.ts +50 -0
- package/cjs/utils/createLLMTools.js +168 -0
- package/cjs/utils/createStandardValidation.d.ts +83 -0
- package/cjs/utils/createStandardValidation.js +35 -0
- package/cjs/utils/createValidateOnClient.d.ts +7 -0
- package/cjs/utils/createValidateOnClient.js +19 -0
- package/cjs/utils/deepExtend.d.ts +54 -0
- package/cjs/utils/deepExtend.js +134 -0
- package/{utils → cjs/utils}/generateStaticAPI.d.ts +2 -2
- package/cjs/utils/generateStaticAPI.js +30 -0
- package/cjs/utils/getJSONSchemaExample.d.ts +11 -0
- package/cjs/utils/getJSONSchemaExample.js +264 -0
- package/cjs/utils/getJSONSchemaSample.d.ts +2 -0
- package/cjs/utils/getJSONSchemaSample.js +167 -0
- package/cjs/utils/getSampleFromObject.d.ts +9 -0
- package/cjs/utils/getSampleFromObject.js +41 -0
- package/cjs/utils/getSchema.d.ts +21 -0
- package/cjs/utils/getSchema.js +38 -0
- package/cjs/utils/multitenant.d.ts +24 -0
- package/cjs/utils/multitenant.js +131 -0
- package/cjs/utils/parseQuery.d.ts +25 -0
- package/cjs/utils/parseQuery.js +156 -0
- package/cjs/utils/reqForm.d.ts +2 -0
- package/cjs/utils/reqForm.js +33 -0
- package/cjs/utils/reqMeta.d.ts +2 -0
- package/cjs/utils/reqQuery.d.ts +2 -0
- package/cjs/utils/reqQuery.js +10 -0
- package/cjs/utils/resolveGeneratorConfigValues.d.ts +18 -0
- package/cjs/utils/resolveGeneratorConfigValues.js +82 -0
- package/cjs/utils/serializeQuery.d.ts +13 -0
- package/cjs/utils/serializeQuery.js +65 -0
- package/cjs/utils/setHandlerSchema.d.ts +4 -0
- package/cjs/utils/setHandlerSchema.js +15 -0
- package/cjs/utils/upperFirst.d.ts +1 -0
- package/cjs/utils/upperFirst.js +6 -0
- package/cjs/utils/withValidationLibrary.d.ts +78 -0
- package/cjs/utils/withValidationLibrary.js +133 -0
- package/mjs/HttpException.d.ts +7 -0
- package/mjs/HttpException.js +15 -0
- package/mjs/JSONLinesResponse.d.ts +19 -0
- package/mjs/JSONLinesResponse.js +95 -0
- package/mjs/VovkApp.d.ts +34 -0
- package/mjs/VovkApp.js +304 -0
- package/mjs/client/createRPC.d.ts +5 -0
- package/mjs/client/createRPC.js +116 -0
- package/mjs/client/defaultHandler.d.ts +6 -0
- package/mjs/client/defaultHandler.js +29 -0
- package/mjs/client/defaultStreamHandler.d.ts +9 -0
- package/mjs/client/defaultStreamHandler.js +105 -0
- package/mjs/client/fetcher.d.ts +18 -0
- package/mjs/client/fetcher.js +97 -0
- package/mjs/client/index.d.ts +4 -0
- package/mjs/client/index.js +10 -0
- package/mjs/client/progressive.d.ts +9 -0
- package/mjs/client/progressive.js +54 -0
- package/mjs/client/types.d.ts +120 -0
- package/mjs/createVovkApp.d.ts +65 -0
- package/mjs/createVovkApp.js +146 -0
- package/mjs/index.d.ts +69 -0
- package/mjs/index.js +42 -0
- package/mjs/openapi/error.d.ts +2 -0
- package/mjs/openapi/error.js +100 -0
- package/mjs/openapi/index.d.ts +8 -0
- package/mjs/openapi/index.js +21 -0
- package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.d.ts +3 -0
- package/mjs/openapi/openAPIToVovkSchema/applyComponentsSchemas.js +67 -0
- package/mjs/openapi/openAPIToVovkSchema/index.d.ts +4 -0
- package/mjs/openapi/openAPIToVovkSchema/index.js +141 -0
- package/mjs/openapi/openAPIToVovkSchema/inlineRefs.d.ts +10 -0
- package/mjs/openapi/openAPIToVovkSchema/inlineRefs.js +102 -0
- package/mjs/openapi/vovkSchemaToOpenAPI.d.ts +8 -0
- package/mjs/openapi/vovkSchemaToOpenAPI.js +238 -0
- package/mjs/types.d.ts +466 -0
- package/mjs/types.js +77 -0
- package/mjs/utils/camelCase.d.ts +6 -0
- package/mjs/utils/camelCase.js +37 -0
- package/mjs/utils/createCodeSamples.d.ts +20 -0
- package/mjs/utils/createCodeSamples.js +279 -0
- package/mjs/utils/createDecorator.d.ts +6 -0
- package/mjs/utils/createDecorator.js +48 -0
- package/mjs/utils/createLLMTools.d.ts +50 -0
- package/mjs/utils/createLLMTools.js +168 -0
- package/mjs/utils/createStandardValidation.d.ts +83 -0
- package/mjs/utils/createStandardValidation.js +35 -0
- package/mjs/utils/createValidateOnClient.d.ts +7 -0
- package/mjs/utils/createValidateOnClient.js +19 -0
- package/mjs/utils/deepExtend.d.ts +54 -0
- package/mjs/utils/deepExtend.js +134 -0
- package/mjs/utils/generateStaticAPI.d.ts +4 -0
- package/mjs/utils/generateStaticAPI.js +30 -0
- package/mjs/utils/getJSONSchemaExample.d.ts +11 -0
- package/mjs/utils/getJSONSchemaExample.js +264 -0
- package/mjs/utils/getJSONSchemaSample.d.ts +2 -0
- package/mjs/utils/getJSONSchemaSample.js +167 -0
- package/mjs/utils/getSampleFromObject.d.ts +9 -0
- package/mjs/utils/getSampleFromObject.js +41 -0
- package/mjs/utils/getSchema.d.ts +21 -0
- package/mjs/utils/getSchema.js +38 -0
- package/mjs/utils/multitenant.d.ts +24 -0
- package/mjs/utils/multitenant.js +131 -0
- package/mjs/utils/parseQuery.d.ts +25 -0
- package/mjs/utils/parseQuery.js +156 -0
- package/mjs/utils/reqForm.d.ts +2 -0
- package/mjs/utils/reqForm.js +33 -0
- package/mjs/utils/reqMeta.d.ts +2 -0
- package/mjs/utils/reqMeta.js +13 -0
- package/mjs/utils/reqQuery.d.ts +2 -0
- package/mjs/utils/reqQuery.js +10 -0
- package/mjs/utils/resolveGeneratorConfigValues.d.ts +18 -0
- package/mjs/utils/resolveGeneratorConfigValues.js +82 -0
- package/mjs/utils/serializeQuery.d.ts +13 -0
- package/mjs/utils/serializeQuery.js +65 -0
- package/mjs/utils/setHandlerSchema.d.ts +4 -0
- package/mjs/utils/setHandlerSchema.js +15 -0
- package/mjs/utils/shim.d.ts +1 -0
- package/mjs/utils/shim.js +18 -0
- package/mjs/utils/upperFirst.d.ts +1 -0
- package/mjs/utils/upperFirst.js +6 -0
- package/mjs/utils/withValidationLibrary.d.ts +78 -0
- package/mjs/utils/withValidationLibrary.js +133 -0
- package/package.json +28 -6
- package/.npmignore +0 -2
- package/StreamJSONResponse.d.ts +0 -17
- package/StreamJSONResponse.js +0 -54
- package/VovkApp.js +0 -185
- package/client/clientizeController.d.ts +0 -4
- package/client/clientizeController.js +0 -93
- package/client/defaultFetcher.d.ts +0 -4
- package/client/defaultFetcher.js +0 -49
- package/client/defaultHandler.d.ts +0 -2
- package/client/defaultStreamHandler.d.ts +0 -4
- package/client/index.d.ts +0 -4
- package/client/index.js +0 -5
- package/client/types.d.ts +0 -100
- package/createDecorator.d.ts +0 -4
- package/createVovkApp.js +0 -118
- package/index.d.ts +0 -60
- package/index.js +0 -20
- package/types.d.ts +0 -157
- package/utils/generateStaticAPI.js +0 -18
- package/utils/getSchema.d.ts +0 -8
- package/utils/getSchema.js +0 -38
- package/utils/reqForm.d.ts +0 -3
- package/utils/reqForm.js +0 -13
- package/utils/reqMeta.d.ts +0 -3
- package/utils/reqQuery.d.ts +0 -3
- package/utils/reqQuery.js +0 -25
- package/utils/setClientValidatorsForHandler.d.ts +0 -5
- package/utils/setClientValidatorsForHandler.js +0 -25
- package/worker/index.d.ts +0 -3
- package/worker/index.js +0 -7
- package/worker/promisifyWorker.d.ts +0 -2
- package/worker/promisifyWorker.js +0 -141
- package/worker/types.d.ts +0 -31
- package/worker/worker.d.ts +0 -1
- package/worker/worker.js +0 -43
- /package/{HttpException.d.ts → cjs/HttpException.d.ts} +0 -0
- /package/{HttpException.js → cjs/HttpException.js} +0 -0
- /package/{client → cjs/client}/types.js +0 -0
- /package/{utils → cjs/utils}/reqMeta.js +0 -0
- /package/{utils → cjs/utils}/shim.d.ts +0 -0
- /package/{utils → cjs/utils}/shim.js +0 -0
- /package/{worker → mjs/client}/types.js +0 -0
package/mjs/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>;
|
|
@@ -0,0 +1,116 @@
|
|
|
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_1 = require("./fetcher");
|
|
8
|
+
const defaultHandler_1 = require("./defaultHandler");
|
|
9
|
+
const defaultStreamHandler_1 = require("./defaultStreamHandler");
|
|
10
|
+
const serializeQuery_1 = __importDefault(require("../utils/serializeQuery"));
|
|
11
|
+
const trimPath = (path) => path.trim().replace(/^\/|\/$/g, '');
|
|
12
|
+
const getHandlerPath = (endpoint, params, query) => {
|
|
13
|
+
let result = endpoint;
|
|
14
|
+
const queryStr = query ? (0, serializeQuery_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 = (givenSchema, segmentName, rpcModuleName, givenFetcher, options) => {
|
|
21
|
+
const schema = givenSchema; // fixes incompatibilities with JSON module
|
|
22
|
+
// fetcher ??= defaultFetcher as NonNullable<typeof fetcher>;
|
|
23
|
+
const segmentNamePath = options?.segmentNameOverride ?? segmentName;
|
|
24
|
+
const segmentSchema = schema.segments[segmentName];
|
|
25
|
+
if (!segmentSchema)
|
|
26
|
+
throw new Error(`Unable to create RPC module. Segment schema is missing for segment "${segmentName}".`);
|
|
27
|
+
let controllerSchema = schema.segments[segmentName]?.controllers[rpcModuleName];
|
|
28
|
+
const client = {};
|
|
29
|
+
if (!controllerSchema) {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.warn(`🐺 Unable to create RPC module. Controller schema is missing for module "${rpcModuleName}" from segment "${segmentName}". Assuming that schema is not ready yet and a segment is importing an uncompiled RPC module.`);
|
|
32
|
+
controllerSchema = {
|
|
33
|
+
rpcModuleName,
|
|
34
|
+
prefix: '',
|
|
35
|
+
handlers: {},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const controllerPrefix = trimPath(controllerSchema.prefix ?? '');
|
|
39
|
+
const forceApiRoot = controllerSchema.forceApiRoot ?? segmentSchema.forceApiRoot;
|
|
40
|
+
const originalApiRoot = forceApiRoot ?? options?.apiRoot ?? '/api';
|
|
41
|
+
for (const [staticMethodName, handlerSchema] of Object.entries(controllerSchema.handlers ?? {})) {
|
|
42
|
+
const { path, httpMethod, validation } = handlerSchema;
|
|
43
|
+
const getEndpoint = ({ apiRoot, params, query }) => {
|
|
44
|
+
apiRoot = apiRoot ?? originalApiRoot;
|
|
45
|
+
const endpoint = [
|
|
46
|
+
apiRoot.startsWith('http://') || apiRoot.startsWith('https://') || apiRoot.startsWith('/') ? '' : '/',
|
|
47
|
+
apiRoot,
|
|
48
|
+
forceApiRoot ? '' : segmentNamePath,
|
|
49
|
+
getHandlerPath([controllerPrefix, path].filter(Boolean).join('/'), params, query),
|
|
50
|
+
]
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.join('/')
|
|
53
|
+
.replace(/([^:])\/+/g, '$1/'); // replace // by / but not for protocols (http://, https://)
|
|
54
|
+
return endpoint;
|
|
55
|
+
};
|
|
56
|
+
const handler = (async (input = {}) => {
|
|
57
|
+
const fetcher = givenFetcher instanceof Promise
|
|
58
|
+
? (await givenFetcher).fetcher
|
|
59
|
+
: (givenFetcher ?? fetcher_1.fetcher);
|
|
60
|
+
const validate = async (validationInput, { endpoint, }) => {
|
|
61
|
+
const validateOnClient = input.validateOnClient ??
|
|
62
|
+
(options?.validateOnClient instanceof Promise
|
|
63
|
+
? (await options?.validateOnClient)?.validateOnClient
|
|
64
|
+
: options?.validateOnClient);
|
|
65
|
+
if (validateOnClient && validation) {
|
|
66
|
+
if (typeof validateOnClient !== 'function') {
|
|
67
|
+
throw new Error('validateOnClient must be a function');
|
|
68
|
+
}
|
|
69
|
+
return ((await validateOnClient({ ...validationInput }, validation, { fullSchema: schema, endpoint })) ??
|
|
70
|
+
validationInput);
|
|
71
|
+
}
|
|
72
|
+
return validationInput;
|
|
73
|
+
};
|
|
74
|
+
const internalOptions = {
|
|
75
|
+
name: staticMethodName,
|
|
76
|
+
httpMethod: httpMethod,
|
|
77
|
+
getEndpoint,
|
|
78
|
+
validate,
|
|
79
|
+
defaultHandler: defaultHandler_1.defaultHandler,
|
|
80
|
+
defaultStreamHandler: defaultStreamHandler_1.defaultStreamHandler,
|
|
81
|
+
schema: handlerSchema,
|
|
82
|
+
};
|
|
83
|
+
const internalInput = {
|
|
84
|
+
...options,
|
|
85
|
+
...input,
|
|
86
|
+
body: input.body ?? null,
|
|
87
|
+
query: input.query ?? {},
|
|
88
|
+
params: input.params ?? {},
|
|
89
|
+
};
|
|
90
|
+
if (!fetcher)
|
|
91
|
+
throw new Error('Fetcher is not provided');
|
|
92
|
+
const [respData, resp] = await fetcher(internalOptions, internalInput);
|
|
93
|
+
return input.transform ? input.transform(respData, resp) : respData;
|
|
94
|
+
});
|
|
95
|
+
// TODO use Object.freeze, Object.seal or Object.defineProperty to avoid mutation
|
|
96
|
+
handler.schema = handlerSchema;
|
|
97
|
+
handler.controllerSchema = controllerSchema;
|
|
98
|
+
handler.segmentSchema = segmentSchema;
|
|
99
|
+
handler.fullSchema = schema;
|
|
100
|
+
handler.isRPC = true;
|
|
101
|
+
handler.apiRoot = originalApiRoot;
|
|
102
|
+
handler.path = [segmentNamePath, controllerPrefix, path].filter(Boolean).join('/');
|
|
103
|
+
handler.queryKey = (key) => [
|
|
104
|
+
handler.segmentSchema.segmentName,
|
|
105
|
+
handler.controllerSchema.prefix ?? '',
|
|
106
|
+
handler.controllerSchema.rpcModuleName,
|
|
107
|
+
handler.schema.path,
|
|
108
|
+
handler.schema.httpMethod,
|
|
109
|
+
...(key ?? []),
|
|
110
|
+
];
|
|
111
|
+
// @ts-expect-error TODO
|
|
112
|
+
client[staticMethodName] = handler;
|
|
113
|
+
}
|
|
114
|
+
return client;
|
|
115
|
+
};
|
|
116
|
+
exports.createRPC = createRPC;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultHandler = exports.DEFAULT_ERROR_MESSAGE = void 0;
|
|
4
|
+
const HttpException_1 = require("../HttpException");
|
|
5
|
+
exports.DEFAULT_ERROR_MESSAGE = 'Unknown error at defaultHandler';
|
|
6
|
+
// Helper function to get a value from an object using dot notation path
|
|
7
|
+
const getNestedValue = (obj, path) => {
|
|
8
|
+
return path.split('.').reduce((o, key) => (o && typeof o === 'object' ? o[key] : undefined), obj);
|
|
9
|
+
};
|
|
10
|
+
const defaultHandler = async ({ response, schema }) => {
|
|
11
|
+
let result;
|
|
12
|
+
try {
|
|
13
|
+
result = await response.json();
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
// handle parsing errors
|
|
17
|
+
throw new HttpException_1.HttpException(response.status, e?.message ?? exports.DEFAULT_ERROR_MESSAGE);
|
|
18
|
+
}
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
const errorKey = schema.operationObject && 'x-errorMessageKey' in schema.operationObject
|
|
21
|
+
? schema.operationObject['x-errorMessageKey']
|
|
22
|
+
: 'message';
|
|
23
|
+
// handle server errors
|
|
24
|
+
const errorResponse = result;
|
|
25
|
+
throw new HttpException_1.HttpException(response.status, getNestedValue(errorResponse, errorKey) ?? exports.DEFAULT_ERROR_MESSAGE, errorResponse?.cause ?? JSON.stringify(result));
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
exports.defaultHandler = defaultHandler;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { VovkHandlerSchema } from '../types';
|
|
2
|
+
import type { VovkStreamAsyncIterable } from './types';
|
|
3
|
+
import '../utils/shim';
|
|
4
|
+
export declare const DEFAULT_ERROR_MESSAGE = "An unknown error at defaultStreamHandler";
|
|
5
|
+
export declare const defaultStreamHandler: ({ response, abortController, }: {
|
|
6
|
+
response: Response;
|
|
7
|
+
abortController: AbortController;
|
|
8
|
+
schema: VovkHandlerSchema;
|
|
9
|
+
}) => VovkStreamAsyncIterable<unknown>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultStreamHandler = exports.DEFAULT_ERROR_MESSAGE = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const HttpException_1 = require("../HttpException");
|
|
6
|
+
require("../utils/shim");
|
|
7
|
+
exports.DEFAULT_ERROR_MESSAGE = 'An unknown error at defaultStreamHandler';
|
|
8
|
+
const defaultStreamHandler = ({ response, abortController, }) => {
|
|
9
|
+
if (!response.ok) {
|
|
10
|
+
response
|
|
11
|
+
.json()
|
|
12
|
+
.then((res) => {
|
|
13
|
+
throw new HttpException_1.HttpException(response.status, res.message ?? exports.DEFAULT_ERROR_MESSAGE);
|
|
14
|
+
})
|
|
15
|
+
.catch((e) => {
|
|
16
|
+
throw new HttpException_1.HttpException(response.status, e.message ?? exports.DEFAULT_ERROR_MESSAGE, e);
|
|
17
|
+
});
|
|
18
|
+
// handle server errors
|
|
19
|
+
}
|
|
20
|
+
if (!response.body)
|
|
21
|
+
throw new HttpException_1.HttpException(types_1.HttpStatus.NULL, 'Stream body is falsy. Check your controller code.');
|
|
22
|
+
const reader = response.body.getReader();
|
|
23
|
+
const subscribers = new Set();
|
|
24
|
+
async function* asyncIterator() {
|
|
25
|
+
let prepend = '';
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (true) {
|
|
28
|
+
let value;
|
|
29
|
+
try {
|
|
30
|
+
let done;
|
|
31
|
+
if (abortController.signal.aborted)
|
|
32
|
+
break;
|
|
33
|
+
({ value, done } = await reader.read());
|
|
34
|
+
if (done)
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
// await reader.cancel(); // TODO in which cases it needs to be canceled?
|
|
39
|
+
const err = new Error('JSONLines stream error. ' + String(error));
|
|
40
|
+
err.cause = error;
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
// typeof value === 'number' is a workaround for React Native
|
|
44
|
+
const string = typeof value === 'number' ? String.fromCharCode(value) : new TextDecoder().decode(value);
|
|
45
|
+
prepend += string;
|
|
46
|
+
const lines = prepend.split('\n').filter(Boolean);
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
let data;
|
|
49
|
+
try {
|
|
50
|
+
data = JSON.parse(line);
|
|
51
|
+
prepend = prepend.slice(line.length + 1);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
if (data) {
|
|
57
|
+
subscribers.forEach((cb) => {
|
|
58
|
+
if (!abortController.signal.aborted)
|
|
59
|
+
cb(data, i);
|
|
60
|
+
});
|
|
61
|
+
i++;
|
|
62
|
+
if ('isError' in data && 'reason' in data) {
|
|
63
|
+
const upcomingError = data.reason;
|
|
64
|
+
abortController.abort(data.reason);
|
|
65
|
+
if (typeof upcomingError === 'string') {
|
|
66
|
+
throw new Error(upcomingError);
|
|
67
|
+
}
|
|
68
|
+
throw upcomingError;
|
|
69
|
+
}
|
|
70
|
+
else if (!abortController.signal.aborted) {
|
|
71
|
+
yield data;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const asPromise = async () => {
|
|
78
|
+
const items = [];
|
|
79
|
+
for await (const item of asyncIterator()) {
|
|
80
|
+
items.push(item);
|
|
81
|
+
}
|
|
82
|
+
return items;
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
status: response.status,
|
|
86
|
+
asPromise,
|
|
87
|
+
abortController,
|
|
88
|
+
[Symbol.asyncIterator]: asyncIterator,
|
|
89
|
+
[Symbol.dispose]: () => {
|
|
90
|
+
abortController.abort('Stream disposed');
|
|
91
|
+
},
|
|
92
|
+
[Symbol.asyncDispose]: () => {
|
|
93
|
+
abortController.abort('Stream async disposed');
|
|
94
|
+
},
|
|
95
|
+
onIterate: (cb) => {
|
|
96
|
+
if (abortController.signal.aborted)
|
|
97
|
+
return () => { };
|
|
98
|
+
subscribers.add(cb);
|
|
99
|
+
return () => {
|
|
100
|
+
subscribers.delete(cb);
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
exports.defaultStreamHandler = defaultStreamHandler;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { VovkFetcherOptions, VovkFetcher } from './types';
|
|
2
|
+
import { HttpException } from '../HttpException';
|
|
3
|
+
export declare const DEFAULT_ERROR_MESSAGE = "Unknown error at default fetcher";
|
|
4
|
+
export declare function createFetcher<T>({ prepareRequestInit, transformResponse, onSuccess, onError, }?: {
|
|
5
|
+
prepareRequestInit?: (init: RequestInit, options: VovkFetcherOptions<T>) => RequestInit | Promise<RequestInit>;
|
|
6
|
+
transformResponse?: (respData: unknown, options: VovkFetcherOptions<T>, response: Response, init: RequestInit) => unknown | Promise<unknown>;
|
|
7
|
+
onSuccess?: (respData: unknown, options: VovkFetcherOptions<T>, response: Response, init: RequestInit) => void | Promise<void>;
|
|
8
|
+
onError?: (error: HttpException, options: VovkFetcherOptions<T>, response: Response | null, init: RequestInit | null, respData: unknown | null) => void | Promise<void>;
|
|
9
|
+
}): VovkFetcher<VovkFetcherOptions<T>>;
|
|
10
|
+
export declare const fetcher: VovkFetcher<{
|
|
11
|
+
apiRoot?: string;
|
|
12
|
+
disableClientValidation?: boolean;
|
|
13
|
+
validateOnClient?: import("./types").VovkValidateOnClient<unknown> | Promise<{
|
|
14
|
+
validateOnClient: import("./types").VovkValidateOnClient<unknown>;
|
|
15
|
+
}> | undefined;
|
|
16
|
+
interpretAs?: string;
|
|
17
|
+
init?: RequestInit;
|
|
18
|
+
}>;
|