bxo 0.0.5-dev.2 → 0.0.5-dev.21

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bxo",
3
3
  "module": "index.ts",
4
- "version": "0.0.5-dev.2",
4
+ "version": "0.0.5-dev.21",
5
5
  "description": "A simple and lightweight web framework for Bun",
6
6
  "type": "module",
7
7
  "devDependencies": {
package/plugins/index.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  // Export all plugins
2
2
  export { cors } from './cors';
3
- export { logger } from './logger';
4
- export { auth, createJWT } from './auth';
5
3
  export { rateLimit } from './ratelimit';
6
4
 
7
5
  // Import BXO for plugin typing
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
- `);
package/plugins/auth.ts DELETED
@@ -1,119 +0,0 @@
1
- import BXO from '../index';
2
-
3
- interface AuthOptions {
4
- type: 'jwt' | 'bearer' | 'apikey';
5
- secret?: string;
6
- header?: string;
7
- verify?: (token: string, ctx: any) => Promise<any> | any;
8
- exclude?: string[];
9
- }
10
-
11
- export function auth(options: AuthOptions): BXO {
12
- const {
13
- type,
14
- secret,
15
- header = 'authorization',
16
- verify,
17
- exclude = []
18
- } = options;
19
-
20
- const authInstance = new BXO();
21
-
22
- authInstance.onRequest(async (ctx: any) => {
23
- const url = new URL(ctx.request.url);
24
- const pathname = url.pathname;
25
-
26
- // Skip auth for excluded paths
27
- if (exclude.some(path => {
28
- if (path.includes('*')) {
29
- const regex = new RegExp(path.replace(/\*/g, '.*'));
30
- return regex.test(pathname);
31
- }
32
- return pathname === path || pathname.startsWith(path);
33
- })) {
34
- return;
35
- }
36
-
37
- const authHeader = ctx.request.headers.get(header.toLowerCase());
38
-
39
- if (!authHeader) {
40
- throw new Response(JSON.stringify({ error: 'Authorization header required' }), {
41
- status: 401,
42
- headers: { 'Content-Type': 'application/json' }
43
- });
44
- }
45
-
46
- let token: string;
47
-
48
- if (type === 'jwt' || type === 'bearer') {
49
- if (!authHeader.startsWith('Bearer ')) {
50
- throw new Response(JSON.stringify({ error: 'Invalid authorization format. Use Bearer <token>' }), {
51
- status: 401,
52
- headers: { 'Content-Type': 'application/json' }
53
- });
54
- }
55
- token = authHeader.slice(7);
56
- } else if (type === 'apikey') {
57
- token = authHeader;
58
- } else {
59
- token = authHeader;
60
- }
61
-
62
- try {
63
- let user: any;
64
-
65
- if (verify) {
66
- user = await verify(token, ctx);
67
- } else if (type === 'jwt' && secret) {
68
- // Simple JWT verification (in production, use a proper JWT library)
69
- const [headerB64, payloadB64, signature] = token.split('.');
70
- if (!headerB64 || !payloadB64 || !signature) {
71
- throw new Error('Invalid JWT format');
72
- }
73
-
74
- const payload = JSON.parse(atob(payloadB64));
75
-
76
- // Check expiration
77
- if (payload.exp && Date.now() >= payload.exp * 1000) {
78
- throw new Error('Token expired');
79
- }
80
-
81
- user = payload;
82
- } else {
83
- user = { token };
84
- }
85
-
86
- // Attach user to context
87
- ctx.user = user;
88
-
89
- } catch (error) {
90
- const message = error instanceof Error ? error.message : 'Invalid token';
91
- throw new Response(JSON.stringify({ error: message }), {
92
- status: 401,
93
- headers: { 'Content-Type': 'application/json' }
94
- });
95
- }
96
- });
97
-
98
- return authInstance;
99
- }
100
-
101
- // Helper function for creating JWT tokens (simple implementation)
102
- export function createJWT(payload: any, secret: string, expiresIn: number = 3600): string {
103
- const header = { alg: 'HS256', typ: 'JWT' };
104
- const now = Math.floor(Date.now() / 1000);
105
-
106
- const jwtPayload = {
107
- ...payload,
108
- iat: now,
109
- exp: now + expiresIn
110
- };
111
-
112
- const headerB64 = btoa(JSON.stringify(header));
113
- const payloadB64 = btoa(JSON.stringify(jwtPayload));
114
-
115
- // Simple signature (in production, use proper HMAC-SHA256)
116
- const signature = btoa(`${headerB64}.${payloadB64}.${secret}`);
117
-
118
- return `${headerB64}.${payloadB64}.${signature}`;
119
- }
package/plugins/logger.ts DELETED
@@ -1,109 +0,0 @@
1
- import BXO from '../index';
2
-
3
- interface LoggerOptions {
4
- format?: 'simple' | 'detailed' | 'json';
5
- includeBody?: boolean;
6
- includeHeaders?: boolean;
7
- }
8
-
9
- export function logger(options: LoggerOptions = {}): BXO {
10
- const {
11
- format = 'simple',
12
- includeBody = false,
13
- includeHeaders = false
14
- } = options;
15
-
16
- const loggerInstance = new BXO();
17
-
18
- loggerInstance.onRequest(async (ctx: any) => {
19
- ctx._startTime = Date.now();
20
-
21
- if (format === 'json') {
22
- const logData: any = {
23
- timestamp: new Date().toISOString(),
24
- method: ctx.request.method,
25
- url: ctx.request.url,
26
- type: 'request'
27
- };
28
-
29
- if (includeHeaders) {
30
- logData.headers = Object.fromEntries(ctx.request.headers.entries());
31
- }
32
-
33
- if (includeBody && ctx.body) {
34
- logData.body = ctx.body;
35
- }
36
-
37
- console.log(JSON.stringify(logData));
38
- } else if (format === 'detailed') {
39
- console.log(`→ ${ctx.request.method} ${ctx.request.url}`);
40
- if (includeHeaders) {
41
- console.log(' Headers:', Object.fromEntries(ctx.request.headers.entries()));
42
- }
43
- if (includeBody && ctx.body) {
44
- console.log(' Body:', ctx.body);
45
- }
46
- } else {
47
- console.log(`→ ${ctx.request.method} ${ctx.request.url}`);
48
- }
49
- });
50
-
51
- loggerInstance.onResponse(async (ctx: any, response: any) => {
52
- const duration = Date.now() - (ctx._startTime || 0);
53
- const status = ctx.set.status || 200;
54
-
55
- if (format === 'json') {
56
- const logData: any = {
57
- timestamp: new Date().toISOString(),
58
- method: ctx.request.method,
59
- url: ctx.request.url,
60
- status,
61
- duration: `${duration}ms`,
62
- type: 'response'
63
- };
64
-
65
- if (includeHeaders && ctx.set.headers) {
66
- logData.responseHeaders = ctx.set.headers;
67
- }
68
-
69
- if (includeBody && response) {
70
- logData.response = response;
71
- }
72
-
73
- console.log(JSON.stringify(logData));
74
- } else if (format === 'detailed') {
75
- console.log(`← ${ctx.request.method} ${ctx.request.url} ${status} ${duration}ms`);
76
- if (includeHeaders && ctx.set.headers) {
77
- console.log(' Response Headers:', ctx.set.headers);
78
- }
79
- if (includeBody && response) {
80
- console.log(' Response:', response);
81
- }
82
- } else {
83
- const statusColor = status >= 400 ? '\x1b[31m' : status >= 300 ? '\x1b[33m' : '\x1b[32m';
84
- const resetColor = '\x1b[0m';
85
- console.log(`← ${ctx.request.method} ${ctx.request.url} ${statusColor}${status}${resetColor} ${duration}ms`);
86
- }
87
-
88
- return response;
89
- });
90
-
91
- loggerInstance.onError(async (ctx: any, error: Error) => {
92
- const duration = Date.now() - (ctx._startTime || 0);
93
-
94
- if (format === 'json') {
95
- console.log(JSON.stringify({
96
- timestamp: new Date().toISOString(),
97
- method: ctx.request.method,
98
- url: ctx.request.url,
99
- error: error.message,
100
- duration: `${duration}ms`,
101
- type: 'error'
102
- }));
103
- } else {
104
- console.log(`✗ ${ctx.request.method} ${ctx.request.url} \x1b[31mERROR\x1b[0m ${duration}ms: ${error.message}`);
105
- }
106
- });
107
-
108
- return loggerInstance;
109
- }