bxo 0.0.5-dev.22 → 0.0.5-dev.24

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 (3) hide show
  1. package/README.md +75 -0
  2. package/index.ts +28 -9
  3. 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: (code: number, data?: any) => any;
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']> | any> | InferResponseType<TConfig['response']> | any;
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
- for (const [key, value] of searchParams.entries()) {
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
- for (const [key, value] of headers.entries()) {
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
@@ -623,7 +643,6 @@ export default class BXO {
623
643
  // Validate response against schema if provided
624
644
  if (route.config?.response && !(response instanceof Response)) {
625
645
  try {
626
- console.log('response', response);
627
646
  const status = ctx.set.status || 200;
628
647
  response = this.validateResponse(route.config.response, response, status);
629
648
  } catch (validationError) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bxo",
3
3
  "module": "index.ts",
4
- "version": "0.0.5-dev.22",
4
+ "version": "0.0.5-dev.24",
5
5
  "description": "A simple and lightweight web framework for Bun",
6
6
  "type": "module",
7
7
  "devDependencies": {