@getvision/server 0.4.0-6e5c887-develop → 0.4.1-b47357d-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,32 @@
1
1
  # @getvision/server
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6e5c887: Migrate all adapters to use UniversalValidator supporting Zod, Valibot, and Standard Schema v1. The new validation system provides:
8
+ - Unified `validator()` function that works with any validation library
9
+ - Automatic error response formatting with proper issue paths
10
+ - Schema introspection for template generation
11
+ - Backward compatibility with existing zValidator (deprecated)
12
+
13
+ **Breaking changes:**
14
+ - `zValidator` is now deprecated in favor of universal `validator()`
15
+ - Error response format has been standardized across all adapters
16
+ - Some internal types have changed to support multiple validation libraries
17
+
18
+ Migration guide:
19
+
20
+ ```ts
21
+ // Before
22
+ import { zValidator } from "@getvision/adapter-hono";
23
+ app.post("/users", zValidator("json", userSchema));
24
+
25
+ // After (works with Zod, Valibot, or Standard Schema)
26
+ import { validator } from "@getvision/adapter-hono";
27
+ app.post("/users", validator("json", userSchema));
28
+ ```
29
+
3
30
  ## 0.3.6
4
31
 
5
32
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getvision/server",
3
- "version": "0.4.0-6e5c887-develop",
3
+ "version": "0.4.1-b47357d-develop",
4
4
  "type": "module",
5
5
  "description": "Vision Server - Meta-framework with built-in observability, pub/sub, and type-safe APIs",
6
6
  "exports": {
package/src/service.ts CHANGED
@@ -145,20 +145,42 @@ export class ServiceBuilder<
145
145
  public getRoutesMetadata(): Array<{
146
146
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
147
147
  path: string
148
+ queryParams?: any
148
149
  requestBody?: any
149
150
  responseBody?: any
150
151
  }> {
151
152
  const routes: Array<{
152
153
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
153
154
  path: string
155
+ queryParams?: any
154
156
  requestBody?: any
155
157
  responseBody?: any
156
158
  }> = []
157
159
  this.endpoints.forEach((ep) => {
158
160
  let requestBody = undefined
159
- if (ep.schema.input && ['POST', 'PUT', 'PATCH'].includes(ep.method)) {
160
- requestBody = generateTemplate(ep.schema.input)
161
+ let queryParams = undefined
162
+
163
+ if (ep.schema.input) {
164
+ if (['POST', 'PUT', 'PATCH'].includes(ep.method)) {
165
+ requestBody = generateTemplate(ep.schema.input)
166
+ } else if (ep.method === 'GET' || ep.method === 'DELETE') {
167
+ // Exclude path params from query params
168
+ const pathParamNames = (ep.path.match(/:([^/]+)/g) || []).map((p: string) => p.slice(1))
169
+ const fullTemplate = generateTemplate(ep.schema.input)
170
+
171
+ if (fullTemplate && pathParamNames.length > 0) {
172
+ const queryFields = fullTemplate.fields.filter(
173
+ (f: { name: string }) => !pathParamNames.includes(f.name)
174
+ )
175
+ if (queryFields.length > 0) {
176
+ queryParams = { ...fullTemplate, fields: queryFields }
177
+ }
178
+ } else {
179
+ queryParams = fullTemplate
180
+ }
181
+ }
161
182
  }
183
+
162
184
  let responseBody = undefined
163
185
  if (ep.schema.output) {
164
186
  responseBody = generateTemplate(ep.schema.output)
@@ -166,6 +188,7 @@ export class ServiceBuilder<
166
188
  routes.push({
167
189
  method: ep.method,
168
190
  path: ep.path,
191
+ queryParams,
169
192
  requestBody,
170
193
  responseBody,
171
194
  })
@@ -384,13 +407,34 @@ export class ServiceBuilder<
384
407
  build(app: Hono, servicesAccumulator?: Array<{ name: string; routes: any[] }>) {
385
408
  // Prepare routes with Zod schemas
386
409
  const routes = Array.from(this.endpoints.values()).map(ep => {
387
- // Generate requestBody schema (input)
410
+ // Generate requestBody schema (input) for POST/PUT/PATCH
388
411
  let requestBody = undefined
389
- if (ep.schema.input && ['POST', 'PUT', 'PATCH'].includes(ep.method)) {
390
- requestBody = generateTemplate(ep.schema.input)
412
+ let queryParams = undefined
413
+
414
+ if (ep.schema.input) {
415
+ if (['POST', 'PUT', 'PATCH'].includes(ep.method)) {
416
+ requestBody = generateTemplate(ep.schema.input)
417
+ } else if (ep.method === 'GET' || ep.method === 'DELETE') {
418
+ // For GET/DELETE, input schema represents query parameters
419
+ // BUT we need to exclude path params from query params
420
+ const pathParamNames = (ep.path.match(/:([^/]+)/g) || []).map((p: string) => p.slice(1))
421
+ const fullTemplate = generateTemplate(ep.schema.input)
422
+
423
+ if (fullTemplate && pathParamNames.length > 0) {
424
+ // Filter out path params from query params
425
+ const queryFields = fullTemplate.fields.filter(
426
+ (f: { name: string }) => !pathParamNames.includes(f.name)
427
+ )
428
+ if (queryFields.length > 0) {
429
+ queryParams = { ...fullTemplate, fields: queryFields }
430
+ }
431
+ } else {
432
+ queryParams = fullTemplate
433
+ }
434
+ }
391
435
  }
392
436
 
393
- // Generate responseBody schema (output) - NEW!
437
+ // Generate responseBody schema (output)
394
438
  let responseBody = undefined
395
439
  if (ep.schema.output) {
396
440
  responseBody = generateTemplate(ep.schema.output)
@@ -401,6 +445,7 @@ export class ServiceBuilder<
401
445
  path: ep.path,
402
446
  handler: this.name,
403
447
  middleware: [],
448
+ queryParams,
404
449
  requestBody,
405
450
  responseBody,
406
451
  }
@@ -525,8 +570,12 @@ export class ServiceBuilder<
525
570
  // Validate input with UniversalValidator (supports Zod, Valibot, etc.)
526
571
  const validated = UniversalValidator.parse(ep.schema.input, input)
527
572
 
573
+ // Merge back path params that are not in the schema
574
+ // This ensures path params like :id are always available to the handler
575
+ const finalInput = { ...params, ...(validated || {}) }
576
+
528
577
  // Execute handler
529
- const result = await ep.handler(validated, c as any)
578
+ const result = await ep.handler(finalInput, c as any)
530
579
 
531
580
  // If an output schema exists, validate and return JSON
532
581
  if (ep.schema.output) {
package/src/vision-app.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Hono } from 'hono'
2
- import type { Env, Schema, Input, MiddlewareHandler } from 'hono'
2
+ import type { Env, Schema } from 'hono'
3
3
  import { VisionCore, runInTraceContext } from '@getvision/core'
4
4
  import type { RouteMetadata } from '@getvision/core'
5
5
  import { AsyncLocalStorage } from 'async_hooks'
@@ -161,9 +161,7 @@ export class Vision<
161
161
  private eventBus: EventBus
162
162
  private config: VisionConfig
163
163
  private serviceBuilders: ServiceBuilder<any, E>[] = []
164
- private fileBasedRoutes: RouteMetadata[] = []
165
164
  private bunServer?: any
166
- private _cleanupPromise?: Promise<void>
167
165
  private signalHandler?: () => Promise<void>
168
166
 
169
167
  constructor(config?: VisionConfig) {
@@ -540,6 +538,7 @@ export class Vision<
540
538
  method: r.method,
541
539
  path: r.path,
542
540
  handler: name,
541
+ queryParams: r.queryParams,
543
542
  requestBody: r.requestBody,
544
543
  responseBody: r.responseBody,
545
544
  }))
@@ -559,31 +558,6 @@ export class Vision<
559
558
  builder.build(this as any, allServices)
560
559
  }
561
560
 
562
- // Group file-based routes by path prefix (e.g., /products, /analytics)
563
- if (this.fileBasedRoutes.length > 0) {
564
- const groupedRoutes = new Map<string, RouteMetadata[]>()
565
-
566
- for (const route of this.fileBasedRoutes) {
567
- // Extract first path segment as service name
568
- const segments = route.path.split('/').filter(s => s && !s.startsWith(':'))
569
- const serviceName = segments[0] || 'root'
570
-
571
- if (!groupedRoutes.has(serviceName)) {
572
- groupedRoutes.set(serviceName, [])
573
- }
574
- groupedRoutes.get(serviceName)!.push(route)
575
- }
576
-
577
- // Add each group as a service
578
- for (const [name, routes] of groupedRoutes.entries()) {
579
- const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1)
580
- allServices.push({
581
- name: capitalizedName,
582
- routes
583
- })
584
- }
585
- }
586
-
587
561
  // Don't register to VisionCore here - let start() handle it after sub-apps are loaded
588
562
  // Just return allServices so they can be registered later
589
563
  return allServices