bxo 0.0.5-dev.8 โ 0.0.5-dev.80
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 +258 -483
- package/example/cookie-example.ts +151 -0
- package/example/cors-example.ts +49 -0
- package/example/index.html +5 -0
- package/example/index.ts +191 -0
- package/example/multipart-example.ts +203 -0
- package/example/openapi-example.ts +132 -0
- package/example/passthrough-validation-example.ts +115 -0
- package/example/url-encoding-example.ts +93 -0
- package/example/websocket-example.ts +132 -0
- package/package.json +8 -8
- package/plugins/cors.ts +123 -73
- package/plugins/index.ts +2 -11
- package/plugins/openapi.ts +204 -0
- package/src/index.ts +960 -0
- package/test-url-encoding.ts +20 -0
- package/tsconfig.json +3 -5
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +0 -111
- package/example.ts +0 -183
- package/index.ts +0 -835
- package/plugins/auth.ts +0 -119
- package/plugins/logger.ts +0 -109
- package/plugins/ratelimit.ts +0 -140
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import BXO from "./src";
|
|
2
|
+
|
|
3
|
+
const app = new BXO({ serve: { port: 3001 } });
|
|
4
|
+
|
|
5
|
+
// Test the exact scenario you mentioned
|
|
6
|
+
app.get("/api/resources/:resourceType/:id", (ctx) => {
|
|
7
|
+
return ctx.json({
|
|
8
|
+
message: "Success!",
|
|
9
|
+
resourceType: ctx.params.resourceType,
|
|
10
|
+
id: ctx.params.id,
|
|
11
|
+
allParams: ctx.params,
|
|
12
|
+
url: ctx.request.url,
|
|
13
|
+
pathname: new URL(ctx.request.url).pathname
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
app.start();
|
|
18
|
+
console.log(`Test server running on http://localhost:${app.server?.port}`);
|
|
19
|
+
console.log(`Test URL: http://localhost:${app.server?.port}/api/resources/Doctype%20Permission/01992af8-1c69-7000-9219-9b83c2feb2d6`);
|
|
20
|
+
|
package/tsconfig.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
//
|
|
4
|
-
"lib": ["ESNext"],
|
|
3
|
+
// Enable latest features
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
5
|
"target": "ESNext",
|
|
6
|
-
"module": "
|
|
6
|
+
"module": "ESNext",
|
|
7
7
|
"moduleDetection": "force",
|
|
8
8
|
"jsx": "react-jsx",
|
|
9
9
|
"allowJs": true,
|
|
@@ -18,8 +18,6 @@
|
|
|
18
18
|
"strict": true,
|
|
19
19
|
"skipLibCheck": true,
|
|
20
20
|
"noFallthroughCasesInSwitch": true,
|
|
21
|
-
"noUncheckedIndexedAccess": true,
|
|
22
|
-
"noImplicitOverride": true,
|
|
23
21
|
|
|
24
22
|
// Some stricter flags (disabled by default)
|
|
25
23
|
"noUnusedLocals": false,
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Use Bun instead of Node.js, npm, pnpm, or vite.
|
|
3
|
-
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
Default to using Bun instead of Node.js.
|
|
8
|
-
|
|
9
|
-
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
10
|
-
- Use `bun test` instead of `jest` or `vitest`
|
|
11
|
-
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
12
|
-
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
13
|
-
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
14
|
-
- Bun automatically loads .env, so don't use dotenv.
|
|
15
|
-
|
|
16
|
-
## APIs
|
|
17
|
-
|
|
18
|
-
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
19
|
-
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
20
|
-
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
21
|
-
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
22
|
-
- `WebSocket` is built-in. Don't use `ws`.
|
|
23
|
-
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
24
|
-
- Bun.$`ls` instead of execa.
|
|
25
|
-
|
|
26
|
-
## Testing
|
|
27
|
-
|
|
28
|
-
Use `bun test` to run tests.
|
|
29
|
-
|
|
30
|
-
```ts#index.test.ts
|
|
31
|
-
import { test, expect } from "bun:test";
|
|
32
|
-
|
|
33
|
-
test("hello world", () => {
|
|
34
|
-
expect(1).toBe(1);
|
|
35
|
-
});
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Frontend
|
|
39
|
-
|
|
40
|
-
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
41
|
-
|
|
42
|
-
Server:
|
|
43
|
-
|
|
44
|
-
```ts#index.ts
|
|
45
|
-
import index from "./index.html"
|
|
46
|
-
|
|
47
|
-
Bun.serve({
|
|
48
|
-
routes: {
|
|
49
|
-
"/": index,
|
|
50
|
-
"/api/users/:id": {
|
|
51
|
-
GET: (req) => {
|
|
52
|
-
return new Response(JSON.stringify({ id: req.params.id }));
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
// optional websocket support
|
|
57
|
-
websocket: {
|
|
58
|
-
open: (ws) => {
|
|
59
|
-
ws.send("Hello, world!");
|
|
60
|
-
},
|
|
61
|
-
message: (ws, message) => {
|
|
62
|
-
ws.send(message);
|
|
63
|
-
},
|
|
64
|
-
close: (ws) => {
|
|
65
|
-
// handle close
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
development: {
|
|
69
|
-
hmr: true,
|
|
70
|
-
console: true,
|
|
71
|
-
}
|
|
72
|
-
})
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
76
|
-
|
|
77
|
-
```html#index.html
|
|
78
|
-
<html>
|
|
79
|
-
<body>
|
|
80
|
-
<h1>Hello, world!</h1>
|
|
81
|
-
<script type="module" src="./frontend.tsx"></script>
|
|
82
|
-
</body>
|
|
83
|
-
</html>
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
With the following `frontend.tsx`:
|
|
87
|
-
|
|
88
|
-
```tsx#frontend.tsx
|
|
89
|
-
import React from "react";
|
|
90
|
-
|
|
91
|
-
// import .css files directly and it works
|
|
92
|
-
import './index.css';
|
|
93
|
-
|
|
94
|
-
import { createRoot } from "react-dom/client";
|
|
95
|
-
|
|
96
|
-
const root = createRoot(document.body);
|
|
97
|
-
|
|
98
|
-
export default function Frontend() {
|
|
99
|
-
return <h1>Hello, world!</h1>;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
root.render(<Frontend />);
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Then, run index.ts
|
|
106
|
-
|
|
107
|
-
```sh
|
|
108
|
-
bun --hot ./index.ts
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
|
package/example.ts
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import BXO, { z } from './index';
|
|
2
|
-
import { cors, logger, auth, rateLimit, createJWT } from './plugins';
|
|
3
|
-
|
|
4
|
-
// Create the app instance
|
|
5
|
-
const app = new BXO();
|
|
6
|
-
|
|
7
|
-
// Enable hot reload
|
|
8
|
-
app.enableHotReload(['./']); // Watch current directory
|
|
9
|
-
|
|
10
|
-
// Add plugins
|
|
11
|
-
app
|
|
12
|
-
.use(logger({ format: 'simple' }))
|
|
13
|
-
.use(cors({
|
|
14
|
-
origin: ['http://localhost:3000', 'https://example.com'],
|
|
15
|
-
credentials: true
|
|
16
|
-
}))
|
|
17
|
-
.use(rateLimit({
|
|
18
|
-
max: 100,
|
|
19
|
-
window: 60, // 1 minute
|
|
20
|
-
exclude: ['/health']
|
|
21
|
-
}))
|
|
22
|
-
.use(auth({
|
|
23
|
-
type: 'jwt',
|
|
24
|
-
secret: 'your-secret-key',
|
|
25
|
-
exclude: ['/', '/login', '/health']
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
// Add simplified lifecycle hooks
|
|
29
|
-
app
|
|
30
|
-
.onBeforeStart(() => {
|
|
31
|
-
console.log('๐ง Preparing to start server...');
|
|
32
|
-
})
|
|
33
|
-
.onAfterStart(() => {
|
|
34
|
-
console.log('โ
Server fully started and ready!');
|
|
35
|
-
})
|
|
36
|
-
.onBeforeStop(() => {
|
|
37
|
-
console.log('๐ง Preparing to stop server...');
|
|
38
|
-
})
|
|
39
|
-
.onAfterStop(() => {
|
|
40
|
-
console.log('โ
Server fully stopped!');
|
|
41
|
-
})
|
|
42
|
-
.onBeforeRestart(() => {
|
|
43
|
-
console.log('๐ง Preparing to restart server...');
|
|
44
|
-
})
|
|
45
|
-
.onAfterRestart(() => {
|
|
46
|
-
console.log('โ
Server restart completed!');
|
|
47
|
-
})
|
|
48
|
-
.onRequest((ctx) => {
|
|
49
|
-
console.log(`๐จ Processing ${ctx.request.method} ${ctx.request.url}`);
|
|
50
|
-
})
|
|
51
|
-
.onResponse((ctx, response) => {
|
|
52
|
-
console.log(`๐ค Response sent for ${ctx.request.method} ${ctx.request.url}`);
|
|
53
|
-
return response;
|
|
54
|
-
})
|
|
55
|
-
.onError((ctx, error) => {
|
|
56
|
-
console.error(`๐ฅ Error in ${ctx.request.method} ${ctx.request.url}:`, error.message);
|
|
57
|
-
return { error: 'Something went wrong', timestamp: new Date().toISOString() };
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Routes exactly like your example
|
|
61
|
-
app
|
|
62
|
-
// Two arguments: path, handler
|
|
63
|
-
.get('/simple', async (ctx) => {
|
|
64
|
-
return { message: 'Hello World' };
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// Three arguments: path, handler, config
|
|
68
|
-
.get('/users/:id', async (ctx) => {
|
|
69
|
-
// ctx.params.id is fully typed as string (UUID)
|
|
70
|
-
// ctx.query.include is typed as string | undefined
|
|
71
|
-
return { user: { id: ctx.params.id, include: ctx.query.include } };
|
|
72
|
-
}, {
|
|
73
|
-
params: z.object({ id: z.string().uuid() }),
|
|
74
|
-
query: z.object({ include: z.string().optional() })
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
.post('/users', async (ctx) => {
|
|
78
|
-
// ctx.body is fully typed with name: string, email: string
|
|
79
|
-
return { created: ctx.body };
|
|
80
|
-
}, {
|
|
81
|
-
body: z.object({
|
|
82
|
-
name: z.string(),
|
|
83
|
-
email: z.string().email()
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
// Additional examples
|
|
88
|
-
.get('/health', async (ctx) => {
|
|
89
|
-
return {
|
|
90
|
-
status: 'ok',
|
|
91
|
-
timestamp: new Date().toISOString(),
|
|
92
|
-
server: app.getServerInfo()
|
|
93
|
-
};
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
.post('/login', async (ctx) => {
|
|
97
|
-
const { username, password } = ctx.body;
|
|
98
|
-
|
|
99
|
-
// Simple auth check (in production, verify against database)
|
|
100
|
-
if (username === 'admin' && password === 'password') {
|
|
101
|
-
const token = createJWT({ username, role: 'admin' }, 'your-secret-key', 3600);
|
|
102
|
-
return { token, user: { username, role: 'admin' } };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
ctx.set.status = 401;
|
|
106
|
-
return { error: 'Invalid credentials' };
|
|
107
|
-
}, {
|
|
108
|
-
body: z.object({
|
|
109
|
-
username: z.string(),
|
|
110
|
-
password: z.string()
|
|
111
|
-
})
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
.get('/protected', async (ctx) => {
|
|
115
|
-
// ctx.user is available here because of auth plugin
|
|
116
|
-
return { message: 'This is protected', user: ctx.user };
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
// Server control endpoints
|
|
120
|
-
.post('/restart', async (ctx) => {
|
|
121
|
-
// Restart the server
|
|
122
|
-
setTimeout(() => app.restart(3000), 100);
|
|
123
|
-
return { message: 'Server restart initiated' };
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
.get('/status', async (ctx) => {
|
|
127
|
-
return {
|
|
128
|
-
...app.getServerInfo(),
|
|
129
|
-
uptime: process.uptime(),
|
|
130
|
-
memory: process.memoryUsage()
|
|
131
|
-
};
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
.put('/users/:id', async (ctx) => {
|
|
135
|
-
return {
|
|
136
|
-
updated: ctx.body,
|
|
137
|
-
id: ctx.params.id,
|
|
138
|
-
version: ctx.headers['if-match']
|
|
139
|
-
};
|
|
140
|
-
}, {
|
|
141
|
-
params: z.object({ id: z.string().uuid() }),
|
|
142
|
-
body: z.object({
|
|
143
|
-
name: z.string().optional(),
|
|
144
|
-
email: z.string().email().optional()
|
|
145
|
-
}),
|
|
146
|
-
headers: z.object({
|
|
147
|
-
'if-match': z.string()
|
|
148
|
-
})
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
.delete('/users/:id', async (ctx) => {
|
|
152
|
-
ctx.set.status = 204;
|
|
153
|
-
return null;
|
|
154
|
-
}, {
|
|
155
|
-
params: z.object({ id: z.string().uuid() })
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// Start the server (with hot reload enabled)
|
|
159
|
-
app.start(3000, 'localhost');
|
|
160
|
-
|
|
161
|
-
console.log(`
|
|
162
|
-
๐ฆ BXO Framework with Hot Reload
|
|
163
|
-
|
|
164
|
-
โจ Features Enabled:
|
|
165
|
-
- ๐ Hot reload (edit any .ts/.js file to restart)
|
|
166
|
-
- ๐ฃ Full lifecycle hooks (before/after pattern)
|
|
167
|
-
- ๐ JWT authentication
|
|
168
|
-
- ๐ Rate limiting
|
|
169
|
-
- ๐ CORS support
|
|
170
|
-
- ๐ Request logging
|
|
171
|
-
|
|
172
|
-
๐งช Try these endpoints:
|
|
173
|
-
- GET /simple
|
|
174
|
-
- GET /users/123e4567-e89b-12d3-a456-426614174000?include=profile
|
|
175
|
-
- POST /users (with JSON body: {"name": "John", "email": "john@example.com"})
|
|
176
|
-
- GET /health (shows server info)
|
|
177
|
-
- POST /login (with JSON body: {"username": "admin", "password": "password"})
|
|
178
|
-
- GET /protected (requires Bearer token from /login)
|
|
179
|
-
- GET /status (server statistics)
|
|
180
|
-
- POST /restart (restart server programmatically)
|
|
181
|
-
|
|
182
|
-
๐ก Edit this file and save to see hot reload in action!
|
|
183
|
-
`);
|