vector-framework 1.1.1 → 1.2.0

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 (98) hide show
  1. package/README.md +87 -635
  2. package/dist/auth/protected.d.ts.map +1 -1
  3. package/dist/auth/protected.js.map +1 -1
  4. package/dist/cache/manager.d.ts.map +1 -1
  5. package/dist/cache/manager.js +2 -7
  6. package/dist/cache/manager.js.map +1 -1
  7. package/dist/cli/index.js +17 -62
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/cli/option-resolution.d.ts +4 -0
  10. package/dist/cli/option-resolution.d.ts.map +1 -0
  11. package/dist/cli/option-resolution.js +28 -0
  12. package/dist/cli/option-resolution.js.map +1 -0
  13. package/dist/cli.js +2721 -617
  14. package/dist/constants/index.d.ts +3 -0
  15. package/dist/constants/index.d.ts.map +1 -1
  16. package/dist/constants/index.js +6 -0
  17. package/dist/constants/index.js.map +1 -1
  18. package/dist/core/config-loader.d.ts.map +1 -1
  19. package/dist/core/config-loader.js +2 -0
  20. package/dist/core/config-loader.js.map +1 -1
  21. package/dist/core/router.d.ts +41 -17
  22. package/dist/core/router.d.ts.map +1 -1
  23. package/dist/core/router.js +432 -153
  24. package/dist/core/router.js.map +1 -1
  25. package/dist/core/server.d.ts +14 -1
  26. package/dist/core/server.d.ts.map +1 -1
  27. package/dist/core/server.js +250 -30
  28. package/dist/core/server.js.map +1 -1
  29. package/dist/core/vector.d.ts +4 -3
  30. package/dist/core/vector.d.ts.map +1 -1
  31. package/dist/core/vector.js +21 -12
  32. package/dist/core/vector.js.map +1 -1
  33. package/dist/dev/route-generator.d.ts.map +1 -1
  34. package/dist/dev/route-generator.js.map +1 -1
  35. package/dist/dev/route-scanner.d.ts.map +1 -1
  36. package/dist/dev/route-scanner.js +1 -5
  37. package/dist/dev/route-scanner.js.map +1 -1
  38. package/dist/http.d.ts +14 -14
  39. package/dist/http.d.ts.map +1 -1
  40. package/dist/http.js +34 -41
  41. package/dist/http.js.map +1 -1
  42. package/dist/index.js +1314 -8
  43. package/dist/index.mjs +1314 -8
  44. package/dist/middleware/manager.d.ts.map +1 -1
  45. package/dist/middleware/manager.js +4 -0
  46. package/dist/middleware/manager.js.map +1 -1
  47. package/dist/openapi/docs-ui.d.ts +2 -0
  48. package/dist/openapi/docs-ui.d.ts.map +1 -0
  49. package/dist/openapi/docs-ui.js +1313 -0
  50. package/dist/openapi/docs-ui.js.map +1 -0
  51. package/dist/openapi/generator.d.ts +12 -0
  52. package/dist/openapi/generator.d.ts.map +1 -0
  53. package/dist/openapi/generator.js +273 -0
  54. package/dist/openapi/generator.js.map +1 -0
  55. package/dist/types/index.d.ts +70 -11
  56. package/dist/types/index.d.ts.map +1 -1
  57. package/dist/types/standard-schema.d.ts +118 -0
  58. package/dist/types/standard-schema.d.ts.map +1 -0
  59. package/dist/types/standard-schema.js +2 -0
  60. package/dist/types/standard-schema.js.map +1 -0
  61. package/dist/utils/cors.d.ts +13 -0
  62. package/dist/utils/cors.d.ts.map +1 -0
  63. package/dist/utils/cors.js +89 -0
  64. package/dist/utils/cors.js.map +1 -0
  65. package/dist/utils/path.d.ts +6 -0
  66. package/dist/utils/path.d.ts.map +1 -1
  67. package/dist/utils/path.js +5 -0
  68. package/dist/utils/path.js.map +1 -1
  69. package/dist/utils/schema-validation.d.ts +31 -0
  70. package/dist/utils/schema-validation.d.ts.map +1 -0
  71. package/dist/utils/schema-validation.js +77 -0
  72. package/dist/utils/schema-validation.js.map +1 -0
  73. package/dist/utils/validation.d.ts.map +1 -1
  74. package/dist/utils/validation.js +1 -0
  75. package/dist/utils/validation.js.map +1 -1
  76. package/package.json +13 -12
  77. package/src/auth/protected.ts +3 -13
  78. package/src/cache/manager.ts +4 -18
  79. package/src/cli/index.ts +19 -75
  80. package/src/cli/option-resolution.ts +40 -0
  81. package/src/constants/index.ts +7 -0
  82. package/src/core/config-loader.ts +3 -3
  83. package/src/core/router.ts +502 -156
  84. package/src/core/server.ts +327 -32
  85. package/src/core/vector.ts +49 -29
  86. package/src/dev/route-generator.ts +1 -3
  87. package/src/dev/route-scanner.ts +2 -9
  88. package/src/http.ts +85 -125
  89. package/src/middleware/manager.ts +4 -0
  90. package/src/openapi/assets/tailwindcdn.js +83 -0
  91. package/src/openapi/docs-ui.ts +1317 -0
  92. package/src/openapi/generator.ts +359 -0
  93. package/src/types/index.ts +104 -17
  94. package/src/types/standard-schema.ts +147 -0
  95. package/src/utils/cors.ts +101 -0
  96. package/src/utils/path.ts +6 -0
  97. package/src/utils/schema-validation.ts +123 -0
  98. package/src/utils/validation.ts +1 -0
package/src/http.ts CHANGED
@@ -1,81 +1,63 @@
1
- import { cors, type IRequest, type RouteEntry, withContent } from 'itty-router';
2
- import { buildRouteRegex } from './utils/path';
3
1
  import { CONTENT_TYPES, HTTP_STATUS } from './constants';
4
2
  import type {
5
3
  CacheOptions,
6
4
  DefaultVectorTypes,
7
5
  GetAuthType,
6
+ InferRouteInputFromSchemaDefinition,
7
+ RouteSchemaDefinition,
8
8
  VectorRequest,
9
9
  VectorTypes,
10
10
  } from './types';
11
11
  import { getVectorInstance } from './core/vector';
12
12
 
13
- export interface ProtectedRequest<TTypes extends VectorTypes = DefaultVectorTypes>
14
- extends IRequest {
15
- authUser?: GetAuthType<TTypes>;
16
- }
17
-
18
- export const { preflight, corsify } = cors({
19
- origin: '*',
20
- credentials: true,
21
- allowHeaders: 'Content-Type, Authorization',
22
- allowMethods: 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
23
- exposeHeaders: 'Authorization',
24
- maxAge: 86_400,
25
- });
26
-
27
- interface ExtendedApiOptions extends ApiOptions {
13
+ interface ExtendedApiOptions<TSchemaDef extends RouteSchemaDefinition | undefined = RouteSchemaDefinition | undefined>
14
+ extends ApiOptions<TSchemaDef> {
28
15
  method: string;
29
16
  path: string;
30
17
  }
31
18
 
32
- export interface RouteDefinition<TTypes extends VectorTypes = DefaultVectorTypes> {
33
- entry: RouteEntry;
34
- options: ExtendedApiOptions;
35
- handler: (req: VectorRequest<TTypes>) => Promise<unknown>;
19
+ export interface RouteDefinition<
20
+ TTypes extends VectorTypes = DefaultVectorTypes,
21
+ TValidatedInput = undefined,
22
+ TSchemaDef extends RouteSchemaDefinition | undefined = RouteSchemaDefinition | undefined,
23
+ > {
24
+ entry: { method: string; path: string };
25
+ options: ExtendedApiOptions<TSchemaDef>;
26
+ handler: (req: VectorRequest<TTypes, TValidatedInput>) => Promise<unknown> | unknown;
36
27
  }
37
28
 
38
- export function route<TTypes extends VectorTypes = DefaultVectorTypes>(
39
- options: ExtendedApiOptions,
40
- fn: (req: VectorRequest<TTypes>) => Promise<unknown>
41
- ): RouteDefinition<TTypes> {
42
- const handler = api(options, fn);
43
-
44
- const entry: RouteEntry = [
45
- options.method.toUpperCase(),
46
- buildRouteRegex(options.path),
47
- [handler],
48
- options.path,
49
- ];
50
-
29
+ export function route<
30
+ TTypes extends VectorTypes = DefaultVectorTypes,
31
+ TSchemaDef extends RouteSchemaDefinition | undefined = RouteSchemaDefinition | undefined,
32
+ >(
33
+ options: ExtendedApiOptions<TSchemaDef>,
34
+ fn: (req: VectorRequest<TTypes, InferRouteInputFromSchemaDefinition<TSchemaDef>>) => Promise<unknown> | unknown
35
+ ): RouteDefinition<TTypes, InferRouteInputFromSchemaDefinition<TSchemaDef>, TSchemaDef> {
51
36
  return {
52
- entry,
37
+ entry: {
38
+ method: options.method.toUpperCase(),
39
+ path: options.path,
40
+ },
53
41
  options,
54
42
  handler: fn,
55
43
  };
56
44
  }
57
45
 
58
- function hasBigInt(value: unknown, depth = 0): boolean {
59
- if (typeof value === 'bigint') return true;
60
- if (depth > 4 || value === null || typeof value !== 'object') return false;
61
- for (const v of Object.values(value as object)) {
62
- if (hasBigInt(v, depth + 1)) return true;
63
- }
64
- return false;
65
- }
66
-
67
46
  function stringifyData(data: unknown): string {
68
47
  const val = data ?? null;
69
- if (!hasBigInt(val)) return JSON.stringify(val);
70
- return JSON.stringify(val, (_key, value) =>
71
- typeof value === 'bigint' ? value.toString() : value
72
- );
48
+ try {
49
+ return JSON.stringify(val);
50
+ } catch (e) {
51
+ if (e instanceof TypeError && /\bbigint\b/i.test(e.message)) {
52
+ return JSON.stringify(val, (_key, value) => (typeof value === 'bigint' ? value.toString() : value));
53
+ }
54
+ throw e;
55
+ }
73
56
  }
74
57
 
75
58
  const ApiResponse = {
76
59
  success: <T>(data: T, contentType?: string) => createResponse(HTTP_STATUS.OK, data, contentType),
77
- created: <T>(data: T, contentType?: string) =>
78
- createResponse(HTTP_STATUS.CREATED, data, contentType),
60
+ created: <T>(data: T, contentType?: string) => createResponse(HTTP_STATUS.CREATED, data, contentType),
79
61
  };
80
62
 
81
63
  function createErrorResponse(code: number, message: string, contentType?: string): Response {
@@ -97,40 +79,29 @@ export const APIError = {
97
79
  unauthorized: (msg = 'Unauthorized', contentType?: string) =>
98
80
  createErrorResponse(HTTP_STATUS.UNAUTHORIZED, msg, contentType),
99
81
 
100
- paymentRequired: (msg = 'Payment Required', contentType?: string) =>
101
- createErrorResponse(402, msg, contentType),
82
+ paymentRequired: (msg = 'Payment Required', contentType?: string) => createErrorResponse(402, msg, contentType),
102
83
 
103
- forbidden: (msg = 'Forbidden', contentType?: string) =>
104
- createErrorResponse(HTTP_STATUS.FORBIDDEN, msg, contentType),
84
+ forbidden: (msg = 'Forbidden', contentType?: string) => createErrorResponse(HTTP_STATUS.FORBIDDEN, msg, contentType),
105
85
 
106
- notFound: (msg = 'Not Found', contentType?: string) =>
107
- createErrorResponse(HTTP_STATUS.NOT_FOUND, msg, contentType),
86
+ notFound: (msg = 'Not Found', contentType?: string) => createErrorResponse(HTTP_STATUS.NOT_FOUND, msg, contentType),
108
87
 
109
- methodNotAllowed: (msg = 'Method Not Allowed', contentType?: string) =>
110
- createErrorResponse(405, msg, contentType),
88
+ methodNotAllowed: (msg = 'Method Not Allowed', contentType?: string) => createErrorResponse(405, msg, contentType),
111
89
 
112
- notAcceptable: (msg = 'Not Acceptable', contentType?: string) =>
113
- createErrorResponse(406, msg, contentType),
90
+ notAcceptable: (msg = 'Not Acceptable', contentType?: string) => createErrorResponse(406, msg, contentType),
114
91
 
115
- requestTimeout: (msg = 'Request Timeout', contentType?: string) =>
116
- createErrorResponse(408, msg, contentType),
92
+ requestTimeout: (msg = 'Request Timeout', contentType?: string) => createErrorResponse(408, msg, contentType),
117
93
 
118
- conflict: (msg = 'Conflict', contentType?: string) =>
119
- createErrorResponse(HTTP_STATUS.CONFLICT, msg, contentType),
94
+ conflict: (msg = 'Conflict', contentType?: string) => createErrorResponse(HTTP_STATUS.CONFLICT, msg, contentType),
120
95
 
121
96
  gone: (msg = 'Gone', contentType?: string) => createErrorResponse(410, msg, contentType),
122
97
 
123
- lengthRequired: (msg = 'Length Required', contentType?: string) =>
124
- createErrorResponse(411, msg, contentType),
98
+ lengthRequired: (msg = 'Length Required', contentType?: string) => createErrorResponse(411, msg, contentType),
125
99
 
126
- preconditionFailed: (msg = 'Precondition Failed', contentType?: string) =>
127
- createErrorResponse(412, msg, contentType),
100
+ preconditionFailed: (msg = 'Precondition Failed', contentType?: string) => createErrorResponse(412, msg, contentType),
128
101
 
129
- payloadTooLarge: (msg = 'Payload Too Large', contentType?: string) =>
130
- createErrorResponse(413, msg, contentType),
102
+ payloadTooLarge: (msg = 'Payload Too Large', contentType?: string) => createErrorResponse(413, msg, contentType),
131
103
 
132
- uriTooLong: (msg = 'URI Too Long', contentType?: string) =>
133
- createErrorResponse(414, msg, contentType),
104
+ uriTooLong: (msg = 'URI Too Long', contentType?: string) => createErrorResponse(414, msg, contentType),
134
105
 
135
106
  unsupportedMediaType: (msg = 'Unsupported Media Type', contentType?: string) =>
136
107
  createErrorResponse(415, msg, contentType),
@@ -138,33 +109,27 @@ export const APIError = {
138
109
  rangeNotSatisfiable: (msg = 'Range Not Satisfiable', contentType?: string) =>
139
110
  createErrorResponse(416, msg, contentType),
140
111
 
141
- expectationFailed: (msg = 'Expectation Failed', contentType?: string) =>
142
- createErrorResponse(417, msg, contentType),
112
+ expectationFailed: (msg = 'Expectation Failed', contentType?: string) => createErrorResponse(417, msg, contentType),
143
113
 
144
- imATeapot: (msg = "I'm a teapot", contentType?: string) =>
145
- createErrorResponse(418, msg, contentType),
114
+ imATeapot: (msg = "I'm a teapot", contentType?: string) => createErrorResponse(418, msg, contentType),
146
115
 
147
- misdirectedRequest: (msg = 'Misdirected Request', contentType?: string) =>
148
- createErrorResponse(421, msg, contentType),
116
+ misdirectedRequest: (msg = 'Misdirected Request', contentType?: string) => createErrorResponse(421, msg, contentType),
149
117
 
150
118
  unprocessableEntity: (msg = 'Unprocessable Entity', contentType?: string) =>
151
119
  createErrorResponse(HTTP_STATUS.UNPROCESSABLE_ENTITY, msg, contentType),
152
120
 
153
121
  locked: (msg = 'Locked', contentType?: string) => createErrorResponse(423, msg, contentType),
154
122
 
155
- failedDependency: (msg = 'Failed Dependency', contentType?: string) =>
156
- createErrorResponse(424, msg, contentType),
123
+ failedDependency: (msg = 'Failed Dependency', contentType?: string) => createErrorResponse(424, msg, contentType),
157
124
 
158
125
  tooEarly: (msg = 'Too Early', contentType?: string) => createErrorResponse(425, msg, contentType),
159
126
 
160
- upgradeRequired: (msg = 'Upgrade Required', contentType?: string) =>
161
- createErrorResponse(426, msg, contentType),
127
+ upgradeRequired: (msg = 'Upgrade Required', contentType?: string) => createErrorResponse(426, msg, contentType),
162
128
 
163
129
  preconditionRequired: (msg = 'Precondition Required', contentType?: string) =>
164
130
  createErrorResponse(428, msg, contentType),
165
131
 
166
- tooManyRequests: (msg = 'Too Many Requests', contentType?: string) =>
167
- createErrorResponse(429, msg, contentType),
132
+ tooManyRequests: (msg = 'Too Many Requests', contentType?: string) => createErrorResponse(429, msg, contentType),
168
133
 
169
134
  requestHeaderFieldsTooLarge: (msg = 'Request Header Fields Too Large', contentType?: string) =>
170
135
  createErrorResponse(431, msg, contentType),
@@ -176,17 +141,13 @@ export const APIError = {
176
141
  internalServerError: (msg = 'Internal Server Error', contentType?: string) =>
177
142
  createErrorResponse(HTTP_STATUS.INTERNAL_SERVER_ERROR, msg, contentType),
178
143
 
179
- notImplemented: (msg = 'Not Implemented', contentType?: string) =>
180
- createErrorResponse(501, msg, contentType),
144
+ notImplemented: (msg = 'Not Implemented', contentType?: string) => createErrorResponse(501, msg, contentType),
181
145
 
182
- badGateway: (msg = 'Bad Gateway', contentType?: string) =>
183
- createErrorResponse(502, msg, contentType),
146
+ badGateway: (msg = 'Bad Gateway', contentType?: string) => createErrorResponse(502, msg, contentType),
184
147
 
185
- serviceUnavailable: (msg = 'Service Unavailable', contentType?: string) =>
186
- createErrorResponse(503, msg, contentType),
148
+ serviceUnavailable: (msg = 'Service Unavailable', contentType?: string) => createErrorResponse(503, msg, contentType),
187
149
 
188
- gatewayTimeout: (msg = 'Gateway Timeout', contentType?: string) =>
189
- createErrorResponse(504, msg, contentType),
150
+ gatewayTimeout: (msg = 'Gateway Timeout', contentType?: string) => createErrorResponse(504, msg, contentType),
190
151
 
191
152
  httpVersionNotSupported: (msg = 'HTTP Version Not Supported', contentType?: string) =>
192
153
  createErrorResponse(505, msg, contentType),
@@ -197,11 +158,9 @@ export const APIError = {
197
158
  insufficientStorage: (msg = 'Insufficient Storage', contentType?: string) =>
198
159
  createErrorResponse(507, msg, contentType),
199
160
 
200
- loopDetected: (msg = 'Loop Detected', contentType?: string) =>
201
- createErrorResponse(508, msg, contentType),
161
+ loopDetected: (msg = 'Loop Detected', contentType?: string) => createErrorResponse(508, msg, contentType),
202
162
 
203
- notExtended: (msg = 'Not Extended', contentType?: string) =>
204
- createErrorResponse(510, msg, contentType),
163
+ notExtended: (msg = 'Not Extended', contentType?: string) => createErrorResponse(510, msg, contentType),
205
164
 
206
165
  networkAuthenticationRequired: (msg = 'Network Authentication Required', contentType?: string) =>
207
166
  createErrorResponse(511, msg, contentType),
@@ -210,22 +169,15 @@ export const APIError = {
210
169
  invalidArgument: (msg = 'Invalid Argument', contentType?: string) =>
211
170
  createErrorResponse(HTTP_STATUS.UNPROCESSABLE_ENTITY, msg, contentType),
212
171
 
213
- rateLimitExceeded: (msg = 'Rate Limit Exceeded', contentType?: string) =>
214
- createErrorResponse(429, msg, contentType),
172
+ rateLimitExceeded: (msg = 'Rate Limit Exceeded', contentType?: string) => createErrorResponse(429, msg, contentType),
215
173
 
216
- maintenance: (msg = 'Service Under Maintenance', contentType?: string) =>
217
- createErrorResponse(503, msg, contentType),
174
+ maintenance: (msg = 'Service Under Maintenance', contentType?: string) => createErrorResponse(503, msg, contentType),
218
175
 
219
176
  // Helper to create custom error with any status code
220
- custom: (statusCode: number, msg: string, contentType?: string) =>
221
- createErrorResponse(statusCode, msg, contentType),
177
+ custom: (statusCode: number, msg: string, contentType?: string) => createErrorResponse(statusCode, msg, contentType),
222
178
  };
223
179
 
224
- export function createResponse(
225
- statusCode: number,
226
- data?: unknown,
227
- contentType: string = CONTENT_TYPES.JSON
228
- ): Response {
180
+ export function createResponse(statusCode: number, data?: unknown, contentType: string = CONTENT_TYPES.JSON): Response {
229
181
  const body = contentType === CONTENT_TYPES.JSON ? stringifyData(data) : data;
230
182
 
231
183
  return new Response(body as string, {
@@ -238,7 +190,6 @@ export const protectedRoute = async <TTypes extends VectorTypes = DefaultVectorT
238
190
  request: VectorRequest<TTypes>,
239
191
  responseContentType?: string
240
192
  ) => {
241
- // Get the Vector instance to access the protected handler
242
193
  const vector = getVectorInstance();
243
194
 
244
195
  const protectedHandler = vector.getProtectedHandler();
@@ -250,25 +201,24 @@ export const protectedRoute = async <TTypes extends VectorTypes = DefaultVectorT
250
201
  const authUser = await protectedHandler(request as any);
251
202
  request.authUser = authUser as GetAuthType<TTypes>;
252
203
  } catch (error) {
253
- throw APIError.unauthorized(
254
- error instanceof Error ? error.message : 'Authentication failed',
255
- responseContentType
256
- );
204
+ throw APIError.unauthorized(error instanceof Error ? error.message : 'Authentication failed', responseContentType);
257
205
  }
258
206
  };
259
207
 
260
- export interface ApiOptions {
208
+ export interface ApiOptions<TSchemaDef extends RouteSchemaDefinition | undefined = RouteSchemaDefinition | undefined> {
261
209
  auth?: boolean;
262
210
  expose?: boolean;
263
211
  rawRequest?: boolean;
212
+ validate?: boolean;
264
213
  rawResponse?: boolean;
265
214
  cache?: CacheOptions | number | null;
266
215
  responseContentType?: string;
216
+ schema?: TSchemaDef;
267
217
  }
268
218
 
269
- export function api<TTypes extends VectorTypes = DefaultVectorTypes>(
219
+ export function api<TTypes extends VectorTypes = DefaultVectorTypes, TValidatedInput = undefined>(
270
220
  options: ApiOptions,
271
- fn: (request: VectorRequest<TTypes>) => Promise<unknown>
221
+ fn: (request: VectorRequest<TTypes, TValidatedInput>) => Promise<unknown> | unknown
272
222
  ) {
273
223
  const {
274
224
  auth = false,
@@ -278,32 +228,42 @@ export function api<TTypes extends VectorTypes = DefaultVectorTypes>(
278
228
  responseContentType = CONTENT_TYPES.JSON,
279
229
  } = options;
280
230
 
281
- // For backward compatibility with direct route usage (not auto-discovered)
282
- // This wrapper is only used when routes are NOT auto-discovered
283
- return async (request: IRequest) => {
231
+ return async (request: Request) => {
232
+ const req = request as unknown as VectorRequest<TTypes>;
233
+
284
234
  if (!expose) {
285
235
  return APIError.forbidden('Forbidden');
286
236
  }
287
237
 
288
238
  try {
289
239
  if (auth) {
290
- await protectedRoute(request as any as VectorRequest<TTypes>, responseContentType);
240
+ await protectedRoute(req, responseContentType);
291
241
  }
292
242
 
293
- if (!rawRequest) {
294
- await withContent(request);
243
+ if (!rawRequest && req.method !== 'GET' && req.method !== 'HEAD') {
244
+ try {
245
+ const contentType = req.headers.get('content-type');
246
+ if (contentType?.startsWith('application/json')) {
247
+ req.content = await req.json();
248
+ } else if (contentType?.startsWith('application/x-www-form-urlencoded')) {
249
+ req.content = Object.fromEntries(await req.formData());
250
+ } else if (contentType?.startsWith('multipart/form-data')) {
251
+ req.content = await req.formData();
252
+ } else {
253
+ req.content = await req.text();
254
+ }
255
+ } catch {
256
+ req.content = null;
257
+ }
295
258
  }
296
259
 
297
- // Cache handling is now done in the router
298
- const result = await fn(request as any as VectorRequest<TTypes>);
260
+ const result = await fn(req as unknown as VectorRequest<TTypes, TValidatedInput>);
299
261
 
300
262
  return rawResponse ? result : ApiResponse.success(result, responseContentType);
301
263
  } catch (err: unknown) {
302
- // Ensure we return a Response object
303
264
  if (err instanceof Response) {
304
265
  return err;
305
266
  }
306
- // For non-Response errors, wrap them
307
267
  return APIError.internalServerError(String(err), responseContentType);
308
268
  }
309
269
  };
@@ -19,6 +19,8 @@ export class MiddlewareManager<TTypes extends VectorTypes = DefaultVectorTypes>
19
19
  }
20
20
 
21
21
  async executeBefore(request: VectorRequest<TTypes>): Promise<VectorRequest<TTypes> | Response> {
22
+ if (this.beforeHandlers.length === 0) return request;
23
+
22
24
  let currentRequest = request;
23
25
 
24
26
  for (const handler of this.beforeHandlers) {
@@ -35,6 +37,8 @@ export class MiddlewareManager<TTypes extends VectorTypes = DefaultVectorTypes>
35
37
  }
36
38
 
37
39
  async executeFinally(response: Response, request: VectorRequest<TTypes>): Promise<Response> {
40
+ if (this.finallyHandlers.length === 0) return response;
41
+
38
42
  let currentResponse = response;
39
43
 
40
44
  for (const handler of this.finallyHandlers) {