vector-framework 1.0.0 → 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 (104) hide show
  1. package/README.md +87 -634
  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 +5 -2
  5. package/dist/cache/manager.d.ts.map +1 -1
  6. package/dist/cache/manager.js +21 -12
  7. package/dist/cache/manager.js.map +1 -1
  8. package/dist/cli/index.js +60 -126
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/option-resolution.d.ts +4 -0
  11. package/dist/cli/option-resolution.d.ts.map +1 -0
  12. package/dist/cli/option-resolution.js +28 -0
  13. package/dist/cli/option-resolution.js.map +1 -0
  14. package/dist/cli.js +2774 -599
  15. package/dist/constants/index.d.ts +3 -0
  16. package/dist/constants/index.d.ts.map +1 -1
  17. package/dist/constants/index.js +6 -0
  18. package/dist/constants/index.js.map +1 -1
  19. package/dist/core/config-loader.d.ts +2 -2
  20. package/dist/core/config-loader.d.ts.map +1 -1
  21. package/dist/core/config-loader.js +18 -18
  22. package/dist/core/config-loader.js.map +1 -1
  23. package/dist/core/router.d.ts +41 -15
  24. package/dist/core/router.d.ts.map +1 -1
  25. package/dist/core/router.js +465 -150
  26. package/dist/core/router.js.map +1 -1
  27. package/dist/core/server.d.ts +17 -3
  28. package/dist/core/server.d.ts.map +1 -1
  29. package/dist/core/server.js +274 -33
  30. package/dist/core/server.js.map +1 -1
  31. package/dist/core/vector.d.ts +9 -8
  32. package/dist/core/vector.d.ts.map +1 -1
  33. package/dist/core/vector.js +40 -32
  34. package/dist/core/vector.js.map +1 -1
  35. package/dist/dev/route-generator.d.ts.map +1 -1
  36. package/dist/dev/route-generator.js.map +1 -1
  37. package/dist/dev/route-scanner.d.ts +1 -1
  38. package/dist/dev/route-scanner.d.ts.map +1 -1
  39. package/dist/dev/route-scanner.js +37 -43
  40. package/dist/dev/route-scanner.js.map +1 -1
  41. package/dist/http.d.ts +14 -14
  42. package/dist/http.d.ts.map +1 -1
  43. package/dist/http.js +84 -84
  44. package/dist/http.js.map +1 -1
  45. package/dist/index.d.ts +3 -3
  46. package/dist/index.js +1314 -8
  47. package/dist/index.mjs +1314 -8
  48. package/dist/middleware/manager.d.ts +1 -1
  49. package/dist/middleware/manager.d.ts.map +1 -1
  50. package/dist/middleware/manager.js +4 -0
  51. package/dist/middleware/manager.js.map +1 -1
  52. package/dist/openapi/docs-ui.d.ts +2 -0
  53. package/dist/openapi/docs-ui.d.ts.map +1 -0
  54. package/dist/openapi/docs-ui.js +1313 -0
  55. package/dist/openapi/docs-ui.js.map +1 -0
  56. package/dist/openapi/generator.d.ts +12 -0
  57. package/dist/openapi/generator.d.ts.map +1 -0
  58. package/dist/openapi/generator.js +273 -0
  59. package/dist/openapi/generator.js.map +1 -0
  60. package/dist/types/index.d.ts +70 -11
  61. package/dist/types/index.d.ts.map +1 -1
  62. package/dist/types/standard-schema.d.ts +118 -0
  63. package/dist/types/standard-schema.d.ts.map +1 -0
  64. package/dist/types/standard-schema.js +2 -0
  65. package/dist/types/standard-schema.js.map +1 -0
  66. package/dist/utils/cors.d.ts +13 -0
  67. package/dist/utils/cors.d.ts.map +1 -0
  68. package/dist/utils/cors.js +89 -0
  69. package/dist/utils/cors.js.map +1 -0
  70. package/dist/utils/path.d.ts +7 -0
  71. package/dist/utils/path.d.ts.map +1 -1
  72. package/dist/utils/path.js +14 -3
  73. package/dist/utils/path.js.map +1 -1
  74. package/dist/utils/schema-validation.d.ts +31 -0
  75. package/dist/utils/schema-validation.d.ts.map +1 -0
  76. package/dist/utils/schema-validation.js +77 -0
  77. package/dist/utils/schema-validation.js.map +1 -0
  78. package/dist/utils/validation.d.ts.map +1 -1
  79. package/dist/utils/validation.js +1 -0
  80. package/dist/utils/validation.js.map +1 -1
  81. package/package.json +24 -19
  82. package/src/auth/protected.ts +3 -13
  83. package/src/cache/manager.ts +25 -30
  84. package/src/cli/index.ts +62 -141
  85. package/src/cli/option-resolution.ts +40 -0
  86. package/src/constants/index.ts +7 -0
  87. package/src/core/config-loader.ts +20 -22
  88. package/src/core/router.ts +535 -155
  89. package/src/core/server.ts +354 -45
  90. package/src/core/vector.ts +71 -61
  91. package/src/dev/route-generator.ts +1 -3
  92. package/src/dev/route-scanner.ts +38 -51
  93. package/src/http.ts +117 -187
  94. package/src/index.ts +3 -3
  95. package/src/middleware/manager.ts +8 -11
  96. package/src/openapi/assets/tailwindcdn.js +83 -0
  97. package/src/openapi/docs-ui.ts +1317 -0
  98. package/src/openapi/generator.ts +359 -0
  99. package/src/types/index.ts +104 -17
  100. package/src/types/standard-schema.ts +147 -0
  101. package/src/utils/cors.ts +101 -0
  102. package/src/utils/path.ts +19 -4
  103. package/src/utils/schema-validation.ts +123 -0
  104. package/src/utils/validation.ts +1 -0
@@ -0,0 +1,359 @@
1
+ import type { RegisteredRouteDefinition } from '../core/router';
2
+ import type { OpenAPIInfoOptions, RouteSchemaDefinition, StandardJSONSchemaCapable } from '../types';
3
+
4
+ type JsonSchema = Record<string, unknown>;
5
+
6
+ export interface OpenAPIGenerationOptions {
7
+ target: string;
8
+ info?: OpenAPIInfoOptions;
9
+ }
10
+
11
+ export interface OpenAPIGenerationResult {
12
+ document: Record<string, unknown>;
13
+ warnings: string[];
14
+ }
15
+
16
+ function isJSONSchemaCapable(schema: unknown): schema is StandardJSONSchemaCapable {
17
+ const standard = (schema as any)?.['~standard'];
18
+ const converter = standard?.jsonSchema;
19
+ return (
20
+ !!standard &&
21
+ typeof standard === 'object' &&
22
+ standard.version === 1 &&
23
+ !!converter &&
24
+ typeof converter.input === 'function' &&
25
+ typeof converter.output === 'function'
26
+ );
27
+ }
28
+
29
+ function normalizeRoutePathForOpenAPI(path: string): { openapiPath: string; pathParamNames: string[] } {
30
+ let wildcardCount = 0;
31
+ const pathParamNames: string[] = [];
32
+
33
+ const segments = path.split('/').map((segment) => {
34
+ const greedyParamMatch = /^:([A-Za-z0-9_]+)\+$/.exec(segment);
35
+ if (greedyParamMatch?.[1]) {
36
+ pathParamNames.push(greedyParamMatch[1]);
37
+ return `{${greedyParamMatch[1]}}`;
38
+ }
39
+
40
+ const paramMatch = /^:([A-Za-z0-9_]+)$/.exec(segment);
41
+ if (paramMatch?.[1]) {
42
+ pathParamNames.push(paramMatch[1]);
43
+ return `{${paramMatch[1]}}`;
44
+ }
45
+
46
+ if (segment === '*') {
47
+ wildcardCount += 1;
48
+ const wildcardParamName = wildcardCount === 1 ? 'wildcard' : `wildcard${wildcardCount}`;
49
+ pathParamNames.push(wildcardParamName);
50
+ return `{${wildcardParamName}}`;
51
+ }
52
+
53
+ return segment;
54
+ });
55
+
56
+ return {
57
+ openapiPath: segments.join('/'),
58
+ pathParamNames,
59
+ };
60
+ }
61
+
62
+ function toOpenAPIPath(path: string): string {
63
+ return normalizeRoutePathForOpenAPI(path).openapiPath;
64
+ }
65
+
66
+ function createOperationId(method: string, path: string): string {
67
+ const normalized = `${method.toLowerCase()}_${path}`
68
+ .replace(/[:{}]/g, '')
69
+ .replace(/[^A-Za-z0-9_]+/g, '_')
70
+ .replace(/^_+|_+$/g, '');
71
+ return normalized || `${method.toLowerCase()}_operation`;
72
+ }
73
+
74
+ function inferTagFromPath(path: string): string {
75
+ const segments = path.split('/').filter(Boolean);
76
+ for (const segment of segments) {
77
+ if (!segment.startsWith(':') && segment !== '*') {
78
+ return segment.toLowerCase();
79
+ }
80
+ }
81
+ return 'default';
82
+ }
83
+
84
+ function extractPathParamNames(path: string): string[] {
85
+ return normalizeRoutePathForOpenAPI(path).pathParamNames;
86
+ }
87
+
88
+ function addMissingPathParameters(operation: Record<string, any>, routePath: string): void {
89
+ const existingPathNames = new Set(
90
+ (operation.parameters || []).filter((p: any) => p.in === 'path').map((p: any) => String(p.name))
91
+ );
92
+
93
+ for (const pathName of extractPathParamNames(routePath)) {
94
+ if (existingPathNames.has(pathName)) continue;
95
+
96
+ (operation.parameters ||= []).push({
97
+ name: pathName,
98
+ in: 'path',
99
+ required: true,
100
+ schema: { type: 'string' },
101
+ });
102
+ }
103
+ }
104
+
105
+ function isNoBodyResponseStatus(status: string): boolean {
106
+ const numericStatus = Number(status);
107
+ if (!Number.isInteger(numericStatus)) return false;
108
+ return (
109
+ (numericStatus >= 100 && numericStatus < 200) ||
110
+ numericStatus === 204 ||
111
+ numericStatus === 205 ||
112
+ numericStatus === 304
113
+ );
114
+ }
115
+
116
+ function getResponseDescription(status: string): string {
117
+ if (status === '204') return 'No Content';
118
+ if (status === '205') return 'Reset Content';
119
+ if (status === '304') return 'Not Modified';
120
+ const numericStatus = Number(status);
121
+ if (Number.isInteger(numericStatus) && numericStatus >= 100 && numericStatus < 200) {
122
+ return 'Informational';
123
+ }
124
+ return 'OK';
125
+ }
126
+
127
+ function convertInputSchema(
128
+ routePath: string,
129
+ inputSchema: unknown,
130
+ target: string,
131
+ warnings: string[]
132
+ ): JsonSchema | null {
133
+ if (!isJSONSchemaCapable(inputSchema)) {
134
+ return null;
135
+ }
136
+
137
+ try {
138
+ return inputSchema['~standard'].jsonSchema.input({ target });
139
+ } catch (error) {
140
+ warnings.push(
141
+ `[OpenAPI] Failed input schema conversion for ${routePath}: ${
142
+ error instanceof Error ? error.message : String(error)
143
+ }`
144
+ );
145
+ return null;
146
+ }
147
+ }
148
+
149
+ function convertOutputSchema(
150
+ routePath: string,
151
+ statusCode: string,
152
+ outputSchema: unknown,
153
+ target: string,
154
+ warnings: string[]
155
+ ): JsonSchema | null {
156
+ if (!isJSONSchemaCapable(outputSchema)) {
157
+ return null;
158
+ }
159
+
160
+ try {
161
+ return outputSchema['~standard'].jsonSchema.output({ target });
162
+ } catch (error) {
163
+ warnings.push(
164
+ `[OpenAPI] Failed output schema conversion for ${routePath} (${statusCode}): ${
165
+ error instanceof Error ? error.message : String(error)
166
+ }`
167
+ );
168
+ return null;
169
+ }
170
+ }
171
+
172
+ function isRecord(value: unknown): value is Record<string, unknown> {
173
+ return !!value && typeof value === 'object' && !Array.isArray(value);
174
+ }
175
+
176
+ function addStructuredInputToOperation(operation: Record<string, any>, inputJSONSchema: JsonSchema): void {
177
+ if (!isRecord(inputJSONSchema)) return;
178
+ if (inputJSONSchema.type !== 'object' || !isRecord(inputJSONSchema.properties)) {
179
+ operation.requestBody = {
180
+ required: true,
181
+ content: {
182
+ 'application/json': {
183
+ schema: inputJSONSchema,
184
+ },
185
+ },
186
+ };
187
+ return;
188
+ }
189
+
190
+ const rootRequired = new Set<string>(
191
+ Array.isArray(inputJSONSchema.required) ? (inputJSONSchema.required as string[]) : []
192
+ );
193
+ const properties = inputJSONSchema.properties as Record<string, unknown>;
194
+ const parameters: any[] = Array.isArray(operation.parameters) ? operation.parameters : [];
195
+
196
+ const parameterSections: Array<{
197
+ key: string;
198
+ in: 'path' | 'query' | 'header' | 'cookie';
199
+ }> = [
200
+ { key: 'params', in: 'path' },
201
+ { key: 'query', in: 'query' },
202
+ { key: 'headers', in: 'header' },
203
+ { key: 'cookies', in: 'cookie' },
204
+ ];
205
+
206
+ for (const section of parameterSections) {
207
+ const sectionSchema = properties[section.key];
208
+ if (!isRecord(sectionSchema)) continue;
209
+ if (sectionSchema.type !== 'object' || !isRecord(sectionSchema.properties)) continue;
210
+
211
+ const sectionRequired = new Set<string>(
212
+ Array.isArray(sectionSchema.required) ? (sectionSchema.required as string[]) : []
213
+ );
214
+
215
+ for (const [name, schema] of Object.entries(sectionSchema.properties)) {
216
+ parameters.push({
217
+ name,
218
+ in: section.in,
219
+ required: section.in === 'path' ? true : sectionRequired.has(name),
220
+ schema: isRecord(schema) ? schema : {},
221
+ });
222
+ }
223
+ }
224
+
225
+ if (parameters.length > 0) {
226
+ const deduped = new Map<string, any>();
227
+ for (const parameter of parameters) {
228
+ deduped.set(`${parameter.in}:${parameter.name}`, parameter);
229
+ }
230
+ operation.parameters = [...deduped.values()];
231
+ }
232
+
233
+ const bodySchema = properties.body;
234
+ if (bodySchema) {
235
+ operation.requestBody = {
236
+ required: rootRequired.has('body'),
237
+ content: {
238
+ 'application/json': {
239
+ schema: isRecord(bodySchema) ? bodySchema : {},
240
+ },
241
+ },
242
+ };
243
+ }
244
+ }
245
+
246
+ function addOutputSchemasToOperation(
247
+ operation: Record<string, any>,
248
+ routePath: string,
249
+ routeSchema: RouteSchemaDefinition,
250
+ target: string,
251
+ warnings: string[]
252
+ ): void {
253
+ const output = routeSchema.output;
254
+
255
+ if (!output) {
256
+ operation.responses = {
257
+ 200: { description: 'OK' },
258
+ };
259
+ return;
260
+ }
261
+
262
+ const responses: Record<string, any> = {};
263
+
264
+ // Single output schema shorthand: schema.output = SomeSchema (defaults to 200)
265
+ if (typeof output === 'object' && output !== null && '~standard' in output) {
266
+ const outputSchema = convertOutputSchema(routePath, '200', output, target, warnings);
267
+
268
+ if (outputSchema) {
269
+ responses['200'] = {
270
+ description: 'OK',
271
+ content: {
272
+ 'application/json': {
273
+ schema: outputSchema,
274
+ },
275
+ },
276
+ };
277
+ } else {
278
+ responses['200'] = { description: 'OK' };
279
+ }
280
+ } else {
281
+ for (const [statusCode, schema] of Object.entries(output as Record<string, unknown>)) {
282
+ const status = String(statusCode);
283
+ const outputSchema = convertOutputSchema(routePath, status, schema, target, warnings);
284
+ const description = getResponseDescription(status);
285
+
286
+ if (outputSchema && !isNoBodyResponseStatus(status)) {
287
+ responses[status] = {
288
+ description,
289
+ content: {
290
+ 'application/json': {
291
+ schema: outputSchema,
292
+ },
293
+ },
294
+ };
295
+ } else {
296
+ responses[status] = {
297
+ description,
298
+ };
299
+ }
300
+ }
301
+ }
302
+
303
+ if (Object.keys(responses).length === 0) {
304
+ responses['200'] = { description: 'OK' };
305
+ }
306
+
307
+ operation.responses = responses;
308
+ }
309
+
310
+ export function generateOpenAPIDocument(
311
+ routes: RegisteredRouteDefinition[],
312
+ options: OpenAPIGenerationOptions
313
+ ): OpenAPIGenerationResult {
314
+ const warnings: string[] = [];
315
+ const paths: Record<string, Record<string, unknown>> = {};
316
+
317
+ for (const route of routes) {
318
+ if (route.options.expose === false) continue;
319
+ if (!route.method || !route.path) continue;
320
+
321
+ const method = route.method.toLowerCase();
322
+ if (method === 'options') continue;
323
+
324
+ const openapiPath = toOpenAPIPath(route.path);
325
+ const operation: Record<string, any> = {
326
+ operationId: createOperationId(method, openapiPath),
327
+ tags: [route.options.schema?.tag || inferTagFromPath(route.path)],
328
+ };
329
+
330
+ const inputJSONSchema = convertInputSchema(route.path, route.options.schema?.input, options.target, warnings);
331
+
332
+ if (inputJSONSchema) {
333
+ addStructuredInputToOperation(operation, inputJSONSchema);
334
+ }
335
+ addMissingPathParameters(operation, route.path);
336
+
337
+ addOutputSchemasToOperation(operation, route.path, route.options.schema || {}, options.target, warnings);
338
+
339
+ paths[openapiPath] ||= {};
340
+ paths[openapiPath][method] = operation;
341
+ }
342
+
343
+ const openapiVersion = options.target === 'openapi-3.0' ? '3.0.3' : '3.1.0';
344
+
345
+ const document = {
346
+ openapi: openapiVersion,
347
+ info: {
348
+ title: options.info?.title || 'Vector API',
349
+ version: options.info?.version || '1.0.0',
350
+ ...(options.info?.description ? { description: options.info.description } : {}),
351
+ },
352
+ paths,
353
+ };
354
+
355
+ return {
356
+ document,
357
+ warnings,
358
+ };
359
+ }
@@ -1,4 +1,4 @@
1
- import type { IRequest } from 'itty-router';
1
+ import type { StandardJSONSchemaV1, StandardSchemaV1, StandardTypedV1 } from './standard-schema';
2
2
 
3
3
  // Default AuthUser type - users can override this with their own type
4
4
  export interface DefaultAuthUser {
@@ -27,13 +27,9 @@ export interface DefaultVectorTypes extends VectorTypes {
27
27
  }
28
28
 
29
29
  // Type helpers
30
- export type GetAuthType<T extends VectorTypes> = T['auth'] extends undefined
31
- ? DefaultAuthUser
32
- : T['auth'];
30
+ export type GetAuthType<T extends VectorTypes> = T['auth'] extends undefined ? DefaultAuthUser : T['auth'];
33
31
 
34
- export type GetContextType<T extends VectorTypes> = T['context'] extends undefined
35
- ? Record<string, any>
36
- : T['context'];
32
+ export type GetContextType<T extends VectorTypes> = T['context'] extends undefined ? Record<string, any> : T['context'];
37
33
 
38
34
  export type GetCacheType<T extends VectorTypes> = T['cache'] extends undefined ? any : T['cache'];
39
35
 
@@ -44,16 +40,39 @@ export type GetMetadataType<T extends VectorTypes> = T['metadata'] extends undef
44
40
  // Legacy support - keep AuthUser for backward compatibility
45
41
  export type AuthUser = DefaultAuthUser;
46
42
 
47
- export interface VectorRequest<TTypes extends VectorTypes = DefaultVectorTypes>
48
- extends Omit<IRequest, 'params'> {
43
+ type DefaultQueryShape = { [key: string]: string | string[] | undefined };
44
+ type DefaultParamsShape = Record<string, string>;
45
+ type DefaultCookiesShape = Record<string, string>;
46
+
47
+ type BaseVectorRequest = Omit<Request, 'body' | 'json' | 'text' | 'formData' | 'arrayBuffer' | 'blob'>;
48
+
49
+ type InferValidatedSection<TValidatedInput, TKey extends string, TFallback> = [TValidatedInput] extends [undefined]
50
+ ? TFallback
51
+ : TValidatedInput extends Record<string, unknown>
52
+ ? TKey extends keyof TValidatedInput
53
+ ? TValidatedInput[TKey]
54
+ : TFallback
55
+ : TFallback;
56
+
57
+ type InferValidatedInputValue<TValidatedInput> = [TValidatedInput] extends [undefined] ? unknown : TValidatedInput;
58
+
59
+ export type BunRouteHandler = (req: Request) => Response | Promise<Response>;
60
+ export type BunMethodMap = Record<string, BunRouteHandler>;
61
+ export type BunRouteTable = Record<string, BunMethodMap | Response>;
62
+ export type LegacyRouteEntry = [string, RegExp, [BunRouteHandler, ...BunRouteHandler[]], string?];
63
+
64
+ export interface VectorRequest<TTypes extends VectorTypes = DefaultVectorTypes, TValidatedInput = undefined>
65
+ extends BaseVectorRequest {
49
66
  authUser?: GetAuthType<TTypes>;
50
67
  context: GetContextType<TTypes>;
51
68
  metadata?: GetMetadataType<TTypes>;
52
- content?: any;
53
- params?: Record<string, string>;
54
- query: { [key: string]: string | string[] | undefined };
69
+ content?: InferValidatedSection<TValidatedInput, 'body', any>;
70
+ body?: InferValidatedSection<TValidatedInput, 'body', any>;
71
+ params?: InferValidatedSection<TValidatedInput, 'params', DefaultParamsShape>;
72
+ query: InferValidatedSection<TValidatedInput, 'query', DefaultQueryShape>;
55
73
  headers: Headers;
56
- cookies?: Record<string, string>;
74
+ cookies?: InferValidatedSection<TValidatedInput, 'cookies', DefaultCookiesShape>;
75
+ validatedInput?: InferValidatedInputValue<TValidatedInput>;
57
76
  startTime?: number;
58
77
  [key: string]: any;
59
78
  }
@@ -63,6 +82,33 @@ export interface CacheOptions {
63
82
  ttl?: number;
64
83
  }
65
84
 
85
+ export type StandardRouteSchema = StandardSchemaV1<any, any>;
86
+ export type RouteSchemaStatusCode = number | `${number}` | 'default';
87
+ export type RouteSchemaOutputMap = Partial<Record<RouteSchemaStatusCode, StandardRouteSchema>>;
88
+
89
+ export interface RouteSchemaDefinition<
90
+ TInput extends StandardRouteSchema | undefined = StandardRouteSchema | undefined,
91
+ TOutput extends RouteSchemaOutputMap | StandardRouteSchema | undefined =
92
+ | RouteSchemaOutputMap
93
+ | StandardRouteSchema
94
+ | undefined,
95
+ > {
96
+ input?: TInput;
97
+ output?: TOutput;
98
+ tag?: string;
99
+ }
100
+
101
+ export type InferStandardSchemaInput<TSchema extends StandardRouteSchema> = StandardSchemaV1.InferInput<TSchema>;
102
+ export type InferStandardSchemaOutput<TSchema extends StandardRouteSchema> = StandardSchemaV1.InferOutput<TSchema>;
103
+ export type StandardJSONSchemaCapable = StandardJSONSchemaV1<any, any>;
104
+
105
+ export type InferRouteInputFromSchemaDefinition<TSchemaDef extends RouteSchemaDefinition | undefined> =
106
+ TSchemaDef extends { input: infer TInputSchema }
107
+ ? TInputSchema extends StandardRouteSchema
108
+ ? InferStandardSchemaOutput<TInputSchema>
109
+ : undefined
110
+ : undefined;
111
+
66
112
  export interface RouteOptions<TTypes extends VectorTypes = DefaultVectorTypes> {
67
113
  method: string;
68
114
  path: string;
@@ -70,9 +116,23 @@ export interface RouteOptions<TTypes extends VectorTypes = DefaultVectorTypes> {
70
116
  expose?: boolean; // defaults to true
71
117
  cache?: CacheOptions | number;
72
118
  rawRequest?: boolean;
119
+ validate?: boolean; // defaults to validating schema.input unless false
73
120
  rawResponse?: boolean;
74
121
  responseContentType?: string;
75
122
  metadata?: GetMetadataType<TTypes>;
123
+ schema?: RouteSchemaDefinition;
124
+ }
125
+
126
+ export interface RouteBooleanDefaults {
127
+ auth?: boolean;
128
+ expose?: boolean;
129
+ rawRequest?: boolean;
130
+ validate?: boolean;
131
+ rawResponse?: boolean;
132
+ }
133
+
134
+ export interface VectorDefaults {
135
+ route?: RouteBooleanDefaults;
76
136
  }
77
137
 
78
138
  // Legacy config interface - will be deprecated
@@ -88,6 +148,8 @@ export interface VectorConfig<TTypes extends VectorTypes = DefaultVectorTypes> {
88
148
  routeExcludePatterns?: string[];
89
149
  autoDiscover?: boolean;
90
150
  idleTimeout?: number;
151
+ defaults?: VectorDefaults;
152
+ openapi?: OpenAPIOptions | boolean;
91
153
  }
92
154
 
93
155
  // New config-driven schema - flat structure
@@ -100,6 +162,7 @@ export interface VectorConfigSchema<TTypes extends VectorTypes = DefaultVectorTy
100
162
  routesDir?: string;
101
163
  routeExcludePatterns?: string[];
102
164
  idleTimeout?: number;
165
+ defaults?: VectorDefaults;
103
166
 
104
167
  // Middleware functions
105
168
  before?: BeforeMiddlewareHandler<TTypes>[];
@@ -112,6 +175,9 @@ export interface VectorConfigSchema<TTypes extends VectorTypes = DefaultVectorTy
112
175
  // CORS configuration
113
176
  cors?: CorsOptions | boolean;
114
177
 
178
+ // OpenAPI/docs configuration
179
+ openapi?: OpenAPIOptions | boolean;
180
+
115
181
  // Custom types for TypeScript
116
182
  types?: VectorTypes;
117
183
  }
@@ -125,6 +191,25 @@ export interface CorsOptions {
125
191
  maxAge?: number;
126
192
  }
127
193
 
194
+ export interface OpenAPIDocsOptions {
195
+ enabled?: boolean;
196
+ path?: string;
197
+ }
198
+
199
+ export interface OpenAPIInfoOptions {
200
+ title?: string;
201
+ version?: string;
202
+ description?: string;
203
+ }
204
+
205
+ export interface OpenAPIOptions {
206
+ enabled?: boolean;
207
+ path?: string;
208
+ target?: 'openapi-3.0' | 'draft-2020-12' | 'draft-07' | ({} & string);
209
+ docs?: boolean | OpenAPIDocsOptions;
210
+ info?: OpenAPIInfoOptions;
211
+ }
212
+
128
213
  export type BeforeMiddlewareHandler<TTypes extends VectorTypes = DefaultVectorTypes> = (
129
214
  request: VectorRequest<TTypes>
130
215
  ) => Promise<VectorRequest<TTypes> | Response> | VectorRequest<TTypes> | Response;
@@ -135,8 +220,8 @@ export type AfterMiddlewareHandler<TTypes extends VectorTypes = DefaultVectorTyp
135
220
  ) => Promise<Response> | Response;
136
221
  export type MiddlewareHandler = BeforeMiddlewareHandler | AfterMiddlewareHandler;
137
222
 
138
- export type RouteHandler<TTypes extends VectorTypes = DefaultVectorTypes> = (
139
- request: VectorRequest<TTypes>
223
+ export type RouteHandler<TTypes extends VectorTypes = DefaultVectorTypes, TValidatedInput = undefined> = (
224
+ request: VectorRequest<TTypes, TValidatedInput>
140
225
  ) => Promise<any> | any;
141
226
 
142
227
  export type ProtectedHandler<TTypes extends VectorTypes = DefaultVectorTypes> = (
@@ -145,9 +230,9 @@ export type ProtectedHandler<TTypes extends VectorTypes = DefaultVectorTypes> =
145
230
 
146
231
  export type CacheHandler = (key: string, factory: () => Promise<any>, ttl: number) => Promise<any>;
147
232
 
148
- export interface RouteDefinition<TTypes extends VectorTypes = DefaultVectorTypes> {
233
+ export interface RouteDefinition<TTypes extends VectorTypes = DefaultVectorTypes, TValidatedInput = undefined> {
149
234
  options: RouteOptions<TTypes>;
150
- handler: RouteHandler<TTypes>;
235
+ handler: RouteHandler<TTypes, TValidatedInput>;
151
236
  }
152
237
 
153
238
  export interface GeneratedRoute<TTypes extends VectorTypes = DefaultVectorTypes> {
@@ -156,3 +241,5 @@ export interface GeneratedRoute<TTypes extends VectorTypes = DefaultVectorTypes>
156
241
  method: string;
157
242
  options: RouteOptions<TTypes>;
158
243
  }
244
+
245
+ export type { StandardJSONSchemaV1, StandardSchemaV1, StandardTypedV1 };