vector-framework 1.2.1 → 1.2.3
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/README.md +18 -6
- package/dist/auth/protected.d.ts +4 -4
- package/dist/auth/protected.d.ts.map +1 -1
- package/dist/auth/protected.js +10 -7
- package/dist/auth/protected.js.map +1 -1
- package/dist/cache/manager.d.ts +2 -0
- package/dist/cache/manager.d.ts.map +1 -1
- package/dist/cache/manager.js +21 -4
- package/dist/cache/manager.js.map +1 -1
- package/dist/checkpoint/artifacts/compressor.d.ts +5 -0
- package/dist/checkpoint/artifacts/compressor.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/compressor.js +24 -0
- package/dist/checkpoint/artifacts/compressor.js.map +1 -0
- package/dist/checkpoint/artifacts/decompress-worker.d.ts +2 -0
- package/dist/checkpoint/artifacts/decompress-worker.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/decompress-worker.js +31 -0
- package/dist/checkpoint/artifacts/decompress-worker.js.map +1 -0
- package/dist/checkpoint/artifacts/hasher.d.ts +2 -0
- package/dist/checkpoint/artifacts/hasher.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/hasher.js +7 -0
- package/dist/checkpoint/artifacts/hasher.js.map +1 -0
- package/dist/checkpoint/artifacts/manifest.d.ts +6 -0
- package/dist/checkpoint/artifacts/manifest.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/manifest.js +55 -0
- package/dist/checkpoint/artifacts/manifest.js.map +1 -0
- package/dist/checkpoint/artifacts/materializer.d.ts +16 -0
- package/dist/checkpoint/artifacts/materializer.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/materializer.js +168 -0
- package/dist/checkpoint/artifacts/materializer.js.map +1 -0
- package/dist/checkpoint/artifacts/packager.d.ts +12 -0
- package/dist/checkpoint/artifacts/packager.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/packager.js +82 -0
- package/dist/checkpoint/artifacts/packager.js.map +1 -0
- package/dist/checkpoint/artifacts/repository.d.ts +11 -0
- package/dist/checkpoint/artifacts/repository.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/repository.js +29 -0
- package/dist/checkpoint/artifacts/repository.js.map +1 -0
- package/dist/checkpoint/artifacts/store.d.ts +13 -0
- package/dist/checkpoint/artifacts/store.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/store.js +85 -0
- package/dist/checkpoint/artifacts/store.js.map +1 -0
- package/dist/checkpoint/artifacts/types.d.ts +21 -0
- package/dist/checkpoint/artifacts/types.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/types.js +2 -0
- package/dist/checkpoint/artifacts/types.js.map +1 -0
- package/dist/checkpoint/artifacts/worker-decompressor.d.ts +17 -0
- package/dist/checkpoint/artifacts/worker-decompressor.d.ts.map +1 -0
- package/dist/checkpoint/artifacts/worker-decompressor.js +148 -0
- package/dist/checkpoint/artifacts/worker-decompressor.js.map +1 -0
- package/dist/checkpoint/asset-store.d.ts +10 -0
- package/dist/checkpoint/asset-store.d.ts.map +1 -0
- package/dist/checkpoint/asset-store.js +46 -0
- package/dist/checkpoint/asset-store.js.map +1 -0
- package/dist/checkpoint/bundler.d.ts +15 -0
- package/dist/checkpoint/bundler.d.ts.map +1 -0
- package/dist/checkpoint/bundler.js +45 -0
- package/dist/checkpoint/bundler.js.map +1 -0
- package/dist/checkpoint/cli.d.ts +2 -0
- package/dist/checkpoint/cli.d.ts.map +1 -0
- package/dist/checkpoint/cli.js +157 -0
- package/dist/checkpoint/cli.js.map +1 -0
- package/dist/checkpoint/entrypoint-generator.d.ts +17 -0
- package/dist/checkpoint/entrypoint-generator.d.ts.map +1 -0
- package/dist/checkpoint/entrypoint-generator.js +251 -0
- package/dist/checkpoint/entrypoint-generator.js.map +1 -0
- package/dist/checkpoint/forwarder.d.ts +6 -0
- package/dist/checkpoint/forwarder.d.ts.map +1 -0
- package/dist/checkpoint/forwarder.js +74 -0
- package/dist/checkpoint/forwarder.js.map +1 -0
- package/dist/checkpoint/gateway.d.ts +11 -0
- package/dist/checkpoint/gateway.d.ts.map +1 -0
- package/dist/checkpoint/gateway.js +30 -0
- package/dist/checkpoint/gateway.js.map +1 -0
- package/dist/checkpoint/ipc.d.ts +12 -0
- package/dist/checkpoint/ipc.d.ts.map +1 -0
- package/dist/checkpoint/ipc.js +96 -0
- package/dist/checkpoint/ipc.js.map +1 -0
- package/dist/checkpoint/manager.d.ts +20 -0
- package/dist/checkpoint/manager.d.ts.map +1 -0
- package/dist/checkpoint/manager.js +214 -0
- package/dist/checkpoint/manager.js.map +1 -0
- package/dist/checkpoint/process-manager.d.ts +35 -0
- package/dist/checkpoint/process-manager.d.ts.map +1 -0
- package/dist/checkpoint/process-manager.js +203 -0
- package/dist/checkpoint/process-manager.js.map +1 -0
- package/dist/checkpoint/resolver.d.ts +25 -0
- package/dist/checkpoint/resolver.d.ts.map +1 -0
- package/dist/checkpoint/resolver.js +95 -0
- package/dist/checkpoint/resolver.js.map +1 -0
- package/dist/checkpoint/socket-path.d.ts +2 -0
- package/dist/checkpoint/socket-path.d.ts.map +1 -0
- package/dist/checkpoint/socket-path.js +51 -0
- package/dist/checkpoint/socket-path.js.map +1 -0
- package/dist/checkpoint/types.d.ts +54 -0
- package/dist/checkpoint/types.d.ts.map +1 -0
- package/dist/checkpoint/types.js +2 -0
- package/dist/checkpoint/types.js.map +1 -0
- package/dist/cli/index.js +10 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/option-resolution.d.ts +1 -1
- package/dist/cli/option-resolution.d.ts.map +1 -1
- package/dist/cli/option-resolution.js.map +1 -1
- package/dist/cli.js +3817 -350
- package/dist/core/config-loader.d.ts +1 -0
- package/dist/core/config-loader.d.ts.map +1 -1
- package/dist/core/config-loader.js +10 -2
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/router.d.ts +24 -3
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +398 -249
- package/dist/core/router.js.map +1 -1
- package/dist/core/server.d.ts +3 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +35 -10
- package/dist/core/server.js.map +1 -1
- package/dist/core/vector.d.ts +3 -0
- package/dist/core/vector.d.ts.map +1 -1
- package/dist/core/vector.js +51 -1
- package/dist/core/vector.js.map +1 -1
- package/dist/dev/route-scanner.d.ts.map +1 -1
- package/dist/dev/route-scanner.js +2 -1
- package/dist/dev/route-scanner.js.map +1 -1
- package/dist/errors/index.cjs +2 -0
- package/dist/http.d.ts +32 -7
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +144 -13
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +2657 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1433
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1301 -77
- package/dist/middleware/manager.d.ts +3 -3
- package/dist/middleware/manager.d.ts.map +1 -1
- package/dist/middleware/manager.js +9 -8
- package/dist/middleware/manager.js.map +1 -1
- package/dist/openapi/docs-ui.d.ts.map +1 -1
- package/dist/openapi/docs-ui.js +1097 -61
- package/dist/openapi/docs-ui.js.map +1 -1
- package/dist/openapi/generator.d.ts +2 -1
- package/dist/openapi/generator.d.ts.map +1 -1
- package/dist/openapi/generator.js +332 -16
- package/dist/openapi/generator.js.map +1 -1
- package/dist/types/index.d.ts +71 -28
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +24 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +3 -2
- package/dist/utils/validation.js.map +1 -1
- package/package.json +9 -14
- package/src/auth/protected.ts +11 -8
- package/src/cache/manager.ts +23 -4
- package/src/checkpoint/artifacts/compressor.ts +30 -0
- package/src/checkpoint/artifacts/decompress-worker.ts +49 -0
- package/src/checkpoint/artifacts/hasher.ts +6 -0
- package/src/checkpoint/artifacts/manifest.ts +72 -0
- package/src/checkpoint/artifacts/materializer.ts +211 -0
- package/src/checkpoint/artifacts/packager.ts +100 -0
- package/src/checkpoint/artifacts/repository.ts +36 -0
- package/src/checkpoint/artifacts/store.ts +102 -0
- package/src/checkpoint/artifacts/types.ts +24 -0
- package/src/checkpoint/artifacts/worker-decompressor.ts +192 -0
- package/src/checkpoint/asset-store.ts +61 -0
- package/src/checkpoint/bundler.ts +64 -0
- package/src/checkpoint/cli.ts +177 -0
- package/src/checkpoint/entrypoint-generator.ts +275 -0
- package/src/checkpoint/forwarder.ts +84 -0
- package/src/checkpoint/gateway.ts +40 -0
- package/src/checkpoint/ipc.ts +107 -0
- package/src/checkpoint/manager.ts +254 -0
- package/src/checkpoint/process-manager.ts +250 -0
- package/src/checkpoint/resolver.ts +124 -0
- package/src/checkpoint/socket-path.ts +61 -0
- package/src/checkpoint/types.ts +63 -0
- package/src/cli/index.ts +11 -2
- package/src/cli/option-resolution.ts +5 -1
- package/src/core/config-loader.ts +11 -2
- package/src/core/router.ts +505 -264
- package/src/core/server.ts +51 -11
- package/src/core/vector.ts +60 -1
- package/src/dev/route-scanner.ts +2 -1
- package/src/http.ts +219 -19
- package/src/index.ts +3 -2
- package/src/middleware/manager.ts +10 -10
- package/src/openapi/docs-ui.ts +1097 -61
- package/src/openapi/generator.ts +380 -13
- package/src/types/index.ts +83 -30
- package/src/utils/validation.ts +5 -3
package/dist/core/router.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { APIError, createResponse } from '../http';
|
|
2
2
|
import { STATIC_RESPONSES } from '../constants';
|
|
3
|
+
import { AuthKind } from '../types';
|
|
3
4
|
import { buildRouteRegex } from '../utils/path';
|
|
4
5
|
import { createValidationErrorPayload, extractThrownIssues, isStandardRouteSchema, normalizeValidationIssues, runStandardValidation, } from '../utils/schema-validation';
|
|
6
|
+
const AUTH_KIND_VALUES = new Set(Object.values(AuthKind));
|
|
7
|
+
function isAuthKindValue(value) {
|
|
8
|
+
return typeof value === 'string' && AUTH_KIND_VALUES.has(value);
|
|
9
|
+
}
|
|
5
10
|
export class VectorRouter {
|
|
6
11
|
middlewareManager;
|
|
7
12
|
authManager;
|
|
@@ -13,6 +18,7 @@ export class VectorRouter {
|
|
|
13
18
|
routeMatchers = [];
|
|
14
19
|
corsHeadersEntries = null;
|
|
15
20
|
corsHandler = null;
|
|
21
|
+
checkpointGateway = null;
|
|
16
22
|
constructor(middlewareManager, authManager, cacheManager) {
|
|
17
23
|
this.middlewareManager = middlewareManager;
|
|
18
24
|
this.authManager = authManager;
|
|
@@ -24,6 +30,9 @@ export class VectorRouter {
|
|
|
24
30
|
setCorsHandler(handler) {
|
|
25
31
|
this.corsHandler = handler;
|
|
26
32
|
}
|
|
33
|
+
setCheckpointGateway(gateway) {
|
|
34
|
+
this.checkpointGateway = gateway;
|
|
35
|
+
}
|
|
27
36
|
setRouteBooleanDefaults(defaults) {
|
|
28
37
|
this.routeBooleanDefaults = { ...defaults };
|
|
29
38
|
}
|
|
@@ -39,6 +48,11 @@ export class VectorRouter {
|
|
|
39
48
|
resolved[key] = defaults[key];
|
|
40
49
|
}
|
|
41
50
|
}
|
|
51
|
+
// If a route explicitly sets auth:true and the global default auth is an AuthKind,
|
|
52
|
+
// promote the route to that kind so OpenAPI docs and runtime defaults stay aligned.
|
|
53
|
+
if (resolved.auth === true && isAuthKindValue(defaults.auth)) {
|
|
54
|
+
resolved.auth = defaults.auth;
|
|
55
|
+
}
|
|
42
56
|
return resolved;
|
|
43
57
|
}
|
|
44
58
|
route(options, handler) {
|
|
@@ -119,243 +133,301 @@ export class VectorRouter {
|
|
|
119
133
|
catch {
|
|
120
134
|
return APIError.badRequest('Malformed request URL');
|
|
121
135
|
}
|
|
122
|
-
request._parsedUrl = url;
|
|
123
136
|
const pathname = url.pathname;
|
|
137
|
+
// Fast path: exact route lookup avoids scanning regex matchers for common static/method routes.
|
|
138
|
+
const exactPathRoute = this.routeTable[pathname];
|
|
139
|
+
if (exactPathRoute) {
|
|
140
|
+
if (exactPathRoute instanceof Response) {
|
|
141
|
+
// Route table stores a shared Response instance for static routes; clone per request.
|
|
142
|
+
return this.applyCorsResponse(exactPathRoute.clone(), request);
|
|
143
|
+
}
|
|
144
|
+
const exactPathMethodMap = exactPathRoute;
|
|
145
|
+
const handler = exactPathMethodMap[request.method] ?? (request.method === 'HEAD' ? exactPathMethodMap['GET'] : undefined);
|
|
146
|
+
if (handler) {
|
|
147
|
+
const response = await handler(request);
|
|
148
|
+
if (response) {
|
|
149
|
+
return response;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
124
153
|
for (const matcher of this.routeMatchers) {
|
|
125
154
|
const path = matcher.path;
|
|
126
|
-
const
|
|
127
|
-
if (!
|
|
155
|
+
const routeEntry = this.routeTable[path];
|
|
156
|
+
if (!routeEntry)
|
|
128
157
|
continue;
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const match = pathname.match(matcher.regex);
|
|
134
|
-
if (match) {
|
|
135
|
-
try {
|
|
136
|
-
request.params = match.groups ?? {};
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
// Request.params can be readonly on Bun-native requests.
|
|
140
|
-
}
|
|
141
|
-
const handler = methodMap[request.method] ?? methodMap['GET'];
|
|
142
|
-
if (handler) {
|
|
143
|
-
const response = await handler(request);
|
|
144
|
-
if (response)
|
|
145
|
-
return response;
|
|
146
|
-
}
|
|
158
|
+
if (routeEntry instanceof Response) {
|
|
159
|
+
if (pathname === path) {
|
|
160
|
+
// Same reason as exact-path static route handling above.
|
|
161
|
+
return this.applyCorsResponse(routeEntry.clone(), request);
|
|
147
162
|
}
|
|
163
|
+
continue;
|
|
148
164
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (!request.context) {
|
|
154
|
-
request.context = {};
|
|
155
|
-
}
|
|
156
|
-
const hasEmptyParamsObject = !!request.params &&
|
|
157
|
-
typeof request.params === 'object' &&
|
|
158
|
-
!Array.isArray(request.params) &&
|
|
159
|
-
Object.keys(request.params).length === 0;
|
|
160
|
-
if (options?.params !== undefined && (request.params === undefined || hasEmptyParamsObject)) {
|
|
161
|
-
try {
|
|
162
|
-
request.params = options.params;
|
|
165
|
+
const methodMap = routeEntry;
|
|
166
|
+
const handler = methodMap[request.method] ?? (request.method === 'HEAD' ? methodMap['GET'] : undefined);
|
|
167
|
+
if (!handler) {
|
|
168
|
+
continue;
|
|
163
169
|
}
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
const match = pathname.match(matcher.regex);
|
|
171
|
+
if (!match) {
|
|
172
|
+
continue;
|
|
166
173
|
}
|
|
174
|
+
const response = await handler(request);
|
|
175
|
+
if (response)
|
|
176
|
+
return response;
|
|
167
177
|
}
|
|
168
|
-
|
|
169
|
-
|
|
178
|
+
// STATIC_RESPONSES are shared singletons; clone before per-request header mutation.
|
|
179
|
+
return this.applyCorsResponse(STATIC_RESPONSES.NOT_FOUND.clone(), request);
|
|
180
|
+
}
|
|
181
|
+
cloneMetadata(value) {
|
|
182
|
+
if (Array.isArray(value)) {
|
|
183
|
+
return [...value];
|
|
170
184
|
}
|
|
171
|
-
if (
|
|
172
|
-
|
|
185
|
+
if (value && typeof value === 'object') {
|
|
186
|
+
return { ...value };
|
|
173
187
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
});
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
createContext(request, options) {
|
|
191
|
+
const context = {
|
|
192
|
+
request,
|
|
193
|
+
};
|
|
194
|
+
this.setContextField(context, 'metadata', options?.metadata !== undefined ? this.cloneMetadata(options.metadata) : {});
|
|
195
|
+
this.setContextField(context, 'params', options?.params ?? {});
|
|
196
|
+
this.setContextField(context, 'query', options?.query ?? {});
|
|
197
|
+
this.setContextField(context, 'cookies', options?.cookies ?? {});
|
|
198
|
+
return context;
|
|
199
|
+
}
|
|
200
|
+
setContextField(context, key, value) {
|
|
201
|
+
context[key] = value;
|
|
202
|
+
}
|
|
203
|
+
hasOwnContextField(context, key) {
|
|
204
|
+
return Object.prototype.hasOwnProperty.call(context, key);
|
|
205
|
+
}
|
|
206
|
+
buildCheckpointContextPayload(context) {
|
|
207
|
+
const payload = {};
|
|
208
|
+
const allowedKeys = ['metadata', 'content', 'validatedInput', 'authUser'];
|
|
209
|
+
for (const key of allowedKeys) {
|
|
210
|
+
if (!this.hasOwnContextField(context, key)) {
|
|
211
|
+
continue;
|
|
199
212
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
request.query = VectorRouter.parseQuery(url);
|
|
204
|
-
}
|
|
205
|
-
catch {
|
|
206
|
-
// Leave query as-is when request shape is non-extensible.
|
|
207
|
-
}
|
|
213
|
+
const value = context[key];
|
|
214
|
+
if (typeof value === 'function' || typeof value === 'symbol' || value === undefined) {
|
|
215
|
+
continue;
|
|
208
216
|
}
|
|
217
|
+
payload[key] = value;
|
|
209
218
|
}
|
|
210
|
-
|
|
211
|
-
Object.defineProperty(request, 'cookies', {
|
|
212
|
-
get() {
|
|
213
|
-
const cookieHeader = this.headers.get('cookie') ?? '';
|
|
214
|
-
const cookies = {};
|
|
215
|
-
if (cookieHeader) {
|
|
216
|
-
for (const pair of cookieHeader.split(';')) {
|
|
217
|
-
const idx = pair.indexOf('=');
|
|
218
|
-
if (idx > 0) {
|
|
219
|
-
cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
Object.defineProperty(this, 'cookies', {
|
|
224
|
-
value: cookies,
|
|
225
|
-
writable: true,
|
|
226
|
-
configurable: true,
|
|
227
|
-
enumerable: true,
|
|
228
|
-
});
|
|
229
|
-
return cookies;
|
|
230
|
-
},
|
|
231
|
-
configurable: true,
|
|
232
|
-
enumerable: true,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
219
|
+
return payload;
|
|
235
220
|
}
|
|
236
|
-
resolveFallbackParams(
|
|
221
|
+
resolveFallbackParams(pathname, routeMatcher) {
|
|
237
222
|
if (!routeMatcher) {
|
|
238
223
|
return undefined;
|
|
239
224
|
}
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
242
|
-
typeof currentParams === 'object' &&
|
|
243
|
-
!Array.isArray(currentParams) &&
|
|
244
|
-
Object.keys(currentParams).length > 0) {
|
|
225
|
+
const matched = pathname.match(routeMatcher);
|
|
226
|
+
if (!matched?.groups) {
|
|
245
227
|
return undefined;
|
|
246
228
|
}
|
|
247
|
-
|
|
229
|
+
return matched.groups;
|
|
230
|
+
}
|
|
231
|
+
getRequestedCheckpointVersion(request) {
|
|
232
|
+
if (!this.checkpointGateway) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const gateway = this.checkpointGateway;
|
|
236
|
+
if (gateway?.getRequestedVersion) {
|
|
237
|
+
return gateway.getRequestedVersion(request);
|
|
238
|
+
}
|
|
239
|
+
const primary = request.headers.get('x-vector-checkpoint-version');
|
|
240
|
+
if (primary && primary.trim().length > 0) {
|
|
241
|
+
return primary.trim();
|
|
242
|
+
}
|
|
243
|
+
const fallback = request.headers.get('x-vector-checkpoint');
|
|
244
|
+
if (fallback && fallback.trim().length > 0) {
|
|
245
|
+
return fallback.trim();
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
getCheckpointCacheKeyOverrideValue(request) {
|
|
250
|
+
if (!this.checkpointGateway) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const gateway = this.checkpointGateway;
|
|
254
|
+
if (gateway?.getCacheKeyOverrideValue) {
|
|
255
|
+
return gateway.getCacheKeyOverrideValue(request);
|
|
256
|
+
}
|
|
257
|
+
const primary = request.headers.get('x-vector-checkpoint-version');
|
|
258
|
+
if (primary && primary.trim().length > 0) {
|
|
259
|
+
return `x-vector-checkpoint-version:${primary.trim()}`;
|
|
260
|
+
}
|
|
261
|
+
const fallback = request.headers.get('x-vector-checkpoint');
|
|
262
|
+
if (fallback && fallback.trim().length > 0) {
|
|
263
|
+
return `x-vector-checkpoint:${fallback.trim()}`;
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
applyCheckpointCacheNamespace(cacheKey, request) {
|
|
268
|
+
const checkpointVersion = this.getRequestedCheckpointVersion(request);
|
|
269
|
+
if (!checkpointVersion) {
|
|
270
|
+
return cacheKey;
|
|
271
|
+
}
|
|
272
|
+
return `${cacheKey}:checkpoint=${checkpointVersion}`;
|
|
273
|
+
}
|
|
274
|
+
applyCheckpointRouteKeyOverride(cacheKey, request) {
|
|
275
|
+
const override = this.getCheckpointCacheKeyOverrideValue(request);
|
|
276
|
+
if (!override) {
|
|
277
|
+
return cacheKey;
|
|
278
|
+
}
|
|
279
|
+
return override;
|
|
280
|
+
}
|
|
281
|
+
async parseRequestBodyForContext(context, request, checkpointRequested) {
|
|
282
|
+
let parsedContent = null;
|
|
248
283
|
try {
|
|
249
|
-
|
|
284
|
+
// For checkpoint requests we may forward the original stream later, so parse from a clone.
|
|
285
|
+
const bodyReadRequest = checkpointRequested ? request.clone() : request;
|
|
286
|
+
const contentType = bodyReadRequest.headers.get('content-type');
|
|
287
|
+
if (contentType?.startsWith('application/json')) {
|
|
288
|
+
parsedContent = await bodyReadRequest.json();
|
|
289
|
+
}
|
|
290
|
+
else if (contentType?.startsWith('application/x-www-form-urlencoded')) {
|
|
291
|
+
parsedContent = Object.fromEntries(await bodyReadRequest.formData());
|
|
292
|
+
}
|
|
293
|
+
else if (contentType?.startsWith('multipart/form-data')) {
|
|
294
|
+
parsedContent = await bodyReadRequest.formData();
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
parsedContent = await bodyReadRequest.text();
|
|
298
|
+
}
|
|
250
299
|
}
|
|
251
300
|
catch {
|
|
252
|
-
|
|
301
|
+
parsedContent = null;
|
|
253
302
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
303
|
+
this.setContextField(context, 'content', parsedContent);
|
|
304
|
+
}
|
|
305
|
+
isLikelyStreamingBodyRequest(request) {
|
|
306
|
+
if (request.method === 'GET' || request.method === 'HEAD') {
|
|
307
|
+
return false;
|
|
257
308
|
}
|
|
258
|
-
|
|
309
|
+
if (!request.body) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
if (request.duplex === 'half') {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
const transferEncoding = request.headers.get('transfer-encoding');
|
|
316
|
+
if (transferEncoding) {
|
|
317
|
+
const hasChunked = transferEncoding.split(',').some((value) => value.trim().toLowerCase() === 'chunked');
|
|
318
|
+
if (hasChunked) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
259
323
|
}
|
|
260
324
|
wrapHandler(options, handler) {
|
|
261
325
|
const routePath = options.path;
|
|
262
326
|
const routeMatcher = routePath.includes(':') ? buildRouteRegex(routePath) : null;
|
|
263
327
|
return async (request) => {
|
|
264
328
|
const vectorRequest = request;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
329
|
+
let pathname = '';
|
|
330
|
+
try {
|
|
331
|
+
pathname = new URL(request.url).pathname;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// Ignore malformed URLs here; router.handle() already guards route matching.
|
|
335
|
+
}
|
|
336
|
+
const fallbackParams = this.resolveFallbackParams(pathname, routeMatcher);
|
|
337
|
+
const context = this.createContext(vectorRequest, {
|
|
269
338
|
metadata: options.metadata,
|
|
339
|
+
params: this.getRequestParams(request, fallbackParams),
|
|
340
|
+
query: this.getRequestQuery(request),
|
|
341
|
+
cookies: this.getRequestCookies(request),
|
|
270
342
|
});
|
|
271
343
|
try {
|
|
272
344
|
if (options.expose === false) {
|
|
273
345
|
return APIError.forbidden('Forbidden');
|
|
274
346
|
}
|
|
275
|
-
const
|
|
276
|
-
if (
|
|
277
|
-
return
|
|
347
|
+
const beforeResponse = await this.middlewareManager.executeBefore(context);
|
|
348
|
+
if (beforeResponse instanceof Response) {
|
|
349
|
+
return beforeResponse;
|
|
278
350
|
}
|
|
279
|
-
const req = beforeResult;
|
|
280
351
|
if (options.auth) {
|
|
281
352
|
try {
|
|
282
|
-
await this.authManager.authenticate(
|
|
353
|
+
await this.authManager.authenticate(context);
|
|
283
354
|
}
|
|
284
355
|
catch (error) {
|
|
285
356
|
return APIError.unauthorized(error instanceof Error ? error.message : 'Authentication failed', options.responseContentType);
|
|
286
357
|
}
|
|
287
358
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
359
|
+
const executeRoute = async () => {
|
|
360
|
+
const req = context.request;
|
|
361
|
+
const requestForRoute = req;
|
|
362
|
+
const checkpointRequested = this.getRequestedCheckpointVersion(requestForRoute) !== null;
|
|
363
|
+
// Library-wide behavior: applies to any streaming request with input schema validation enabled,
|
|
364
|
+
// regardless of whether checkpoint routing is in play.
|
|
365
|
+
const shouldDeferStreamingValidation = this.isLikelyStreamingBodyRequest(requestForRoute) &&
|
|
366
|
+
options.schema?.input !== undefined &&
|
|
367
|
+
options.validate !== false;
|
|
368
|
+
if (!options.rawRequest && req.method !== 'GET' && req.method !== 'HEAD' && !shouldDeferStreamingValidation) {
|
|
369
|
+
await this.parseRequestBodyForContext(context, requestForRoute, checkpointRequested);
|
|
370
|
+
}
|
|
371
|
+
if (shouldDeferStreamingValidation) {
|
|
372
|
+
const validationWithoutBody = await this.validateInputSchema(context, options, fallbackParams, {
|
|
373
|
+
includeBody: false,
|
|
374
|
+
allowBodyDeferral: true,
|
|
375
|
+
});
|
|
376
|
+
if (validationWithoutBody.response) {
|
|
377
|
+
return validationWithoutBody.response;
|
|
297
378
|
}
|
|
298
|
-
|
|
299
|
-
|
|
379
|
+
if (validationWithoutBody.requiresBody) {
|
|
380
|
+
if (!options.rawRequest && req.method !== 'GET' && req.method !== 'HEAD') {
|
|
381
|
+
await this.parseRequestBodyForContext(context, requestForRoute, checkpointRequested);
|
|
382
|
+
}
|
|
383
|
+
const fullValidation = await this.validateInputSchema(context, options, fallbackParams);
|
|
384
|
+
if (fullValidation.response) {
|
|
385
|
+
return fullValidation.response;
|
|
386
|
+
}
|
|
300
387
|
}
|
|
301
|
-
|
|
302
|
-
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
const inputValidation = await this.validateInputSchema(context, options, fallbackParams);
|
|
391
|
+
if (inputValidation.response) {
|
|
392
|
+
return inputValidation.response;
|
|
303
393
|
}
|
|
304
394
|
}
|
|
305
|
-
|
|
306
|
-
|
|
395
|
+
if (this.checkpointGateway) {
|
|
396
|
+
const checkpointResponse = await this.checkpointGateway.handle(req, this.buildCheckpointContextPayload(context));
|
|
397
|
+
if (checkpointResponse) {
|
|
398
|
+
return checkpointResponse;
|
|
399
|
+
}
|
|
307
400
|
}
|
|
308
|
-
|
|
309
|
-
}
|
|
310
|
-
const inputValidationResponse = await this.validateInputSchema(req, options);
|
|
311
|
-
if (inputValidationResponse) {
|
|
312
|
-
return inputValidationResponse;
|
|
313
|
-
}
|
|
401
|
+
return await handler(context);
|
|
402
|
+
};
|
|
314
403
|
let result;
|
|
315
404
|
const cacheOptions = options.cache;
|
|
316
405
|
if (cacheOptions && typeof cacheOptions === 'number' && cacheOptions > 0) {
|
|
317
|
-
const cacheKey = this.cacheManager.generateKey(
|
|
318
|
-
authUser:
|
|
319
|
-
});
|
|
320
|
-
result = await this.cacheManager.get(cacheKey, async () =>
|
|
321
|
-
const res = await handler(req);
|
|
322
|
-
if (res instanceof Response) {
|
|
323
|
-
return {
|
|
324
|
-
_isResponse: true,
|
|
325
|
-
body: await res.text(),
|
|
326
|
-
status: res.status,
|
|
327
|
-
headers: Object.fromEntries(res.headers.entries()),
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
return res;
|
|
331
|
-
}, cacheOptions);
|
|
406
|
+
const cacheKey = this.applyCheckpointCacheNamespace(this.cacheManager.generateKey(context.request, {
|
|
407
|
+
authUser: context.authUser,
|
|
408
|
+
}), context.request);
|
|
409
|
+
result = await this.cacheManager.get(cacheKey, async () => await executeRoute(), cacheOptions);
|
|
332
410
|
}
|
|
333
411
|
else if (cacheOptions && typeof cacheOptions === 'object' && cacheOptions.ttl) {
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
412
|
+
const hasRouteCacheKey = typeof cacheOptions.key === 'string' && cacheOptions.key.length > 0;
|
|
413
|
+
let cacheKey;
|
|
414
|
+
if (hasRouteCacheKey) {
|
|
415
|
+
cacheKey = this.applyCheckpointRouteKeyOverride(cacheOptions.key, context.request);
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
const generatedKey = this.cacheManager.generateKey(context.request, {
|
|
419
|
+
authUser: context.authUser,
|
|
337
420
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
return {
|
|
342
|
-
_isResponse: true,
|
|
343
|
-
body: await res.text(),
|
|
344
|
-
status: res.status,
|
|
345
|
-
headers: Object.fromEntries(res.headers.entries()),
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
return res;
|
|
349
|
-
}, cacheOptions.ttl);
|
|
421
|
+
cacheKey = this.applyCheckpointCacheNamespace(generatedKey, context.request);
|
|
422
|
+
}
|
|
423
|
+
result = await this.cacheManager.get(cacheKey, async () => await executeRoute(), cacheOptions.ttl);
|
|
350
424
|
}
|
|
351
425
|
else {
|
|
352
|
-
result = await
|
|
426
|
+
result = await executeRoute();
|
|
353
427
|
}
|
|
354
|
-
if (result
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
headers: result.headers,
|
|
358
|
-
});
|
|
428
|
+
if (result instanceof Response && !!cacheOptions) {
|
|
429
|
+
// Cache layers can return shared Response instances; clone before per-request mutations.
|
|
430
|
+
result = result.clone();
|
|
359
431
|
}
|
|
360
432
|
let response;
|
|
361
433
|
if (options.rawResponse || result instanceof Response) {
|
|
@@ -364,21 +436,8 @@ export class VectorRouter {
|
|
|
364
436
|
else {
|
|
365
437
|
response = createResponse(200, result, options.responseContentType);
|
|
366
438
|
}
|
|
367
|
-
response = await this.middlewareManager.executeFinally(response,
|
|
368
|
-
|
|
369
|
-
const entries = this.corsHeadersEntries;
|
|
370
|
-
if (entries) {
|
|
371
|
-
for (const [k, v] of entries) {
|
|
372
|
-
response.headers.set(k, v);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
else {
|
|
376
|
-
const dynamicCors = this.corsHandler;
|
|
377
|
-
if (dynamicCors) {
|
|
378
|
-
response = dynamicCors(response, req);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
return response;
|
|
439
|
+
response = await this.middlewareManager.executeFinally(response, context);
|
|
440
|
+
return this.applyCorsResponse(response, context.request);
|
|
382
441
|
}
|
|
383
442
|
catch (error) {
|
|
384
443
|
if (error instanceof Response) {
|
|
@@ -396,10 +455,13 @@ export class VectorRouter {
|
|
|
396
455
|
const nodeEnv = typeof Bun !== 'undefined' ? Bun.env.NODE_ENV : process.env.NODE_ENV;
|
|
397
456
|
return nodeEnv !== 'production';
|
|
398
457
|
}
|
|
399
|
-
async buildInputValidationPayload(
|
|
400
|
-
|
|
401
|
-
|
|
458
|
+
async buildInputValidationPayload(context, options, fallbackParams, validationOptions) {
|
|
459
|
+
const request = context.request;
|
|
460
|
+
const includeBody = validationOptions?.includeBody !== false;
|
|
461
|
+
let body = includeBody && this.hasOwnContextField(context, 'content') ? context.content : undefined;
|
|
462
|
+
if (includeBody && options.rawRequest && request.method !== 'GET' && request.method !== 'HEAD') {
|
|
402
463
|
try {
|
|
464
|
+
// Read raw body from a clone so handlers/checkpoint forwarding can still consume the original stream.
|
|
403
465
|
body = await request.clone().text();
|
|
404
466
|
}
|
|
405
467
|
catch {
|
|
@@ -407,94 +469,154 @@ export class VectorRouter {
|
|
|
407
469
|
}
|
|
408
470
|
}
|
|
409
471
|
return {
|
|
410
|
-
params: request
|
|
411
|
-
query: request
|
|
472
|
+
params: this.getRequestParams(request, fallbackParams),
|
|
473
|
+
query: this.getRequestQuery(request),
|
|
412
474
|
headers: Object.fromEntries(request.headers.entries()),
|
|
413
|
-
cookies: request
|
|
475
|
+
cookies: this.getRequestCookies(request),
|
|
414
476
|
body,
|
|
415
477
|
};
|
|
416
478
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
if (
|
|
420
|
-
return;
|
|
479
|
+
getRequestParams(request, fallbackParams) {
|
|
480
|
+
const nativeParams = this.readRequestObjectField(request, 'params');
|
|
481
|
+
if (nativeParams && Object.keys(nativeParams).length > 0) {
|
|
482
|
+
return nativeParams;
|
|
421
483
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
// Request.params can be readonly on Bun-native requests.
|
|
429
|
-
}
|
|
484
|
+
return fallbackParams ?? {};
|
|
485
|
+
}
|
|
486
|
+
getRequestQuery(request) {
|
|
487
|
+
const nativeQuery = this.readRequestObjectField(request, 'query');
|
|
488
|
+
if (nativeQuery) {
|
|
489
|
+
return nativeQuery;
|
|
430
490
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
request.query = validated.query;
|
|
434
|
-
}
|
|
435
|
-
catch {
|
|
436
|
-
// Request.query can be readonly/non-configurable on some request objects.
|
|
437
|
-
}
|
|
491
|
+
try {
|
|
492
|
+
return VectorRouter.parseQuery(new URL(request.url));
|
|
438
493
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
request.cookies = validated.cookies;
|
|
442
|
-
}
|
|
443
|
-
catch {
|
|
444
|
-
// Request.cookies can be readonly/non-configurable on some request objects.
|
|
445
|
-
}
|
|
494
|
+
catch {
|
|
495
|
+
return {};
|
|
446
496
|
}
|
|
447
|
-
|
|
448
|
-
|
|
497
|
+
}
|
|
498
|
+
getRequestCookies(request) {
|
|
499
|
+
const nativeCookies = this.readRequestObjectField(request, 'cookies');
|
|
500
|
+
if (nativeCookies) {
|
|
501
|
+
return nativeCookies;
|
|
449
502
|
}
|
|
503
|
+
return VectorRouter.parseCookies(request.headers.get('cookie'));
|
|
450
504
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
505
|
+
readRequestObjectField(request, key) {
|
|
506
|
+
const value = request[key];
|
|
507
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
508
|
+
return undefined;
|
|
454
509
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
510
|
+
return value;
|
|
511
|
+
}
|
|
512
|
+
applyValidatedInput(context, validatedValue) {
|
|
513
|
+
this.setContextField(context, 'validatedInput', validatedValue);
|
|
514
|
+
}
|
|
515
|
+
issueHasBodyPath(issue) {
|
|
516
|
+
if (!issue || typeof issue !== 'object' || !('path' in issue)) {
|
|
517
|
+
return false;
|
|
458
518
|
}
|
|
459
|
-
|
|
519
|
+
const path = issue.path;
|
|
520
|
+
if (!Array.isArray(path) || path.length === 0) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
const segment = path[0];
|
|
524
|
+
if (segment && typeof segment === 'object' && 'key' in segment) {
|
|
525
|
+
return segment.key === 'body';
|
|
526
|
+
}
|
|
527
|
+
return segment === 'body';
|
|
460
528
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
529
|
+
issueHasExplicitNonBodyPath(issue) {
|
|
530
|
+
if (!issue || typeof issue !== 'object' || !('path' in issue)) {
|
|
531
|
+
return false;
|
|
464
532
|
}
|
|
465
|
-
|
|
466
|
-
|
|
533
|
+
const path = issue.path;
|
|
534
|
+
if (!Array.isArray(path) || path.length === 0) {
|
|
535
|
+
return false;
|
|
467
536
|
}
|
|
537
|
+
const segment = path[0];
|
|
538
|
+
if (segment && typeof segment === 'object' && 'key' in segment) {
|
|
539
|
+
return segment.key !== 'body';
|
|
540
|
+
}
|
|
541
|
+
return segment !== 'body';
|
|
542
|
+
}
|
|
543
|
+
issueHasUnknownPath(issue) {
|
|
544
|
+
if (!issue || typeof issue !== 'object' || !('path' in issue)) {
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
const path = issue.path;
|
|
548
|
+
if (!Array.isArray(path)) {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
return path.length === 0;
|
|
552
|
+
}
|
|
553
|
+
shouldDeferBodyValidation(issues, context, validationOptions) {
|
|
554
|
+
if (!(validationOptions?.allowBodyDeferral === true && validationOptions?.includeBody === false)) {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
const request = context.request;
|
|
558
|
+
const mayHaveRequestBody = request.method !== 'GET' && request.method !== 'HEAD' && request.body !== null;
|
|
559
|
+
if (!mayHaveRequestBody || issues.length === 0) {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
if (issues.some((issue) => this.issueHasBodyPath(issue))) {
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
// Conservative fallback: if issues do not identify a non-body target and at least one issue
|
|
566
|
+
// has unknown/empty path, retry once with body included.
|
|
567
|
+
const hasExplicitNonBodyPath = issues.some((issue) => this.issueHasExplicitNonBodyPath(issue));
|
|
568
|
+
const hasUnknownPath = issues.some((issue) => this.issueHasUnknownPath(issue));
|
|
569
|
+
return !hasExplicitNonBodyPath && hasUnknownPath;
|
|
468
570
|
}
|
|
469
|
-
async validateInputSchema(
|
|
571
|
+
async validateInputSchema(context, options, fallbackParams, validationOptions) {
|
|
470
572
|
const inputSchema = options.schema?.input;
|
|
471
573
|
if (!inputSchema) {
|
|
472
|
-
return null;
|
|
574
|
+
return { response: null, requiresBody: false };
|
|
473
575
|
}
|
|
474
576
|
if (options.validate === false) {
|
|
475
|
-
return null;
|
|
577
|
+
return { response: null, requiresBody: false };
|
|
476
578
|
}
|
|
477
579
|
if (!isStandardRouteSchema(inputSchema)) {
|
|
478
|
-
return
|
|
580
|
+
return {
|
|
581
|
+
response: APIError.internalServerError('Invalid route schema configuration', options.responseContentType),
|
|
582
|
+
requiresBody: false,
|
|
583
|
+
};
|
|
479
584
|
}
|
|
480
585
|
const includeRawIssues = this.isDevelopmentMode();
|
|
481
|
-
const payload = await this.buildInputValidationPayload(
|
|
586
|
+
const payload = await this.buildInputValidationPayload(context, options, fallbackParams, {
|
|
587
|
+
includeBody: validationOptions?.includeBody,
|
|
588
|
+
});
|
|
482
589
|
try {
|
|
483
590
|
const validation = await runStandardValidation(inputSchema, payload);
|
|
484
591
|
if (validation.success === false) {
|
|
592
|
+
if (this.shouldDeferBodyValidation(validation.issues, context, validationOptions)) {
|
|
593
|
+
return { response: null, requiresBody: true };
|
|
594
|
+
}
|
|
485
595
|
const issues = normalizeValidationIssues(validation.issues, includeRawIssues);
|
|
486
|
-
return
|
|
596
|
+
return {
|
|
597
|
+
response: createResponse(422, createValidationErrorPayload('input', issues), options.responseContentType),
|
|
598
|
+
requiresBody: false,
|
|
599
|
+
};
|
|
487
600
|
}
|
|
488
|
-
this.applyValidatedInput(
|
|
489
|
-
return null;
|
|
601
|
+
this.applyValidatedInput(context, validation.value);
|
|
602
|
+
return { response: null, requiresBody: false };
|
|
490
603
|
}
|
|
491
604
|
catch (error) {
|
|
492
605
|
const thrownIssues = extractThrownIssues(error);
|
|
493
606
|
if (thrownIssues) {
|
|
607
|
+
if (this.shouldDeferBodyValidation(thrownIssues, context, validationOptions)) {
|
|
608
|
+
return { response: null, requiresBody: true };
|
|
609
|
+
}
|
|
494
610
|
const issues = normalizeValidationIssues(thrownIssues, includeRawIssues);
|
|
495
|
-
return
|
|
611
|
+
return {
|
|
612
|
+
response: createResponse(422, createValidationErrorPayload('input', issues), options.responseContentType),
|
|
613
|
+
requiresBody: false,
|
|
614
|
+
};
|
|
496
615
|
}
|
|
497
|
-
return
|
|
616
|
+
return {
|
|
617
|
+
response: APIError.internalServerError(error instanceof Error ? error.message : 'Validation failed', options.responseContentType),
|
|
618
|
+
requiresBody: false,
|
|
619
|
+
};
|
|
498
620
|
}
|
|
499
621
|
}
|
|
500
622
|
getOrCreateMethodMap(path) {
|
|
@@ -547,6 +669,19 @@ export class VectorRouter {
|
|
|
547
669
|
}
|
|
548
670
|
return query;
|
|
549
671
|
}
|
|
672
|
+
static parseCookies(cookieHeader) {
|
|
673
|
+
const cookies = {};
|
|
674
|
+
if (!cookieHeader) {
|
|
675
|
+
return cookies;
|
|
676
|
+
}
|
|
677
|
+
for (const pair of cookieHeader.split(';')) {
|
|
678
|
+
const idx = pair.indexOf('=');
|
|
679
|
+
if (idx > 0) {
|
|
680
|
+
cookies[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return cookies;
|
|
684
|
+
}
|
|
550
685
|
routeSpecificityScore(path) {
|
|
551
686
|
const STATIC_SEGMENT_WEIGHT = 1000;
|
|
552
687
|
const PARAM_SEGMENT_WEIGHT = 10;
|
|
@@ -571,5 +706,19 @@ export class VectorRouter {
|
|
|
571
706
|
}
|
|
572
707
|
return score;
|
|
573
708
|
}
|
|
709
|
+
applyCorsResponse(response, request) {
|
|
710
|
+
const entries = this.corsHeadersEntries;
|
|
711
|
+
if (entries) {
|
|
712
|
+
for (const [k, v] of entries) {
|
|
713
|
+
response.headers.set(k, v);
|
|
714
|
+
}
|
|
715
|
+
return response;
|
|
716
|
+
}
|
|
717
|
+
const dynamicCors = this.corsHandler;
|
|
718
|
+
if (dynamicCors) {
|
|
719
|
+
return dynamicCors(response, request);
|
|
720
|
+
}
|
|
721
|
+
return response;
|
|
722
|
+
}
|
|
574
723
|
}
|
|
575
724
|
//# sourceMappingURL=router.js.map
|