@getvision/adapter-fastify 0.0.10 → 0.1.0-6e5c887-develop

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @getvision/adapter-fastify
2
2
 
3
+ ## 0.0.11
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [d5bfbe0]
8
+ - @getvision/core@0.1.0
9
+
3
10
  ## 0.0.10
4
11
 
5
12
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -15,4 +15,5 @@ export declare function enableAutoDiscovery(fastify: FastifyInstance, options?:
15
15
  services?: ServiceDefinition[];
16
16
  }): void;
17
17
  export { generateZodTemplate } from '@getvision/core';
18
+ export { validator, toFastifySchema } from './validator';
18
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAElE,OAAO,EACL,UAAU,EAOX,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAIV,KAAK,EACL,oBAAoB,EACpB,iBAAiB,EAClB,MAAM,iBAAiB,CAAA;AAGxB,UAAU,aAAa;IACrB,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,KAAK,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAMhD;AAED,wBAAgB,aAAa,KAInB,CAAC,EACP,MAAM,MAAM,EACZ,YAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,YAAK,EACpC,IAAI,MAAM,CAAC,KACV,CAAC,CAgCL;AAKD,wBAAgB,iBAAiB,IAAI,UAAU,GAAG,IAAI,CAErD;AAqPD,eAAO,MAAM,YAAY,0CAGvB,CAAA;AAEF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAAE,GAC3C,IAAI,CAgEN;AAmGD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAElE,OAAO,EACL,UAAU,EASX,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAIV,KAAK,EACL,oBAAoB,EACpB,iBAAiB,EAClB,MAAM,iBAAiB,CAAA;AAGxB,UAAU,aAAa;IACrB,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,KAAK,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAMhD;AAED,wBAAgB,aAAa,KAInB,CAAC,EACP,MAAM,MAAM,EACZ,YAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,YAAK,EACpC,IAAI,MAAM,CAAC,KACV,CAAC,CAgCL;AAKD,wBAAgB,iBAAiB,IAAI,UAAU,GAAG,IAAI,CAErD;AAyQD,eAAO,MAAM,YAAY,0CAGvB,CAAA;AAEF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAAE,GAC3C,IAAI,CAsEN;AA+FD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import fp from 'fastify-plugin';
2
- import { VisionCore, autoDetectPackageInfo, autoDetectIntegrations, detectDrizzle, startDrizzleStudio, stopDrizzleStudio, traceContext, } from '@getvision/core';
2
+ import { VisionCore, autoDetectPackageInfo, autoDetectIntegrations, detectDrizzle, startDrizzleStudio, stopDrizzleStudio, generateTemplate, traceContext, } from '@getvision/core';
3
3
  import { fastifyRequestContext, requestContext } from '@fastify/request-context';
4
4
  export function getVisionContext() {
5
5
  const ctx = requestContext.get('visionTrace');
@@ -124,17 +124,35 @@ const visionPluginImpl = async (fastify, options) => {
124
124
  });
125
125
  });
126
126
  const CAPTURE_KEY = Symbol.for('vision.fastify.routes');
127
- const captured = (fastify[CAPTURE_KEY] = fastify[CAPTURE_KEY] || []);
128
127
  fastify.addHook('onRoute', (routeOpts) => {
128
+ if (!fastify[CAPTURE_KEY]) {
129
+ fastify[CAPTURE_KEY] = [];
130
+ }
131
+ const captured = fastify[CAPTURE_KEY];
129
132
  const methods = Array.isArray(routeOpts.method) ? routeOpts.method : [routeOpts.method];
133
+ // Extract schema from preHandler validator if present
134
+ let visionSchema = undefined;
135
+ if (routeOpts.preHandler) {
136
+ const handlers = Array.isArray(routeOpts.preHandler) ? routeOpts.preHandler : [routeOpts.preHandler];
137
+ for (const handler of handlers) {
138
+ if (handler.__visionSchema) {
139
+ visionSchema = handler.__visionSchema;
140
+ break;
141
+ }
142
+ }
143
+ }
130
144
  for (const m of methods) {
131
145
  const method = (m || '').toString().toUpperCase();
132
146
  if (!method || method === 'HEAD' || method === 'OPTIONS')
133
147
  continue;
148
+ const schema = routeOpts.schema ? { ...routeOpts.schema } : {};
149
+ if (visionSchema) {
150
+ schema.__visionSchema = visionSchema;
151
+ }
134
152
  captured.push({
135
153
  method,
136
154
  url: routeOpts.url,
137
- schema: routeOpts.schema,
155
+ schema,
138
156
  handlerName: routeOpts.handler?.name || 'anonymous',
139
157
  });
140
158
  }
@@ -264,7 +282,15 @@ export function enableAutoDiscovery(fastify, options) {
264
282
  handler: route.handlerName || 'anonymous',
265
283
  };
266
284
  // Try to get schema from route
267
- if (route.schema?.body) {
285
+ if (route.schema?.__visionSchema) {
286
+ try {
287
+ routeMeta.requestBody = generateTemplate(route.schema.__visionSchema);
288
+ }
289
+ catch (e) {
290
+ console.error(`[Vision] Template generation error for ${route.method} ${route.url}:`, e);
291
+ }
292
+ }
293
+ else if (route.schema?.body) {
268
294
  try {
269
295
  routeMeta.requestBody = jsonSchemaToTemplate(route.schema.body);
270
296
  }
@@ -340,11 +366,8 @@ function jsonSchemaToTemplate(schema) {
340
366
  default:
341
367
  value = 'null';
342
368
  }
343
- const comment = description
344
- ? ` // ${description}${isRequired ? '' : ' (optional)'}`
345
- : (isRequired ? '' : ' // optional');
346
369
  const comma = index < props.length - 1 ? ',' : '';
347
- lines.push(` "${key}": ${value}${comma}${comment}`);
370
+ lines.push(` "${key}": ${value}${comma}`);
348
371
  fields.push({
349
372
  name: key,
350
373
  type,
@@ -388,3 +411,4 @@ function matchPattern(path, pattern) {
388
411
  return path === pattern;
389
412
  }
390
413
  export { generateZodTemplate } from '@getvision/core';
414
+ export { validator, toFastifySchema } from './validator';
@@ -0,0 +1,69 @@
1
+ import type { FastifySchema, FastifyRequest, FastifyReply } from 'fastify';
2
+ import type { StandardSchemaV1, ValidationSchema } from '@getvision/core';
3
+ /**
4
+ * Convert a Standard Schema to Fastify schema format
5
+ * This allows using any validation library with Fastify's built-in validation
6
+ */
7
+ export declare function toFastifySchema(schema: {
8
+ body?: ValidationSchema;
9
+ querystring?: ValidationSchema;
10
+ params?: ValidationSchema;
11
+ headers?: ValidationSchema;
12
+ response?: {
13
+ [statusCode: number]: ValidationSchema;
14
+ };
15
+ }): FastifySchema;
16
+ /**
17
+ * Universal validator for Fastify routes
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { validator } from '@getvision/adapter-fastify'
22
+ * import { z } from 'zod'
23
+ *
24
+ * const userSchema = z.object({
25
+ * name: z.string(),
26
+ * email: z.string().email()
27
+ * })
28
+ *
29
+ * fastify.post('/users', {
30
+ * preHandler: validator('body', userSchema)
31
+ * }, async (request, reply) => {
32
+ * // request.body is now validated
33
+ * return { success: true }
34
+ * })
35
+ * ```
36
+ */
37
+ export declare function validator<S extends import('zod').ZodTypeAny>(target: 'body', schema: S): (request: FastifyRequest & {
38
+ body: import('zod').infer<S>;
39
+ }, reply: FastifyReply) => Promise<void>;
40
+ export declare function validator<S extends import('zod').ZodTypeAny>(target: 'querystring', schema: S): (request: FastifyRequest & {
41
+ query: import('zod').infer<S>;
42
+ }, reply: FastifyReply) => Promise<void>;
43
+ export declare function validator<S extends import('zod').ZodTypeAny>(target: 'params', schema: S): (request: FastifyRequest & {
44
+ params: import('zod').infer<S>;
45
+ }, reply: FastifyReply) => Promise<void>;
46
+ export declare function validator<S extends import('zod').ZodTypeAny>(target: 'headers', schema: S): (request: FastifyRequest & {
47
+ headers: import('zod').infer<S>;
48
+ }, reply: FastifyReply) => Promise<void>;
49
+ export declare function validator<S extends StandardSchemaV1<any, any>>(target: 'body', schema: S): (request: FastifyRequest & {
50
+ body: StandardSchemaV1.Infer<S>['output'];
51
+ }, reply: FastifyReply) => Promise<void>;
52
+ export declare function validator<S extends StandardSchemaV1<any, any>>(target: 'querystring', schema: S): (request: FastifyRequest & {
53
+ query: StandardSchemaV1.Infer<S>['output'];
54
+ }, reply: FastifyReply) => Promise<void>;
55
+ export declare function validator<S extends StandardSchemaV1<any, any>>(target: 'params', schema: S): (request: FastifyRequest & {
56
+ params: StandardSchemaV1.Infer<S>['output'];
57
+ }, reply: FastifyReply) => Promise<void>;
58
+ export declare function validator<S extends StandardSchemaV1<any, any>>(target: 'headers', schema: S): (request: FastifyRequest & {
59
+ headers: StandardSchemaV1.Infer<S>['output'];
60
+ }, reply: FastifyReply) => Promise<void>;
61
+ export declare function validator(target: 'body', schema: ValidationSchema): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
62
+ export declare function validator(target: 'querystring', schema: ValidationSchema): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
63
+ export declare function validator(target: 'params', schema: ValidationSchema): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
64
+ export declare function validator(target: 'headers', schema: ValidationSchema): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
65
+ /**
66
+ * Extract schema from route configuration
67
+ */
68
+ export declare function extractSchema(routeOptions: any): ValidationSchema | undefined;
69
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { ValidationError, createValidationErrorResponse, UniversalValidator } from '@getvision/core';
2
+ /**
3
+ * Convert a Standard Schema to Fastify schema format
4
+ * This allows using any validation library with Fastify's built-in validation
5
+ */
6
+ export function toFastifySchema(schema) {
7
+ const fastifySchema = {};
8
+ // Note: Fastify has its own validation system
9
+ // This is a bridge to allow Standard Schema libraries to work with Fastify
10
+ // In practice, you might want to use Fastify's native validation or override it
11
+ return fastifySchema;
12
+ }
13
+ export function validator(target, schema) {
14
+ const handler = async (request, reply) => {
15
+ let data;
16
+ // Extract data based on target
17
+ switch (target) {
18
+ case 'body':
19
+ data = request.body;
20
+ break;
21
+ case 'querystring':
22
+ data = request.query;
23
+ break;
24
+ case 'params':
25
+ data = request.params;
26
+ break;
27
+ case 'headers':
28
+ data = request.headers;
29
+ break;
30
+ }
31
+ try {
32
+ // Validate data using UniversalValidator
33
+ const validated = UniversalValidator.parse(schema, data);
34
+ // Store validated data back
35
+ switch (target) {
36
+ case 'body':
37
+ request.body = validated;
38
+ break;
39
+ case 'querystring':
40
+ request.query = validated;
41
+ break;
42
+ case 'params':
43
+ request.params = validated;
44
+ break;
45
+ case 'headers':
46
+ // Fastify headers are read-only, but we can extend the request object
47
+ Object.assign(request.headers, validated);
48
+ break;
49
+ }
50
+ }
51
+ catch (error) {
52
+ if (error instanceof ValidationError) {
53
+ const requestId = request.headers['x-request-id'];
54
+ return reply.status(400).send(createValidationErrorResponse(error.issues, requestId));
55
+ }
56
+ throw error;
57
+ }
58
+ };
59
+ handler.__visionSchema = schema;
60
+ handler.__visionTarget = target;
61
+ return handler;
62
+ }
63
+ /**
64
+ * Extract schema from route configuration
65
+ */
66
+ export function extractSchema(routeOptions) {
67
+ return routeOptions?.schema?.__visionSchema;
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getvision/adapter-fastify",
3
- "version": "0.0.10",
3
+ "version": "0.1.0-6e5c887-develop",
4
4
  "description": "Fastify adapter for Vision Dashboard",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -26,7 +26,7 @@
26
26
  "dependencies": {
27
27
  "@fastify/request-context": "^6.2.1",
28
28
  "fastify-plugin": "^5.1.0",
29
- "@getvision/core": "0.0.8"
29
+ "@getvision/core": "0.1.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "fastify": "^5.6.1",
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@ import {
7
7
  detectDrizzle,
8
8
  startDrizzleStudio,
9
9
  stopDrizzleStudio,
10
+ generateTemplate,
11
+ runInTraceContext,
10
12
  traceContext,
11
13
  } from '@getvision/core'
12
14
  import type {
@@ -189,18 +191,38 @@ const visionPluginImpl: FastifyPluginAsync<VisionFastifyOptions> = async (fastif
189
191
  })
190
192
 
191
193
  const CAPTURE_KEY = Symbol.for('vision.fastify.routes')
192
- const captured: Array<{ method: string; url: string; schema?: any; handlerName?: string }> =
193
- ((fastify as any)[CAPTURE_KEY] = (fastify as any)[CAPTURE_KEY] || [])
194
-
195
- fastify.addHook('onRoute', (routeOpts: any) => {
194
+ fastify.addHook('onRoute', (routeOpts) => {
195
+ if (!(fastify as any)[CAPTURE_KEY]) {
196
+ (fastify as any)[CAPTURE_KEY] = []
197
+ }
198
+ const captured = (fastify as any)[CAPTURE_KEY]
196
199
  const methods = Array.isArray(routeOpts.method) ? routeOpts.method : [routeOpts.method]
200
+
201
+ // Extract schema from preHandler validator if present
202
+ let visionSchema: any = undefined
203
+ if (routeOpts.preHandler) {
204
+ const handlers = Array.isArray(routeOpts.preHandler) ? routeOpts.preHandler : [routeOpts.preHandler]
205
+ for (const handler of handlers) {
206
+ if ((handler as any).__visionSchema) {
207
+ visionSchema = (handler as any).__visionSchema
208
+ break
209
+ }
210
+ }
211
+ }
212
+
197
213
  for (const m of methods) {
198
214
  const method = (m || '').toString().toUpperCase()
199
215
  if (!method || method === 'HEAD' || method === 'OPTIONS') continue
216
+
217
+ const schema: any = routeOpts.schema ? { ...routeOpts.schema } : {}
218
+ if (visionSchema) {
219
+ schema.__visionSchema = visionSchema
220
+ }
221
+
200
222
  captured.push({
201
223
  method,
202
224
  url: routeOpts.url as string,
203
- schema: routeOpts.schema,
225
+ schema,
204
226
  handlerName: routeOpts.handler?.name || 'anonymous',
205
227
  })
206
228
  }
@@ -357,7 +379,13 @@ export function enableAutoDiscovery(
357
379
  }
358
380
 
359
381
  // Try to get schema from route
360
- if (route.schema?.body) {
382
+ if (route.schema?.__visionSchema) {
383
+ try {
384
+ routeMeta.requestBody = generateTemplate(route.schema.__visionSchema)
385
+ } catch (e) {
386
+ console.error(`[Vision] Template generation error for ${route.method} ${route.url}:`, e)
387
+ }
388
+ } else if (route.schema?.body) {
361
389
  try {
362
390
  routeMeta.requestBody = jsonSchemaToTemplate(route.schema.body)
363
391
  } catch (e) {
@@ -442,12 +470,8 @@ function jsonSchemaToTemplate(schema: any): RequestBodySchema {
442
470
  value = 'null'
443
471
  }
444
472
 
445
- const comment = description
446
- ? ` // ${description}${isRequired ? '' : ' (optional)'}`
447
- : (isRequired ? '' : ' // optional')
448
-
449
473
  const comma = index < props.length - 1 ? ',' : ''
450
- lines.push(` "${key}": ${value}${comma}${comment}`)
474
+ lines.push(` "${key}": ${value}${comma}`)
451
475
 
452
476
  fields.push({
453
477
  name: key,
@@ -497,4 +521,6 @@ function matchPattern(path: string, pattern: string): boolean {
497
521
  return path === pattern
498
522
  }
499
523
 
500
- export { generateZodTemplate } from '@getvision/core'
524
+ export { generateZodTemplate } from '@getvision/core'
525
+
526
+ export { validator, toFastifySchema } from './validator'
@@ -0,0 +1,177 @@
1
+ import type { FastifySchema, FastifyRequest, FastifyReply } from 'fastify'
2
+ import type { StandardSchemaV1, ValidationSchema } from '@getvision/core'
3
+ import {
4
+ ValidationError,
5
+ createValidationErrorResponse,
6
+ UniversalValidator
7
+ } from '@getvision/core'
8
+
9
+ /**
10
+ * Convert a Standard Schema to Fastify schema format
11
+ * This allows using any validation library with Fastify's built-in validation
12
+ */
13
+ export function toFastifySchema(schema: {
14
+ body?: ValidationSchema
15
+ querystring?: ValidationSchema
16
+ params?: ValidationSchema
17
+ headers?: ValidationSchema
18
+ response?: {
19
+ [statusCode: number]: ValidationSchema
20
+ }
21
+ }): FastifySchema {
22
+ const fastifySchema: FastifySchema = {}
23
+
24
+ // Note: Fastify has its own validation system
25
+ // This is a bridge to allow Standard Schema libraries to work with Fastify
26
+ // In practice, you might want to use Fastify's native validation or override it
27
+
28
+ return fastifySchema
29
+ }
30
+
31
+ /**
32
+ * Universal validator for Fastify routes
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * import { validator } from '@getvision/adapter-fastify'
37
+ * import { z } from 'zod'
38
+ *
39
+ * const userSchema = z.object({
40
+ * name: z.string(),
41
+ * email: z.string().email()
42
+ * })
43
+ *
44
+ * fastify.post('/users', {
45
+ * preHandler: validator('body', userSchema)
46
+ * }, async (request, reply) => {
47
+ * // request.body is now validated
48
+ * return { success: true }
49
+ * })
50
+ * ```
51
+ */
52
+ export function validator<S extends import('zod').ZodTypeAny>(
53
+ target: 'body',
54
+ schema: S
55
+ ): (request: FastifyRequest & { body: import('zod').infer<S> }, reply: FastifyReply) => Promise<void>
56
+
57
+ export function validator<S extends import('zod').ZodTypeAny>(
58
+ target: 'querystring',
59
+ schema: S
60
+ ): (request: FastifyRequest & { query: import('zod').infer<S> }, reply: FastifyReply) => Promise<void>
61
+
62
+ export function validator<S extends import('zod').ZodTypeAny>(
63
+ target: 'params',
64
+ schema: S
65
+ ): (request: FastifyRequest & { params: import('zod').infer<S> }, reply: FastifyReply) => Promise<void>
66
+
67
+ export function validator<S extends import('zod').ZodTypeAny>(
68
+ target: 'headers',
69
+ schema: S
70
+ ): (request: FastifyRequest & { headers: import('zod').infer<S> }, reply: FastifyReply) => Promise<void>
71
+
72
+ export function validator<S extends StandardSchemaV1<any, any>>(
73
+ target: 'body',
74
+ schema: S
75
+ ): (request: FastifyRequest & { body: StandardSchemaV1.Infer<S>['output'] }, reply: FastifyReply) => Promise<void>
76
+
77
+ export function validator<S extends StandardSchemaV1<any, any>>(
78
+ target: 'querystring',
79
+ schema: S
80
+ ): (request: FastifyRequest & { query: StandardSchemaV1.Infer<S>['output'] }, reply: FastifyReply) => Promise<void>
81
+
82
+ export function validator<S extends StandardSchemaV1<any, any>>(
83
+ target: 'params',
84
+ schema: S
85
+ ): (request: FastifyRequest & { params: StandardSchemaV1.Infer<S>['output'] }, reply: FastifyReply) => Promise<void>
86
+
87
+ export function validator<S extends StandardSchemaV1<any, any>>(
88
+ target: 'headers',
89
+ schema: S
90
+ ): (request: FastifyRequest & { headers: StandardSchemaV1.Infer<S>['output'] }, reply: FastifyReply) => Promise<void>
91
+
92
+ export function validator(
93
+ target: 'body',
94
+ schema: ValidationSchema
95
+ ): (request: FastifyRequest, reply: FastifyReply) => Promise<void>
96
+
97
+ export function validator(
98
+ target: 'querystring',
99
+ schema: ValidationSchema
100
+ ): (request: FastifyRequest, reply: FastifyReply) => Promise<void>
101
+
102
+ export function validator(
103
+ target: 'params',
104
+ schema: ValidationSchema
105
+ ): (request: FastifyRequest, reply: FastifyReply) => Promise<void>
106
+
107
+ export function validator(
108
+ target: 'headers',
109
+ schema: ValidationSchema
110
+ ): (request: FastifyRequest, reply: FastifyReply) => Promise<void>
111
+
112
+ export function validator(
113
+ target: 'body' | 'querystring' | 'params' | 'headers',
114
+ schema: ValidationSchema
115
+ ) {
116
+ const handler = async (request: FastifyRequest, reply: FastifyReply) => {
117
+ let data: unknown
118
+
119
+ // Extract data based on target
120
+ switch (target) {
121
+ case 'body':
122
+ data = request.body
123
+ break
124
+ case 'querystring':
125
+ data = request.query
126
+ break
127
+ case 'params':
128
+ data = request.params
129
+ break
130
+ case 'headers':
131
+ data = request.headers
132
+ break
133
+ }
134
+
135
+ try {
136
+ // Validate data using UniversalValidator
137
+ const validated = UniversalValidator.parse(schema, data)
138
+
139
+ // Store validated data back
140
+ switch (target) {
141
+ case 'body':
142
+ request.body = validated
143
+ break
144
+ case 'querystring':
145
+ request.query = validated as any
146
+ break
147
+ case 'params':
148
+ request.params = validated as any
149
+ break
150
+ case 'headers':
151
+ // Fastify headers are read-only, but we can extend the request object
152
+ Object.assign(request.headers, validated)
153
+ break
154
+ }
155
+ } catch (error) {
156
+ if (error instanceof ValidationError) {
157
+ const requestId = request.headers['x-request-id'] as string
158
+ return reply.status(400).send(
159
+ createValidationErrorResponse(error.issues, requestId)
160
+ )
161
+ }
162
+ throw error
163
+ }
164
+ }
165
+
166
+ (handler as any).__visionSchema = schema;
167
+ (handler as any).__visionTarget = target;
168
+
169
+ return handler
170
+ }
171
+
172
+ /**
173
+ * Extract schema from route configuration
174
+ */
175
+ export function extractSchema(routeOptions: any): ValidationSchema | undefined {
176
+ return routeOptions?.schema?.__visionSchema
177
+ }