bxo 0.0.5-dev.22 → 0.0.5-dev.23
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 +75 -0
- package/index.ts +28 -8
- package/package.json +1 -1
package/README.md
CHANGED
@@ -93,11 +93,86 @@ interface Context<TConfig> {
|
|
93
93
|
status?: number;
|
94
94
|
headers?: Record<string, string>;
|
95
95
|
};
|
96
|
+
status: (code: number, data?: any) => any; // Type-safe status method
|
96
97
|
user?: any; // Added by auth plugin
|
97
98
|
[key: string]: any; // Extended by plugins
|
98
99
|
}
|
99
100
|
```
|
100
101
|
|
102
|
+
### Type-Safe Status Method
|
103
|
+
|
104
|
+
BXO provides a type-safe `ctx.status()` method similar to Elysia, which allows you to set HTTP status codes and return data in one call:
|
105
|
+
|
106
|
+
```typescript
|
107
|
+
// Simple usage
|
108
|
+
app.get('/hello', (ctx) => {
|
109
|
+
return ctx.status(200, { message: 'Hello World' });
|
110
|
+
});
|
111
|
+
|
112
|
+
// With response validation
|
113
|
+
app.get('/user/:id', (ctx) => {
|
114
|
+
const userId = ctx.params.id;
|
115
|
+
|
116
|
+
if (userId === 'not-found') {
|
117
|
+
// TypeScript suggests 404 as valid status
|
118
|
+
return ctx.status(404, { error: 'User not found' });
|
119
|
+
}
|
120
|
+
|
121
|
+
// TypeScript suggests 200 as valid status
|
122
|
+
return ctx.status(200, { user: { id: userId, name: 'John Doe' } });
|
123
|
+
}, {
|
124
|
+
response: {
|
125
|
+
200: z.object({
|
126
|
+
user: z.object({
|
127
|
+
id: z.string(),
|
128
|
+
name: z.string()
|
129
|
+
})
|
130
|
+
}),
|
131
|
+
404: z.object({
|
132
|
+
error: z.string()
|
133
|
+
})
|
134
|
+
}
|
135
|
+
});
|
136
|
+
|
137
|
+
// POST with validation and status responses
|
138
|
+
app.post('/users', (ctx) => {
|
139
|
+
const { name, email } = ctx.body;
|
140
|
+
|
141
|
+
if (!name || !email) {
|
142
|
+
return ctx.status(400, { error: 'Missing required fields' });
|
143
|
+
}
|
144
|
+
|
145
|
+
return ctx.status(201, {
|
146
|
+
success: true,
|
147
|
+
user: { id: 1, name, email }
|
148
|
+
});
|
149
|
+
}, {
|
150
|
+
body: z.object({
|
151
|
+
name: z.string(),
|
152
|
+
email: z.string().email()
|
153
|
+
}),
|
154
|
+
response: {
|
155
|
+
201: z.object({
|
156
|
+
success: z.boolean(),
|
157
|
+
user: z.object({
|
158
|
+
id: z.number(),
|
159
|
+
name: z.string(),
|
160
|
+
email: z.string()
|
161
|
+
})
|
162
|
+
}),
|
163
|
+
400: z.object({
|
164
|
+
error: z.string()
|
165
|
+
})
|
166
|
+
}
|
167
|
+
});
|
168
|
+
```
|
169
|
+
|
170
|
+
**Key Features:**
|
171
|
+
- **Type Safety**: Status codes are suggested based on your response configuration
|
172
|
+
- **Data Validation**: Return data is validated against the corresponding schema
|
173
|
+
- **Autocomplete**: TypeScript provides autocomplete for valid status codes
|
174
|
+
- **Return Type Inference**: Return types are properly inferred from schemas
|
175
|
+
|
101
176
|
### Validation Configuration
|
102
177
|
|
103
178
|
Each route can specify validation schemas for different parts of the request:
|
package/index.ts
CHANGED
@@ -51,6 +51,9 @@ interface RouteConfig {
|
|
51
51
|
detail?: RouteDetail;
|
52
52
|
}
|
53
53
|
|
54
|
+
// Helper type to extract status codes from response config
|
55
|
+
type StatusCodes<T> = T extends Record<number, any> ? keyof T : never;
|
56
|
+
|
54
57
|
// Context type that's fully typed based on the route configuration
|
55
58
|
export type Context<TConfig extends RouteConfig = {}> = {
|
56
59
|
params: TConfig['params'] extends z.ZodSchema<any> ? InferZodType<TConfig['params']> : Record<string, string>;
|
@@ -65,12 +68,29 @@ export type Context<TConfig extends RouteConfig = {}> = {
|
|
65
68
|
headers?: Record<string, string>;
|
66
69
|
cookies?: Cookie[];
|
67
70
|
};
|
68
|
-
status:
|
71
|
+
status: <T extends number>(
|
72
|
+
code: TConfig['response'] extends StatusResponseSchema
|
73
|
+
? StatusCodes<TConfig['response']> | number
|
74
|
+
: T,
|
75
|
+
data?: TConfig['response'] extends StatusResponseSchema
|
76
|
+
? T extends keyof TConfig['response']
|
77
|
+
? InferZodType<TConfig['response'][T]>
|
78
|
+
: any
|
79
|
+
: TConfig['response'] extends ResponseSchema
|
80
|
+
? InferZodType<TConfig['response']>
|
81
|
+
: any
|
82
|
+
) => TConfig['response'] extends StatusResponseSchema
|
83
|
+
? T extends keyof TConfig['response']
|
84
|
+
? InferZodType<TConfig['response'][T]>
|
85
|
+
: any
|
86
|
+
: TConfig['response'] extends ResponseSchema
|
87
|
+
? InferZodType<TConfig['response']>
|
88
|
+
: any;
|
69
89
|
[key: string]: any;
|
70
90
|
};
|
71
91
|
|
72
92
|
// Handler function type with proper response typing
|
73
|
-
type Handler<TConfig extends RouteConfig = {}, EC = {}> = (ctx: Context<TConfig> & EC) => Promise<InferResponseType<TConfig['response']
|
93
|
+
type Handler<TConfig extends RouteConfig = {}, EC = {}> = (ctx: Context<TConfig> & EC) => Promise<InferResponseType<TConfig['response']>> | InferResponseType<TConfig['response']>;
|
74
94
|
|
75
95
|
// Route definition
|
76
96
|
interface Route {
|
@@ -408,18 +428,18 @@ export default class BXO {
|
|
408
428
|
// Parse query string
|
409
429
|
private parseQuery(searchParams: URLSearchParams): Record<string, string | undefined> {
|
410
430
|
const query: Record<string, string | undefined> = {};
|
411
|
-
|
431
|
+
searchParams.forEach((value, key) => {
|
412
432
|
query[key] = value;
|
413
|
-
}
|
433
|
+
});
|
414
434
|
return query;
|
415
435
|
}
|
416
436
|
|
417
437
|
// Parse headers
|
418
438
|
private parseHeaders(headers: Headers): Record<string, string> {
|
419
439
|
const headerObj: Record<string, string> = {};
|
420
|
-
|
440
|
+
headers.forEach((value, key) => {
|
421
441
|
headerObj[key] = value;
|
422
|
-
}
|
442
|
+
});
|
423
443
|
return headerObj;
|
424
444
|
}
|
425
445
|
|
@@ -560,10 +580,10 @@ export default class BXO {
|
|
560
580
|
path: pathname,
|
561
581
|
request,
|
562
582
|
set: {},
|
563
|
-
status: (code: number, data?: any) => {
|
583
|
+
status: ((code: number, data?: any) => {
|
564
584
|
ctx.set.status = code;
|
565
585
|
return data;
|
566
|
-
}
|
586
|
+
}) as any
|
567
587
|
};
|
568
588
|
} catch (validationError) {
|
569
589
|
// Validation failed - return error response
|