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 +70 -0
- package/examples/serve-react/README.md +15 -0
- package/examples/serve-react/app.tsx +8 -0
- package/examples/serve-react/bun.lock +42 -0
- package/examples/serve-react/index.html +9 -0
- package/examples/serve-react/index.ts +27 -0
- package/examples/serve-react/package.json +17 -0
- package/examples/serve-react/tsconfig.json +29 -0
- package/package.json +1 -1
- package/src/core/bxo.ts +34 -14
- package/src/handlers/request-handler.ts +2 -1
- package/src/types/index.ts +1 -0
- package/src/utils/response-handler.ts +5 -5
- package/tests/unit/utils.test.ts +1 -1
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,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,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
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
|
-
|
|
233
|
+
// Merge user's serve options with framework defaults
|
|
234
|
+
const defaultServeOptions = {
|
|
232
235
|
port,
|
|
233
236
|
hostname,
|
|
234
|
-
fetch: (request, server) =>
|
|
235
|
-
|
|
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
|
-
|
|
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
|
package/src/types/index.ts
CHANGED
|
@@ -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)) {
|
package/tests/unit/utils.test.ts
CHANGED
|
@@ -182,7 +182,7 @@ describe('Utility Functions', () => {
|
|
|
182
182
|
|
|
183
183
|
it('should preserve special header casing', () => {
|
|
184
184
|
const headers = {
|
|
185
|
-
'
|
|
185
|
+
'WWW-Authenticate': 'Basic realm="example"',
|
|
186
186
|
'x-frame-options': 'DENY',
|
|
187
187
|
'content-type': 'application/json'
|
|
188
188
|
};
|