bxo 0.0.5-dev.32 → 0.0.5-dev.33

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 (2) hide show
  1. package/index.ts +80 -2
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -86,6 +86,7 @@ export type Context<TConfig extends RouteConfig = {}> = {
86
86
  : TConfig['response'] extends ResponseSchema
87
87
  ? InferZodType<TConfig['response']>
88
88
  : any;
89
+ redirect: (location: string, status?: number) => Response;
89
90
  [key: string]: any;
90
91
  };
91
92
 
@@ -597,7 +598,41 @@ export default class BXO {
597
598
  status: ((code: number, data?: any) => {
598
599
  ctx.set.status = code;
599
600
  return data;
600
- }) as any
601
+ }) as any,
602
+ redirect: ((location: string, status: number = 302) => {
603
+ // Persist redirect intent on context so it also works without returning
604
+ ctx.set.status = status;
605
+ ctx.set.headers = {
606
+ ...(ctx.set.headers || {}),
607
+ Location: location
608
+ };
609
+
610
+ // Prepare headers for immediate Response return (merging any existing headers)
611
+ const responseHeaders: Record<string, string> = { ...ctx.set.headers };
612
+
613
+ // Handle cookies if any are set on context
614
+ if (ctx.set.cookies && ctx.set.cookies.length > 0) {
615
+ const cookieHeaders = ctx.set.cookies.map(cookie => {
616
+ let cookieString = `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`;
617
+ if (cookie.domain) cookieString += `; Domain=${cookie.domain}`;
618
+ if (cookie.path) cookieString += `; Path=${cookie.path}`;
619
+ if (cookie.expires) cookieString += `; Expires=${cookie.expires.toUTCString()}`;
620
+ if (cookie.maxAge) cookieString += `; Max-Age=${cookie.maxAge}`;
621
+ if (cookie.secure) cookieString += `; Secure`;
622
+ if (cookie.httpOnly) cookieString += `; HttpOnly`;
623
+ if (cookie.sameSite) cookieString += `; SameSite=${cookie.sameSite}`;
624
+ return cookieString;
625
+ });
626
+ cookieHeaders.forEach((cookieHeader, index) => {
627
+ responseHeaders[index === 0 ? 'Set-Cookie' : `Set-Cookie-${index}`] = cookieHeader;
628
+ });
629
+ }
630
+
631
+ return new Response(null, {
632
+ status,
633
+ headers: responseHeaders
634
+ });
635
+ }) as any
601
636
  };
602
637
  } catch (validationError) {
603
638
  // Validation failed - return error response
@@ -654,6 +689,41 @@ export default class BXO {
654
689
  }
655
690
  }
656
691
 
692
+ // If the handler did not return a response, but a redirect was configured via ctx.set,
693
+ // automatically create a redirect Response so users can call ctx.redirect(...) or set headers without returning.
694
+ if ((response === undefined || response === null)
695
+ && typeof ctx.set.status === 'number'
696
+ && ctx.set.status >= 300
697
+ && ctx.set.status < 400) {
698
+ const hasLocationHeader = !!(ctx.set.headers && Object.keys(ctx.set.headers).some(k => k.toLowerCase() === 'location'));
699
+ if (hasLocationHeader) {
700
+ let responseHeaders = ctx.set.headers ? { ...ctx.set.headers } : {};
701
+
702
+ // Handle cookies if any are set
703
+ if (ctx.set.cookies && ctx.set.cookies.length > 0) {
704
+ const cookieHeaders = ctx.set.cookies.map(cookie => {
705
+ let cookieString = `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`;
706
+ if (cookie.domain) cookieString += `; Domain=${cookie.domain}`;
707
+ if (cookie.path) cookieString += `; Path=${cookie.path}`;
708
+ if (cookie.expires) cookieString += `; Expires=${cookie.expires.toUTCString()}`;
709
+ if (cookie.maxAge) cookieString += `; Max-Age=${cookie.maxAge}`;
710
+ if (cookie.secure) cookieString += `; Secure`;
711
+ if (cookie.httpOnly) cookieString += `; HttpOnly`;
712
+ if (cookie.sameSite) cookieString += `; SameSite=${cookie.sameSite}`;
713
+ return cookieString;
714
+ });
715
+ cookieHeaders.forEach((cookieHeader, index) => {
716
+ responseHeaders[index === 0 ? 'Set-Cookie' : `Set-Cookie-${index}`] = cookieHeader;
717
+ });
718
+ }
719
+
720
+ return new Response(null, {
721
+ status: ctx.set.status,
722
+ headers: responseHeaders
723
+ });
724
+ }
725
+ }
726
+
657
727
  // Validate response against schema if provided
658
728
  if (this.enableValidation && route.config?.response && !(response instanceof Response)) {
659
729
  try {
@@ -1045,8 +1115,16 @@ const file = (path: string, options?: { type?: string; headers?: Record<string,
1045
1115
  return bunFile;
1046
1116
  }
1047
1117
 
1118
+ // Redirect helper function (like Elysia)
1119
+ const redirect = (location: string, status: number = 302) => {
1120
+ return new Response(null, {
1121
+ status,
1122
+ headers: { Location: location }
1123
+ });
1124
+ }
1125
+
1048
1126
  // Export Zod for convenience
1049
- export { z, error, file };
1127
+ export { z, error, file, redirect };
1050
1128
 
1051
1129
  // Export types for external use
1052
1130
  export type { RouteConfig, RouteDetail, Handler, WebSocketHandler, WSRoute, Cookie, BXOOptions };
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.32",
4
+ "version": "0.0.5-dev.33",
5
5
  "description": "A simple and lightweight web framework for Bun",
6
6
  "type": "module",
7
7
  "devDependencies": {