bxo 0.0.5-dev.64 → 0.0.5-dev.65

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 CHANGED
@@ -406,6 +406,76 @@ await app.stop();
406
406
  await app.listen(3000); // Same as app.start(3000)
407
407
  ```
408
408
 
409
+ ### Custom Serve Options
410
+
411
+ BXO allows you to extend the native Bun serve options, giving you full control over the server configuration while maintaining the framework's functionality:
412
+
413
+ ```typescript
414
+ // Basic serve options
415
+ const app = new BXO({
416
+ serve: {
417
+ port: 3000,
418
+ hostname: "0.0.0.0", // Listen on all interfaces
419
+ }
420
+ });
421
+
422
+ // With TLS/HTTPS options
423
+ const app = new BXO({
424
+ serve: {
425
+ port: 443,
426
+ hostname: "localhost",
427
+ // tls: {
428
+ // cert: Bun.file("cert.pem"),
429
+ // key: Bun.file("key.pem"),
430
+ // }
431
+ }
432
+ });
433
+
434
+ // With development options
435
+ const app = new BXO({
436
+ serve: {
437
+ port: 5000,
438
+ development: true, // Enable development mode
439
+ }
440
+ });
441
+
442
+ // With custom error handling
443
+ const app = new BXO({
444
+ serve: {
445
+ port: 6000,
446
+ error: (error) => {
447
+ console.error("Custom error handler:", error);
448
+ return new Response("Something went wrong", { status: 500 });
449
+ }
450
+ }
451
+ });
452
+
453
+ // With custom fetch handler (extends framework functionality)
454
+ const app = new BXO({
455
+ serve: {
456
+ port: 4000,
457
+ // You can override the fetch handler if needed
458
+ // fetch: (request, server) => {
459
+ // // Your custom logic here
460
+ // // You can still call the framework's handler
461
+ // return app.requestHandler.handleRequest(request, server);
462
+ // }
463
+ }
464
+ });
465
+ ```
466
+
467
+ **Available Serve Options:**
468
+ - `port` - Server port (default: 3000)
469
+ - `hostname` - Server hostname (default: 'localhost')
470
+ - `development` - Enable development mode
471
+ - `tls` - TLS/HTTPS configuration
472
+ - `error` - Custom error handler
473
+ - `fetch` - Custom fetch handler (overrides framework's handler)
474
+ - `websocket` - WebSocket configuration
475
+ - And all other [Bun.serve options](https://bun.sh/docs/api/http#bun-serve)
476
+
477
+ The framework will merge your serve options with its own defaults, ensuring that BXO's routing, middleware, and WebSocket functionality continue to work while allowing you to customize the server behavior.
478
+
409
479
  ### Development vs Production
410
480
 
411
481
  ```typescript
@@ -0,0 +1,15 @@
1
+ # serve-react
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run index.ts
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v1.2.19. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
@@ -0,0 +1,8 @@
1
+ import { createRoot } from 'react-dom/client';
2
+
3
+ function App() {
4
+ return <div>Hello World</div>;
5
+ }
6
+
7
+ const root = createRoot(document.getElementById('root') as any);
8
+ root.render(<App />);
@@ -0,0 +1,42 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "workspaces": {
4
+ "": {
5
+ "name": "serve-react",
6
+ "dependencies": {
7
+ "@types/react-dom": "^19.1.9",
8
+ "react": "^19.1.1",
9
+ "react-dom": "^19.1.1",
10
+ },
11
+ "devDependencies": {
12
+ "@types/bun": "latest",
13
+ },
14
+ "peerDependencies": {
15
+ "typescript": "^5",
16
+ },
17
+ },
18
+ },
19
+ "packages": {
20
+ "@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
21
+
22
+ "@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
23
+
24
+ "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="],
25
+
26
+ "@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="],
27
+
28
+ "bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
29
+
30
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
31
+
32
+ "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
33
+
34
+ "react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="],
35
+
36
+ "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
37
+
38
+ "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
39
+
40
+ "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
41
+ }
42
+ }
@@ -0,0 +1,9 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <body>
5
+ <div id="root"></div>
6
+ </body>
7
+ <script type="module" src="app.tsx"></script>
8
+
9
+ </html>
@@ -0,0 +1,27 @@
1
+ import BXO from "../../src";
2
+ import index from "./index.html"
3
+
4
+ // Create BXO instance with custom serve options
5
+ const app = new BXO({
6
+ serve: {
7
+ port: 4000,
8
+ routes: {
9
+ "/*": index
10
+ }
11
+ // You can add any Bun.serve options here
12
+ // For example, you can add custom headers, TLS options, etc.
13
+ // The framework will merge these with its own defaults
14
+ }
15
+ });
16
+
17
+ // Add routes
18
+ app.get("/", () => {
19
+ return index
20
+ });
21
+
22
+ app.get("/api/status", () => {
23
+ return { status: "ok", message: "BXO is running with custom serve options" };
24
+ });
25
+
26
+ // Start the server
27
+ app.start();
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "serve-react",
3
+ "module": "index.ts",
4
+ "type": "module",
5
+ "private": true,
6
+ "devDependencies": {
7
+ "@types/bun": "latest"
8
+ },
9
+ "peerDependencies": {
10
+ "typescript": "^5"
11
+ },
12
+ "dependencies": {
13
+ "@types/react-dom": "^19.1.9",
14
+ "react": "^19.1.1",
15
+ "react-dom": "^19.1.1"
16
+ }
17
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext", "DOM"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }
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.64",
4
+ "version": "0.0.5-dev.65",
5
5
  "description": "A simple and lightweight web framework for Bun",
6
6
  "type": "module",
7
7
  "exports": {
package/src/core/bxo.ts CHANGED
@@ -1,12 +1,12 @@
1
- import type {
2
- Route,
3
- WSRoute,
4
- Handler,
5
- WebSocketHandler,
6
- RouteConfig,
7
- LifecycleHooks,
8
- Plugin,
9
- BXOOptions
1
+ import type {
2
+ Route,
3
+ WSRoute,
4
+ Handler,
5
+ WebSocketHandler,
6
+ RouteConfig,
7
+ LifecycleHooks,
8
+ Plugin,
9
+ BXOOptions
10
10
  } from '../types';
11
11
  import { RequestHandler } from '../handlers/request-handler';
12
12
 
@@ -22,9 +22,11 @@ export default class BXO {
22
22
  private serverHostname?: string;
23
23
  private enableValidation: boolean = true;
24
24
  private requestHandler: RequestHandler;
25
+ private serveOptions?: Partial<Bun.ServeFunctionOptions<any, any>>;
25
26
 
26
27
  constructor(options?: BXOOptions) {
27
28
  this.enableValidation = options?.enableValidation ?? true;
29
+ this.serveOptions = options?.serve;
28
30
  this.requestHandler = new RequestHandler(
29
31
  this._routes,
30
32
  this._wsRoutes,
@@ -228,11 +230,27 @@ export default class BXO {
228
230
  await this.hooks.onBeforeStart(this);
229
231
  }
230
232
 
231
- this.server = Bun.serve({
233
+ // Merge user's serve options with framework defaults
234
+ const defaultServeOptions = {
232
235
  port,
233
236
  hostname,
234
- fetch: (request, server) => this.requestHandler.handleRequest(request, server),
235
- websocket: {
237
+ fetch: async (request: Request, server: any): Promise<Response> => {
238
+ const result = await this.requestHandler.handleRequest(request, server);
239
+ return result || new Response('Not Found', { status: 404 });
240
+ },
241
+ };
242
+
243
+ // Merge user options with defaults, allowing user options to override
244
+ const finalServeOptions = {
245
+ ...defaultServeOptions,
246
+ ...this.serveOptions,
247
+ // Ensure our fetch handler is always used unless user explicitly overrides
248
+ fetch: this.serveOptions?.fetch || defaultServeOptions.fetch,
249
+ };
250
+
251
+ // Add websocket support if there are WebSocket routes
252
+ if (this.getAllWSRoutes().length > 0) {
253
+ (finalServeOptions as any).websocket = {
236
254
  message: (ws: any, message: any) => {
237
255
  const handler = ws.data?.handler;
238
256
  if (handler?.onMessage) {
@@ -251,8 +269,10 @@ export default class BXO {
251
269
  handler.onClose(ws, code, reason);
252
270
  }
253
271
  }
254
- }
255
- });
272
+ };
273
+ }
274
+
275
+ this.server = Bun.serve(finalServeOptions as any);
256
276
 
257
277
  // Verify server was created successfully
258
278
  if (!this.server) {
@@ -76,7 +76,8 @@ export class RequestHandler {
76
76
 
77
77
  // Process and return response
78
78
  const internalCookies = getInternalCookies(ctx);
79
- return processResponse(response, ctx, internalCookies, this.enableValidation, route.config);
79
+ const processedResponse = processResponse(response, ctx, internalCookies, this.enableValidation, route.config);
80
+ return processedResponse;
80
81
 
81
82
  } catch (error) {
82
83
  // Run error hooks
@@ -140,6 +140,7 @@ export interface LifecycleHooks {
140
140
  // BXO options interface
141
141
  export interface BXOOptions {
142
142
  enableValidation?: boolean;
143
+ serve?: Partial<Bun.ServeFunctionOptions<any, any>>;
143
144
  }
144
145
 
145
146
  // Plugin interface for middleware-style plugins
@@ -13,7 +13,7 @@ export function processResponse(
13
13
  // automatically create a redirect Response so users can call ctx.redirect(...) or set headers without returning.
14
14
  const hasImplicitRedirectIntent = !!ctx.set.redirect
15
15
  || (typeof ctx.set.status === 'number' && ctx.set.status >= 300 && ctx.set.status < 400);
16
-
16
+
17
17
  if ((response === undefined || response === null) && hasImplicitRedirectIntent) {
18
18
  const locationFromHeaders = ctx.set.headers && Object.entries(ctx.set.headers).find(([k]) => k.toLowerCase() === 'location')?.[1];
19
19
  const location = ctx.set.redirect?.location || locationFromHeaders;
@@ -158,7 +158,7 @@ export function processResponse(
158
158
  }
159
159
  return value;
160
160
  }));
161
-
161
+
162
162
  headers['Content-Type'] = 'application/json';
163
163
  return new Response(JSON.stringify(serializableResponse), {
164
164
  status: ctx.set.status || 200,
@@ -223,7 +223,7 @@ export function processResponse(
223
223
  }
224
224
  return value;
225
225
  }));
226
-
226
+
227
227
  return new Response(JSON.stringify(serializableResponse), {
228
228
  ...responseInit,
229
229
  headers: {
@@ -253,7 +253,7 @@ export function createErrorResponse(
253
253
  error: errorMessage,
254
254
  ...(details && { details })
255
255
  };
256
-
256
+
257
257
  return new Response(JSON.stringify(response), {
258
258
  status,
259
259
  headers: { 'Content-Type': 'application/json' }
@@ -266,7 +266,7 @@ export function createValidationErrorResponse(
266
266
  status: number = 400
267
267
  ): Response {
268
268
  let validationDetails = undefined;
269
-
269
+
270
270
  // Handle Error objects
271
271
  if (validationError instanceof Error) {
272
272
  if ('errors' in validationError && Array.isArray(validationError.errors)) {
@@ -182,7 +182,7 @@ describe('Utility Functions', () => {
182
182
 
183
183
  it('should preserve special header casing', () => {
184
184
  const headers = {
185
- 'www-authenticate': 'Basic realm="example"',
185
+ 'WWW-Authenticate': 'Basic realm="example"',
186
186
  'x-frame-options': 'DENY',
187
187
  'content-type': 'application/json'
188
188
  };