@minejs/server 0.0.7 → 0.0.8

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
@@ -8,10 +8,10 @@
8
8
  </div>
9
9
 
10
10
  <div align="center">
11
- <img src="https://img.shields.io/badge/v-0.0.7-black"/>
12
- <img src="https://img.shields.io/badge/🔥-@minejs-black"/>
11
+ <img src="https://img.shields.io/badge/v-0.0.8-black"/>
12
+ <a href="https://github.com/minejs-org"><img src="https://img.shields.io/badge/🔥-@minejs-black"/></a>
13
13
  <br>
14
- <img src="https://img.shields.io/badge/coverage-98.88%25-brightgreen" alt="Test Coverage" />
14
+ <img src="https://img.shields.io/badge/coverage-98.78%25-brightgreen" alt="Test Coverage" />
15
15
  <img src="https://img.shields.io/github/issues/minejs-org/server?style=flat" alt="Github Repo Issues" />
16
16
  <img src="https://img.shields.io/github/stars/minejs-org/server?style=social" alt="GitHub Repo stars" />
17
17
  </div>
@@ -23,850 +23,702 @@
23
23
 
24
24
  <!-- ╔══════════════════════════════ DOC ══════════════════════════════╗ -->
25
25
 
26
- - ## Quick Start 🔥
26
+ - ## Overview 👀
27
27
 
28
- > **_A lightweight, type-safe HTTP server framework for Bun with built-in security, routing, and database support._**
28
+ - #### Why ?
29
+ > To provide a lightweight, type-safe HTTP server framework with built-in security, routing, database support, and i18n - enabling fast, production-ready server applications.
29
30
 
30
- - ### Setup
31
+ - #### When ?
32
+ > When you need to build a production-grade server with:
33
+ > - Type-safe routing and middleware
34
+ > - Built-in security (CORS, CSRF, rate limiting, helmet)
35
+ > - Database integration
36
+ > - Internationalization (i18n) support
37
+ > - Request validation
38
+ > - Static file serving
39
+ > - Comprehensive logging
31
40
 
32
- > install [`hmm`](https://github.com/minejs-org/hmm) first.
41
+ > When you want to write modern server applications without complex boilerplate.
33
42
 
34
- ```bash
35
- hmm i @minejs/server
36
- ```
43
+ > When you use [@cruxjs/app](https://github.com/cruxjs-org/app).
37
44
 
38
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
45
+ <br>
46
+ <br>
39
47
 
40
- - ### Usage
48
+ - ## Quick Start 🔥
41
49
 
42
- ```ts
43
- import { server, type ServerInstance, type AppContext } from '@minejs/server'
44
- ```
50
+ > install [`hmm`](https://github.com/minejs-org/hmm) first.
45
51
 
46
- - ### 1. Basic Server
52
+ ```bash
53
+ # in your terminal
54
+ hmm i @minejs/server
55
+ ```
47
56
 
48
- ```typescript
49
- import { server } from '@minejs/server'
57
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
50
58
 
51
- const app = await server({
52
- port: 3000,
53
- logging: true,
54
- routes: [
55
- {
56
- method: 'GET',
57
- path: '/hello',
58
- handler: (c) => c.json({ message: 'Hello, World!' })
59
- }
60
- ]
61
- })
59
+ - #### Setup
62
60
 
63
- await app.start()
64
- ```
61
+ > ***🌟 If you are using [`@cruxjs/app`](https://github.com/cruxjs-org/app), add the configuration directly to your `.\src\index.ts` file, inside `AppConfig` and skip the manual setup/startup steps. 🌟***
65
62
 
66
- - ### 2. Route Handling with Parameters
63
+ > Create a basic server configuration:
67
64
 
68
- ```typescript
69
- import { server, type AppContext } from '@minejs/server'
65
+ ```typescript
66
+ import { server, type ServerConfig } from '@minejs/server';
70
67
 
71
- const app = await server({
72
- port: 3000,
73
- routes: [
74
- {
75
- method: 'GET',
76
- path: '/users/:id',
77
- handler: (c: AppContext) => {
78
- const userId = c.params.id
79
- return c.json({ userId, name: 'John Doe' })
80
- }
81
- },
82
- {
83
- method: 'POST',
84
- path: '/users',
85
- handler: (c: AppContext) => {
86
- const body = c.body
87
- return c.json({ created: true, data: body }, 201)
88
- }
89
- }
90
- ]
91
- })
68
+ const config: ServerConfig = {
69
+ port : 3000,
70
+ hostname : 'localhost',
92
71
 
93
- await app.start()
94
- ```
72
+ security: {
73
+ cors : true,
74
+ rateLimit: {
75
+ max : 100,
76
+ windowMs : 60000
77
+ }
78
+ },
95
79
 
96
- - ### 3. Request Context (AppContext)
80
+ logging: {
81
+ level : 'info',
82
+ pretty : true
83
+ }
84
+ };
97
85
 
98
- ```typescript
99
- import { server, type AppContext } from '@minejs/server'
86
+ const app = await server(config);
87
+ await app.start();
88
+ ```
100
89
 
101
- const app = await server({
102
- port: 3000,
103
- routes: [
104
- {
105
- method: 'GET',
106
- path: '/context-demo',
107
- handler: (c: AppContext) => {
108
- return c.json({
109
- ip: c.ip, // Client IP
110
- method: c.request.method, // HTTP method
111
- path: c.request.url, // Request URL
112
- lang: c.lang, // Request language
113
- requestId: c.requestId, // Unique request ID
114
- query: c.query, // Query parameters
115
- headers: Object.fromEntries(c.headers.entries())
116
- })
117
- }
118
- }
119
- ]
120
- })
90
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
91
+ <br>
121
92
 
122
- await app.start()
123
- ```
93
+ - #### Usage
124
94
 
125
- - ### 4. Middleware
95
+ > Define routes and add them to your server:
126
96
 
127
- ```typescript
128
- import { server, type AppContext, type AppMiddleware } from '@minejs/server'
129
-
130
- // Custom middleware
131
- const authMiddleware: AppMiddleware = async (c, next) => {
132
- const token = c.getHeader('Authorization')
133
-
134
- if (!token) {
135
- return c.json({ error: 'Unauthorized' }, 401)
97
+ ```typescript
98
+ import { server, type ServerConfig, type AppContext } from '@minejs/server';
99
+
100
+ const config: ServerConfig = {
101
+ port : 3000,
102
+ hostname : 'localhost',
103
+
104
+ routes: [
105
+ {
106
+ method : 'GET',
107
+ path : '/api/hello',
108
+ handler : (c: AppContext) => {
109
+ return c.json({ message: 'Hello World!' });
110
+ }
111
+ },
112
+ {
113
+ method : 'POST',
114
+ path : '/api/users',
115
+ handler : (c: AppContext) => {
116
+ const userData = c.body;
117
+ // Process user data
118
+ return c.json({ id: 1, ...userData }, 201);
119
+ }
120
+ },
121
+ {
122
+ method : 'GET',
123
+ path : '/api/users/:id',
124
+ handler : (c: AppContext) => {
125
+ const userId = c.params.id;
126
+ return c.json({ id: userId, name: 'John Doe' });
127
+ }
136
128
  }
129
+ ],
137
130
 
138
- // Set user in context
139
- c.user = { id: 1, name: 'John' }
140
-
141
- // Continue to next middleware/handler
142
- await next()
131
+ logging: {
132
+ level : 'info',
133
+ pretty : true
143
134
  }
135
+ };
144
136
 
145
- const app = await server({
146
- port: 3000,
147
- routes: [
148
- {
149
- method: 'GET',
150
- path: '/protected',
151
- handler: (c: AppContext) => {
152
- return c.json({ user: c.user })
153
- },
154
- middlewares: [authMiddleware]
155
- }
156
- ]
157
- })
137
+ const app = await server(config);
138
+ await app.start();
139
+ ```
158
140
 
159
- await app.start()
160
- ```
141
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
161
142
 
162
- - ### 5. Security (CORS, Rate Limiting, Headers)
143
+ - #### Server Configuration
163
144
 
164
145
  ```typescript
165
- import { server } from '@minejs/server'
146
+ server: {
147
+ // Port and hostname
148
+ port : 3000,
149
+ hostname : 'localhost',
166
150
 
167
- const app = await server({
168
- port: 3000,
169
- logging: true,
170
- security: {
171
- cors: {
172
- origin: ['https://example.com', 'https://app.example.com'],
173
- credentials: true,
174
- maxAge: 3600
175
- },
176
- rateLimit: {
177
- windowMs: 60000, // 1 minute
178
- max: 100, // 100 requests per minute
179
- message: 'Too many requests'
180
- }
151
+ // Request handling
152
+ requestTimeout : 30000, // Request timeout in ms
153
+ maxRequestSize : 10485760, // Max request body size in bytes
154
+
155
+ // Logging
156
+ logging: {
157
+ level : 'info', // 'debug' | 'info' | 'warn' | 'error'
158
+ pretty : true // Pretty-print logs
181
159
  },
182
- routes: [
183
- {
184
- method: 'GET',
185
- path: '/api/data',
186
- handler: (c) => c.json({ data: [] })
187
- }
188
- ]
189
- })
190
160
 
191
- await app.start()
161
+ // Shutdown
162
+ gracefulShutdownTimeout: 10000 // Graceful shutdown timeout in ms
163
+ }
192
164
  ```
193
165
 
194
- - ### 6. Cookie Management
166
+ - #### Routing
195
167
 
196
168
  ```typescript
197
- import { server, type AppContext } from '@minejs/server'
169
+ routes: [
170
+ {
171
+ // HTTP method(s)
172
+ method : 'GET', // or ['GET', 'POST']
198
173
 
199
- const app = await server({
200
- port: 3000,
201
- routes: [
202
- {
203
- method: 'POST',
204
- path: '/login',
205
- handler: (c: AppContext) => {
206
- // Set cookie
207
- c.setCookie('session_id', 'abc123', {
208
- maxAge: 3600, // 1 hour
209
- httpOnly: true,
210
- secure: true,
211
- sameSite: 'Strict'
212
- })
213
-
214
- return c.json({ success: true })
215
- }
216
- },
217
- {
218
- method: 'GET',
219
- path: '/profile',
220
- handler: (c: AppContext) => {
221
- // Get cookie
222
- const sessionId = c.getCookie('session_id')
223
- return c.json({ sessionId })
224
- }
174
+ // Route path with parameters
175
+ path : '/api/users/:id', // supports :param syntax
176
+
177
+ // Handler function
178
+ handler : (c: AppContext) => {
179
+ return c.json({ id: c.params.id });
225
180
  },
226
- {
227
- method: 'POST',
228
- path: '/logout',
229
- handler: (c: AppContext) => {
230
- // Delete cookie
231
- c.deleteCookie('session_id', { path: '/' })
232
- return c.json({ success: true })
181
+
182
+ // Optional middleware
183
+ middlewares: [
184
+ async (c, next) => {
185
+ // Before handler
186
+ await next();
187
+ // After handler
233
188
  }
234
- }
235
- ]
236
- })
189
+ ],
237
190
 
238
- await app.start()
239
- ```
191
+ // Optional validation
192
+ validate: {
193
+ body : { /* schema */ },
194
+ query : { /* schema */ },
195
+ params : { /* schema */ }
196
+ },
240
197
 
241
- - ### 7. Static File Serving
198
+ // Optional rate limiting
199
+ rateLimit: {
200
+ max : 10,
201
+ windowMs : 60000
202
+ },
242
203
 
243
- ```typescript
244
- import { server } from '@minejs/server'
204
+ // Optional caching
205
+ cache : 3600, // Cache in seconds
245
206
 
246
- const app = await server({
247
- port: 3000,
248
- static: {
249
- path: '/public', // URL prefix
250
- directory: './public', // Local directory
251
- maxAge: 3600, // Cache in seconds
252
- dotfiles: 'deny', // Don't serve hidden files
253
- index: ['index.html'] // Index files
207
+ // Optional route tags
208
+ tags : ['users', 'api']
254
209
  }
255
- })
256
-
257
- await app.start()
258
- // Now http://localhost:3000/public/style.css serves ./public/style.css
210
+ ]
259
211
  ```
260
212
 
261
- - ### 8. Database Integration
213
+ - #### Security Configuration
262
214
 
263
215
  ```typescript
264
- import { server, type AppContext } from '@minejs/server'
265
-
266
- const app = await server({
267
- port: 3000,
268
- database: {
269
- connection: './data.db', // SQLite file path
270
- name: 'default'
216
+ security: {
217
+ // Cross-Origin Resource Sharing
218
+ cors: {
219
+ origin : 'http://localhost:3000', // or ['url1', 'url2'] or function
220
+ methods : ['GET', 'POST', 'PUT', 'DELETE'],
221
+ allowedHeaders : ['Content-Type', 'Authorization'],
222
+ credentials : true,
223
+ maxAge : 86400
271
224
  },
272
- routes: [
273
- {
274
- method: 'GET',
275
- path: '/users',
276
- handler: (c: AppContext) => {
277
- const db = c.db
278
- if (!db) return c.json({ error: 'No database' }, 500)
279
-
280
- // Use database connection
281
- return c.json({ users: [] })
282
- }
283
- }
284
- ]
285
- })
286
225
 
287
- await app.start()
288
- ```
226
+ // Rate limiting
227
+ rateLimit: {
228
+ windowMs : 60000, // Time window in ms
229
+ max : 100, // Max requests per window
230
+ message : 'Too many requests',
231
+ keyGenerator : (c) => c.ip // Custom key generator
232
+ },
289
233
 
290
- - ### 9. Response Methods
234
+ // CSRF protection
235
+ csrf: {
236
+ secret : 'your-secret',
237
+ headerName : 'X-CSRF-Token',
238
+ tokenTTL : 3600000
239
+ },
291
240
 
292
- ```typescript
293
- import { server, type AppContext } from '@minejs/server'
241
+ // Helmet (security headers)
242
+ helmet: {
243
+ contentSecurityPolicy: true,
244
+ hsts : true,
245
+ frameguard : true,
246
+ noSniff : true,
247
+ xssFilter : true
248
+ },
294
249
 
295
- const app = await server({
296
- port: 3000,
297
- routes: [
298
- {
299
- method: 'GET',
300
- path: '/json',
301
- handler: (c: AppContext) => c.json({ message: 'Hello' })
302
- },
303
- {
304
- method: 'GET',
305
- path: '/text',
306
- handler: (c: AppContext) => c.text('Plain text response')
307
- },
308
- {
309
- method: 'GET',
310
- path: '/html',
311
- handler: (c: AppContext) => c.html('<h1>HTML Page</h1>')
312
- },
313
- {
314
- method: 'GET',
315
- path: '/file',
316
- handler: (c: AppContext) => c.file('./public/document.pdf', 'application/pdf')
317
- },
318
- {
319
- method: 'GET',
320
- path: '/redirect',
321
- handler: (c: AppContext) => c.redirect('/new-location', 301)
322
- }
323
- ]
324
- })
250
+ // Request validation
251
+ validation : true,
325
252
 
326
- await app.start()
253
+ // HTML sanitization
254
+ sanitize : true
255
+ }
327
256
  ```
328
257
 
329
- - ### 10. Lifecycle Hooks
258
+ - #### Database Integration
330
259
 
331
260
  ```typescript
332
- import { server, type ServerInstance } from '@minejs/server'
333
-
334
- const app = await server({
335
- port: 3000,
336
- logging: true,
337
-
338
- onStartup: async (app) => {
339
- console.log('Server starting up...')
340
- },
341
-
342
- onReady: async (app, databases) => {
343
- console.log('Server is ready!')
344
- console.log('Database connections:', databases.size)
345
- },
346
-
347
- onShutdown: async () => {
348
- console.log('Server shutting down...')
349
- },
350
-
351
- onError: async (statusCode, path, method) => {
352
- return new Response(
353
- JSON.stringify({
354
- error: 'Custom error page',
355
- statusCode,
356
- path,
357
- method
358
- }),
359
- { status: statusCode, headers: { 'Content-Type': 'application/json' } }
360
- )
361
- }
362
- })
261
+ database: {
262
+ name : 'app',
263
+ connection : './data/app.db', // File path or ':memory:'
264
+ timeout : 5000
265
+ },
363
266
 
364
- await app.start()
267
+ // Or multiple databases
268
+ database: [
269
+ { name: 'main', connection: './data/main.db' },
270
+ { name: 'cache', connection: './data/cache.db' }
271
+ ]
365
272
  ```
366
273
 
367
- - ### 11. Internationalization (i18n)
274
+ - #### i18n Configuration
368
275
 
369
276
  ```typescript
370
- import { server, type AppContext, setupAuto, setLanguage } from '@minejs/server'
277
+ i18n: {
278
+ defaultLanguage : 'en',
279
+ supportedLanguages : ['en', 'ar', 'fr'],
280
+ basePath : './src/public/dist/i18n'
281
+ }
282
+ ```
371
283
 
372
- const app = await server({
373
- port: 3000,
374
- i18n: {
375
- defaultLanguage: 'en',
376
- supportedLanguages: ['en', 'ar', 'fr']
377
- },
378
-
379
- onStartup: async () => {
380
- // Auto-setup: Automatically detects server environment and loads from files
381
- // On server: Reads from ./translations/en.json
382
- // On browser: Fetches from http://localhost:3000/translations/en.json
383
- await setupAuto({
384
- defaultLanguage: 'en',
385
- supportedLanguages: ['en', 'ar', 'fr'],
386
- basePath: './translations/',
387
- fileExtension: 'json'
388
- })
389
- },
390
-
391
- routes: [
392
- {
393
- method: 'GET',
394
- path: '/greeting',
395
- handler: (c: AppContext) => {
396
- const language = c.lang // Detected from query, cookie, or header
397
- // Use c.i18n?.t() to translate with context
398
- return c.json({
399
- language,
400
- greeting: c.i18n?.t('greeting') || 'Hello'
401
- })
402
- }
403
- }
404
- ]
405
- })
284
+ - #### Static File Serving
406
285
 
407
- await app.start()
408
- // Language detection priority: ?lang query param > lang cookie > Accept-Language header > default
409
- ```
286
+ ```typescript
287
+ static: {
288
+ path : '/', // URL path prefix
289
+ directory : './src/public', // Local directory
290
+ maxAge : 3600, // Cache control in seconds
291
+ index : ['index.html'], // Index files
292
+ etag : true, // Enable ETag
293
+ lastModified : true, // Enable Last-Modified
294
+ fallthrough : false // Continue if not found
295
+ },
410
296
 
411
- **Translation file structure:**
412
- ```
413
- your-app/
414
- ├── src/
415
- │ └── index.ts
416
- ├── translations/
417
- │ ├── en.json
418
- │ ├── ar.json
419
- │ └── fr.json
420
- └── package.json
297
+ // Or multiple static dirs
298
+ static: [
299
+ { path: '/', directory: './src/public' },
300
+ { path: '/assets', directory: './src/assets' }
301
+ ]
421
302
  ```
422
303
 
423
- **translations/en.json:**
424
- ```json
425
- {
426
- "greeting": "Hello",
427
- "farewell": "Goodbye",
428
- "welcome": "Welcome, {name}!"
304
+ - #### Context (AppContext) API
305
+
306
+ ```typescript
307
+ handler: (c: AppContext) => {
308
+ // Request data
309
+ c.request // Original Request object
310
+ c.ip // Client IP address
311
+ c.method // HTTP method
312
+ c.path // Request path
313
+ c.params // URL parameters { id: '123' }
314
+ c.query // Query parameters { page: '1' }
315
+ c.body // Parsed request body
316
+ c.headers // Request headers
317
+ c.requestId // Unique request ID
318
+ c.lang // Detected language
319
+
320
+ // Context state
321
+ c.state // Shared state object
322
+
323
+ // User & Database
324
+ c.user // User object (if set by middleware)
325
+ c.db // Database instance
326
+ c.i18n // i18n manager
327
+ c.logger // Logger instance
328
+
329
+ // Response methods
330
+ c.json(data, status) // JSON response
331
+ c.text(data, status) // Text response
332
+ c.html(data, status) // HTML response
333
+ c.redirect(url, status) // Redirect
334
+ c.file(path, contentType) // File response
335
+
336
+ // Cookie methods
337
+ c.setCookie(name, value, options)
338
+ c.getCookie(name)
339
+ c.deleteCookie(name, options)
340
+
341
+ // Header methods
342
+ c.setHeader(key, value)
343
+ c.getHeader(key)
344
+
345
+ // Status control
346
+ c.status(code) // Set status code
347
+ c.statusCode // Get status code
348
+
349
+ return c.json({ success: true });
429
350
  }
430
351
  ```
431
352
 
432
- - ### 12. Logging
353
+ - #### Middleware
433
354
 
434
355
  ```typescript
435
- import { server } from '@minejs/server'
356
+ middlewares: [
357
+ async (c: AppContext, next) => {
358
+ // Before handler
359
+ console.log('Request:', c.path);
436
360
 
437
- const app = await server({
438
- port: 3000,
439
- logging: {
440
- level: 'info', // 'debug' | 'info' | 'warn' | 'error'
441
- pretty: true // Pretty-print logs with colors
361
+ // Call next middleware or handler
362
+ await next();
363
+
364
+ // After handler
365
+ console.log('Response sent');
442
366
  }
443
- })
367
+ ],
444
368
 
445
- await app.start()
369
+ // Or per-route middleware
370
+ routes: [
371
+ {
372
+ method : 'POST',
373
+ path : '/api/data',
374
+ middlewares : [
375
+ async (c, next) => {
376
+ if (!c.request.headers.get('authorization')) {
377
+ return c.json({ error: 'Unauthorized' }, 401);
378
+ }
379
+ await next();
380
+ }
381
+ ],
382
+ handler : (c) => c.json({ ok: true })
383
+ }
384
+ ]
446
385
  ```
447
386
 
448
- <br>
387
+ - #### Lifecycle Hooks
388
+
389
+ ```typescript
390
+ const config: ServerConfig = {
391
+ // Called when server is starting
392
+ onStartup: async (app) => {
393
+ console.log('Server starting...');
394
+ },
449
395
 
450
- - ## API Reference 🔥
396
+ // Called when server is ready
397
+ onReady: async (app, databases) => {
398
+ console.log('Server ready!');
399
+ // Setup initial data, connections, etc.
400
+ },
451
401
 
452
- - #### `server(config?: ServerConfig): ServerInstance`
453
- > Create and configure an HTTP server instance.
402
+ // Called when server is shutting down
403
+ onShutdown: async () => {
404
+ console.log('Server shutting down...');
405
+ // Cleanup resources
406
+ }
407
+ };
408
+ ```
454
409
 
455
- ```typescript
456
- const app = await server({
457
- port: 3000,
458
- hostname: 'localhost',
459
- logging: true,
460
- routes: [],
461
- security: {},
462
- database: {},
463
- static: {}
464
- })
465
- ```
410
+ - #### Error Handling
466
411
 
467
- **Configuration Options:**
468
- - `port` (number | string): Server port, default: `3000`
469
- - `hostname` (string): Server hostname, default: `localhost`
470
- - `requestTimeout` (number): Request timeout in ms, default: `30000`
471
- - `maxRequestSize` (number): Max request body size, default: `10MB`
472
- - `gracefulShutdownTimeout` (number): Shutdown timeout in ms, default: `10000`
473
- - `logging` (boolean | LoggingConfig): Enable logging
474
- - `security` (boolean | SecurityConfig): Security settings
475
- - `database` (DatabaseConfig | DatabaseConfig[]): Database connections
476
- - `i18n` (boolean | I18nConfig): Internationalization settings
477
- - `static` (StaticConfig | StaticConfig[]): Static file serving
478
- - `routes` (RouteDefinition[]): Route definitions
479
- - `middlewares` (AppMiddleware[]): Global middlewares
480
- - `onStartup` (fn): Called on startup
481
- - `onReady` (fn): Called when server is ready
482
- - `onShutdown` (fn): Called on shutdown
483
- - `onError` (fn): Custom error page handler
484
-
485
- - #### `ServerInstance`
412
+ ```typescript
413
+ const config: ServerConfig = {
414
+ // Custom error handler
415
+ errorHandler: (error: Error, context: AppContext) => {
416
+ console.error('Error:', error);
417
+ context.logger?.error(error.message);
418
+ },
486
419
 
487
- ```typescript
488
- interface ServerInstance {
489
- app: unknown // Underlying Bun server
490
- logger: Logger | null // Logger instance
491
- db: Map<string, unknown> // Database connections
492
- bunServer: unknown // Bun server object
493
- start(): Promise<void> // Start the server
494
- stop(): Promise<void> // Stop the server
495
- addRoute(route: RouteDefinition): void
496
- addRoutes(routes: RouteDefinition[]): void
497
- getRoutes(): RouteDefinition[]
498
- }
499
- ```
420
+ // Custom error page responses
421
+ onError: async (statusCode: number, path: string, method: string) => {
422
+ if (statusCode === 404) {
423
+ return new Response('<h1>Page Not Found</h1>', {
424
+ status : 404,
425
+ headers : { 'Content-Type': 'text/html' }
426
+ });
427
+ }
428
+ return new Response('Error', { status: statusCode });
429
+ }
430
+ };
431
+ ```
500
432
 
501
- **Methods:**
502
- ```typescript
503
- // Start server
504
- await app.start()
505
-
506
- // Stop server gracefully
507
- await app.stop()
508
-
509
- // Add route dynamically
510
- app.addRoute({
511
- method: 'GET',
512
- path: '/dynamic',
513
- handler: (c) => c.json({ dynamic: true })
514
- })
515
-
516
- // Add multiple routes
517
- app.addRoutes([
518
- { method: 'GET', path: '/route1', handler: (c) => c.json({}) },
519
- { method: 'POST', path: '/route2', handler: (c) => c.json({}) }
520
- ])
521
-
522
- // Get all routes
523
- const routes = app.getRoutes()
524
- ```
433
+ - #### Dynamic Route Management
525
434
 
526
- - #### `AppContext`
435
+ ```typescript
436
+ const app = await server(config);
437
+ await app.start();
438
+
439
+ // Add routes dynamically
440
+ app.addRoute({
441
+ method : 'GET',
442
+ path : '/dynamic',
443
+ handler : (c) => c.json({ dynamic: true })
444
+ });
445
+
446
+ // Add multiple routes
447
+ app.addRoutes([
448
+ { method: 'GET', path: '/route1', handler: (c) => c.json({}) },
449
+ { method: 'POST', path: '/route2', handler: (c) => c.json({}) }
450
+ ]);
451
+
452
+ // Get all routes
453
+ const routes = app.getRoutes();
454
+
455
+ // Stop server
456
+ await app.stop();
457
+ ```
527
458
 
528
- > Request context passed to every route handler and middleware.
459
+ - #### i18n Usage
529
460
 
530
- ```typescript
531
- interface AppContext {
532
- // Request info
533
- ip: string // Client IP address
534
- request: Request // Fetch API Request object
535
- params: Record<string, string> // Route parameters
536
- query: Record<string, string> // Query string parameters
537
- body: unknown // Parsed request body
538
- headers: Headers // Request headers
539
-
540
- // Server info
541
- db: DB | undefined // Database connection
542
- logger: Logger | null // Logger instance
543
- i18n: I18nManager | null // i18n manager
544
- lang: string // Current language
545
- user?: unknown // User (from middleware)
546
- requestId: string // Unique request ID
547
- state: Record<string, unknown> // Custom state
548
-
549
- // Response methods
550
- json(data: unknown, status?: number): Response
551
- text(data: string, status?: number): Response
552
- html(data: string, status?: number): Response
553
- redirect(url: string, status?: number): Response
554
- file(path: string, contentType?: string): Response
555
-
556
- // Cookie methods
557
- setCookie(name: string, value: string, options?: CookieOptions): AppContext
558
- getCookie(name: string): string | undefined
559
- deleteCookie(name: string, options?: CookieOptions): AppContext
560
-
561
- // Header methods
562
- setHeader(key: string, value: string): AppContext
563
- getHeader(key: string): string | undefined
564
-
565
- // Status code
566
- status(code: number): AppContext
567
- statusCode: number
568
- }
569
- ```
461
+ ```typescript
462
+ handler: (c: AppContext) => {
463
+ import { t } from '@minejs/i18n';
570
464
 
571
- - #### `RouteDefinition`
465
+ // Current language (auto-detected from ?lang, cookie, or header)
466
+ const lang = c.lang;
572
467
 
573
- ```typescript
574
- interface RouteDefinition {
575
- method: HttpMethod | HttpMethod[] // 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD'
576
- path: string // Route path: '/users' or '/users/:id' or '/files/*'
577
- handler: RouteHandler // Route handler function
578
- middlewares?: AppMiddleware[] // Route-specific middlewares
579
- validate?: ValidationSchema // Input validation schema
580
- timeout?: number // Route-specific timeout
581
- rateLimit?: RateLimitConfig // Route-specific rate limiting
582
- cache?: number // Response cache duration in seconds
583
- tags?: string[] // Route tags for documentation
584
- }
585
- ```
468
+ // Translate
469
+ const message = t('hello');
586
470
 
587
- **Examples:**
588
- ```typescript
589
- // Static route
590
- { method: 'GET', path: '/hello', handler: (c) => c.json({}) }
471
+ // With parameters
472
+ const greeting = t('greeting', { name: 'John' });
591
473
 
592
- // Dynamic route with parameter
593
- { method: 'GET', path: '/users/:id', handler: (c) => c.json({ id: c.params.id }) }
474
+ // Get supported languages
475
+ const supported = c.i18n?.getSupportedLanguages();
594
476
 
595
- // Wildcard route
596
- { method: 'GET', path: '/files/*', handler: (c) => c.json({}) }
477
+ return c.json({ message, lang, supported });
478
+ }
479
+ ```
597
480
 
598
- // Multiple methods
599
- { method: ['GET', 'POST'], path: '/data', handler: (c) => c.json({}) }
481
+ <br>
482
+ <br>
600
483
 
601
- // With middlewares
602
- {
603
- method: 'POST',
604
- path: '/admin',
605
- handler: (c) => c.json({ admin: true }),
606
- middlewares: [authMiddleware, adminMiddleware]
607
- }
608
- ```
484
+ - ## Complete Example 📑
609
485
 
610
- - #### `CookieOptions`
486
+ - ### src/server/config.ts
611
487
 
612
488
  ```typescript
613
- interface CookieOptions {
614
- maxAge?: number // Cookie lifetime in seconds
615
- expires?: Date // Cookie expiration date
616
- path?: string // Cookie path (default: '/')
617
- domain?: string // Cookie domain
618
- secure?: boolean // HTTPS only
619
- httpOnly?: boolean // JavaScript cannot access
620
- sameSite?: 'Strict' | 'Lax' | 'None' // CSRF protection
621
- }
622
- ```
489
+ import { ServerConfig } from '@minejs/server';
490
+ import path from 'path';
491
+
492
+ const port = process.env.PORT || 3000;
493
+ const hostname = process.env.HOSTNAME || 'localhost';
494
+ const origin = (process.env.CORS_ORIGINS || `http://${hostname}:${port}`).split(',');
495
+ const logLevel = (process.env.LOG_LEVEL || 'info') as 'error' | 'warn' | 'info' | 'debug';
496
+ const staticPath = process.env.STATIC_PATH || '/';
497
+ const staticDir = path.resolve(process.env.STATIC_DIR || './src/public');
498
+ const i18nPath = path.resolve(process.env.I18N_PATH || './src/public/dist/i18n');
499
+
500
+ export const config: ServerConfig = {
501
+ port : port,
502
+ hostname : hostname,
503
+ requestTimeout : 30000,
504
+ maxRequestSize : 10485760,
505
+
506
+ static: {
507
+ path : staticPath,
508
+ directory : staticDir
509
+ },
623
510
 
624
- - #### `SecurityConfig`
511
+ security: {
512
+ rateLimit: {
513
+ max : 100,
514
+ windowMs : 60000,
515
+ message : 'Too many requests',
516
+ keyGenerator : (c) => {
517
+ const apiKey = c.request?.headers?.get?.('x-api-key');
518
+ return apiKey || c.ip || 'unknown';
519
+ }
520
+ },
521
+ cors: {
522
+ origin : origin,
523
+ credentials : true,
524
+ methods : ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
525
+ allowedHeaders : ['Content-Type', 'Authorization', 'X-Request-ID']
526
+ },
527
+ csrf: true
528
+ },
625
529
 
626
- ```typescript
627
- interface SecurityConfig {
628
- cors?: CorsConfig | boolean
629
- rateLimit?: RateLimitConfig | boolean
630
- csrf?: CsrfConfig | boolean
631
- helmet?: HelmetConfig | boolean
632
- auth?: AuthConfig | boolean
633
- }
634
- ```
530
+ logging: {
531
+ level : logLevel,
532
+ pretty : true
533
+ },
635
534
 
636
- **CORS:**
637
- ```typescript
638
- security: {
639
- cors: {
640
- origin: '*' | string | string[] | (origin: string) => boolean
641
- methods: ['GET', 'POST', 'PUT', 'DELETE']
642
- allowedHeaders: ['Content-Type', 'Authorization']
643
- credentials: true
644
- maxAge: 3600
645
- }
646
- }
647
- ```
535
+ i18n: {
536
+ defaultLanguage : 'en',
537
+ supportedLanguages : ['en', 'ar'],
538
+ basePath : i18nPath
539
+ },
648
540
 
649
- **Rate Limiting:**
650
- ```typescript
651
- security: {
652
- rateLimit: {
653
- windowMs: 60000 // Time window in ms
654
- max: 100 // Max requests per window
655
- keyGenerator: (c) => c.ip // Custom key generator
656
- message: 'Too many requests'
541
+ onShutdown: async () => {
542
+ console.log('Server shutting down...');
657
543
  }
658
- }
544
+ };
659
545
  ```
660
546
 
661
- - #### `LoggingConfig`
547
+ - ### src/server/routes.ts
662
548
 
663
549
  ```typescript
664
- interface LoggingConfig {
665
- level?: 'debug' | 'info' | 'warn' | 'error'
666
- pretty?: boolean
667
- }
668
- ```
550
+ import { RouteDefinition, type AppContext, t } from '@minejs/server';
669
551
 
670
- - #### `DatabaseConfig`
552
+ export const routes: RouteDefinition[] = [
553
+ {
554
+ method : 'GET',
555
+ path : '/api/hello',
556
+
557
+ handler : (c: AppContext) => {
558
+ return c.json({
559
+ message : t('hello'),
560
+ language : c.lang,
561
+ supported : c.i18n?.getSupportedLanguages()
562
+ });
563
+ }
564
+ },
671
565
 
672
- ```typescript
673
- interface DatabaseConfig {
674
- name?: string // Database name, default: 'default'
675
- connection: string // Connection string or file path
676
- timeout?: number // Connection timeout
677
- }
678
- ```
566
+ {
567
+ method : ['GET', 'POST'],
568
+ path : '/api/users',
569
+
570
+ handler : (c: AppContext) => {
571
+ if (c.request.method === 'GET') {
572
+ return c.json([
573
+ { id: 1, name: 'John' },
574
+ { id: 2, name: 'Jane' }
575
+ ]);
576
+ } else {
577
+ const user = c.body;
578
+ return c.json({ id: 3, ...user }, 201);
579
+ }
580
+ }
581
+ },
679
582
 
680
- - #### `StaticConfig`
583
+ {
584
+ method : 'GET',
585
+ path : '/api/users/:id',
681
586
 
682
- ```typescript
683
- interface StaticConfig {
684
- path: string // URL prefix (e.g., '/public')
685
- directory: string // Local directory path
686
- maxAge?: number // Cache duration in seconds
687
- index?: string[] // Index files
688
- dotfiles?: 'allow' | 'deny' | 'ignore'
689
- etag?: boolean // Enable ETag headers
690
- lastModified?: boolean // Enable Last-Modified headers
691
- immutable?: boolean // Add immutable cache directive
692
- extensions?: string[] // Try extensions
693
- fallthrough?: boolean // Continue if file not found
694
- setHeaders?: (ctx: AppContext, path: string) => void
695
- }
587
+ handler : (c: AppContext) => {
588
+ const userId = c.params.id;
589
+ return c.json({ id: userId, name: 'John Doe' });
590
+ }
591
+ }
592
+ ];
696
593
  ```
697
594
 
698
- - #### `Error Classes`
595
+ - ### src/index.ts
699
596
 
700
597
  ```typescript
701
- class AppError extends Error {
702
- statusCode: number
703
- code?: string
704
- }
598
+ import { server } from '@minejs/server';
599
+ import { config } from './server/config';
600
+ import { routes } from './server/routes';
705
601
 
706
- class ValidationError extends AppError {
707
- issues?: unknown
708
- }
709
-
710
- class DatabaseError extends AppError {}
711
- class TimeoutError extends AppError {}
712
- class RateLimitError extends AppError {}
713
- ```
602
+ const main = async () => {
603
+ const app = await server({
604
+ ...config,
605
+ routes
606
+ });
714
607
 
715
- **Usage:**
716
- ```typescript
717
- import { server, AppError, ValidationError } from '@minejs/server'
608
+ await app.start();
609
+ console.log(`Server running on http://${config.hostname}:${config.port}`);
610
+ };
718
611
 
719
- const app = await server({
720
- port: 3000,
721
- routes: [
722
- {
723
- method: 'GET',
724
- path: '/error',
725
- handler: (c) => {
726
- throw new AppError('Something went wrong', 500, 'INTERNAL_ERROR')
727
- }
728
- },
729
- {
730
- method: 'POST',
731
- path: '/validate',
732
- handler: (c) => {
733
- if (!c.body.email) {
734
- throw new ValidationError('Email is required')
735
- }
736
- return c.json({ valid: true })
737
- }
738
- }
739
- ]
740
- })
612
+ main().catch(console.error);
741
613
  ```
742
614
 
743
615
  <br>
616
+ <br>
744
617
 
745
- - ## Advanced Features 🚀
746
-
747
- - ### Middleware Chain
618
+ - ## API Documentation 📖
748
619
 
749
- Middlewares execute in order and can short-circuit the chain:
620
+ - ### Types
750
621
 
751
622
  ```typescript
752
- const authMiddleware: AppMiddleware = async (c, next) => {
753
- const token = c.getHeader('Authorization')
754
- if (!token) return c.json({ error: 'Unauthorized' }, 401)
755
- await next() // Continue to next middleware/handler
756
- }
623
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
757
624
 
758
- const loggingMiddleware: AppMiddleware = async (c, next) => {
759
- console.log('Before:', c.request.method, c.request.url)
760
- await next()
761
- console.log('After:', c.statusCode)
762
- }
625
+ type RouteHandler = (c: AppContext) => Response | Promise<Response>;
763
626
 
764
- app.addRoute({
765
- method: 'GET',
766
- path: '/protected',
767
- handler: (c) => c.json({ data: 'secret' }),
768
- middlewares: [loggingMiddleware, authMiddleware]
769
- })
770
- ```
627
+ type AppMiddleware = (c: AppContext, next: () => Promise<void>) => void | Promise<void>;
771
628
 
772
- - ### Multiple Databases
629
+ interface AppContext {
630
+ // Request data
631
+ ip : string;
632
+ request : Request;
633
+ params : Record<string, string>;
634
+ query : Record<string, string>;
635
+ body : any;
636
+ headers : Headers;
637
+ requestId : string;
638
+ lang? : string;
639
+
640
+ // Context
641
+ state : Record<string, unknown>;
642
+ user? : unknown;
643
+
644
+ // Managers
645
+ db : DB | undefined;
646
+ logger : Logger | null;
647
+ i18n : I18nManager | null;
773
648
 
774
- ```typescript
775
- const app = await server({
776
- port: 3000,
777
- database: [
778
- { name: 'primary', connection: './primary.db' },
779
- { name: 'cache', connection: './cache.db' },
780
- { name: 'logs', connection: ':memory:' }
781
- ]
782
- })
649
+ // Response methods
650
+ json (data: unknown, status?: number): Response;
651
+ text (data: string, status?: number): Response;
652
+ html (data: string, status?: number): Response;
653
+ redirect (url: string, status?: number): Response;
654
+ file (path: string, contentType?: string): Response;
783
655
 
784
- await app.start()
656
+ // Cookie methods
657
+ setCookie (name: string, value: string, options?: CookieOptions): AppContext;
658
+ getCookie (name: string): string | undefined;
659
+ deleteCookie (name: string, options?: Partial<CookieOptions>): AppContext;
785
660
 
786
- // Access in routes
787
- const route = {
788
- method: 'GET',
789
- path: '/data',
790
- handler: (c: AppContext) => {
791
- const primary = app.db.get('primary')
792
- const cache = app.db.get('cache')
793
- return c.json({ primary, cache })
794
- }
795
- }
796
- ```
661
+ // Header methods
662
+ setHeader (key: string, value: string): AppContext;
663
+ getHeader (key: string): string | undefined;
797
664
 
798
- - ### Dynamic Routes
665
+ // Status
666
+ status (code: number): AppContext;
667
+ statusCode : number;
668
+ }
799
669
 
800
- ```typescript
801
- const app = await server({
802
- port: 3000,
803
- logging: false,
804
- routes: [
805
- {
806
- method: 'GET',
807
- path: '/api/users/:id',
808
- handler: (c) => c.json({ userId: c.params.id })
809
- },
810
- {
811
- method: 'GET',
812
- path: '/download/*',
813
- handler: (c) => c.file('./downloads/' + c.params[0])
814
- }
815
- ]
816
- })
670
+ interface ServerInstance {
671
+ start (): Promise<void>;
672
+ stop (): Promise<void>;
673
+ addRoute (route: RouteDefinition): void;
674
+ addRoutes (routes: RouteDefinition[]): void;
675
+ getRoutes (): RouteDefinition[];
676
+ }
817
677
 
818
- await app.start()
678
+ interface ServerConfig {
679
+ port? : number | string;
680
+ hostname? : string;
681
+ requestTimeout? : number;
682
+ maxRequestSize? : number;
683
+ database? : DatabaseConfig | DatabaseConfig[];
684
+ security? : boolean | SecurityConfig;
685
+ logging? : boolean | LoggingConfig;
686
+ i18n? : boolean | I18nConfig;
687
+ static? : StaticConfig | StaticConfig[];
688
+ routes? : RouteDefinition[];
689
+ middlewares? : AppMiddleware[];
690
+ errorHandler? : (error: Error, context: AppContext) => void | Promise<void>;
691
+ onError? : (statusCode: number, path: string, method: string) => Response | Promise<Response>;
692
+ onStartup? : (app: any) => void | Promise<void>;
693
+ onReady? : (app: ServerInstance, db: Map<string, DB>) => void | Promise<void>;
694
+ onShutdown? : () => void | Promise<void>;
695
+ }
819
696
  ```
820
697
 
821
- - ### Error Handling
698
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
822
699
 
823
- ```typescript
824
- const app = await server({
825
- port: 3000,
826
- errorHandler: async (error, context) => {
827
- // Custom error handling
828
- console.error('Error:', error.message)
829
- },
830
- onError: async (statusCode, path, method) => {
831
- // Custom error pages
832
- if (statusCode === 404) {
833
- return new Response(
834
- JSON.stringify({ error: 'Page not found', path }),
835
- { status: 404, headers: { 'Content-Type': 'application/json' } }
836
- )
837
- }
838
- return new Response('Error', { status: statusCode })
839
- }
840
- })
841
- ```
700
+ - ### Functions
842
701
 
843
- - ### Health & Readiness Endpoints
702
+ - `server(config?: ServerConfig): Promise<ServerInstance>` - Create and initialize server
703
+ - `getRoutes(): RouteDefinition[]` - Get all registered routes
704
+ - `addRoute(route: RouteDefinition): void` - Add single route
705
+ - `addRoutes(routes: RouteDefinition[]): void` - Add multiple routes
706
+ - `start(): Promise<void>` - Start server listening
707
+ - `stop(): Promise<void>` - Stop server gracefully
844
708
 
845
- Built-in endpoints for monitoring:
709
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
846
710
 
847
- ```typescript
848
- // GET /health - Server health status
849
- // Response: { status: 'healthy', uptime: 123.45, activeRequests: 5, ... }
711
+ - #### Related
850
712
 
851
- // GET /readiness - Server readiness status
852
- // Response: { ready: true, checks: { database: 'connected', ... }, ... }
853
- ```
713
+ - ##### [@minejs/db](https://github.com/minejs-org/db)
854
714
 
855
- - ### Request Lifecycle
715
+ - ##### [@minejs/i18n](https://github.com/minejs-org/i18n)
856
716
 
857
- 1. **Receive** - Request received by server
858
- 2. **Parse** - URL, method, headers, body parsed
859
- 3. **Security** - CORS, rate limiting, validation
860
- 4. **Match** - Route matching from router
861
- 5. **Context** - AppContext created
862
- 6. **Middlewares** - Route middlewares execute in order
863
- 7. **Handler** - Route handler executes
864
- 8. **Response** - Response sent with security headers
865
- 9. **Log** - Request logged with duration
717
+ - ##### [@cruxjs/app](https://github.com/cruxjs-org/app)
866
718
 
867
- <br>
719
+ - ##### [@cruxjs/client](https://github.com/cruxjs-org/client)
868
720
 
869
- <!-- ╚══════════════════════════════════════════════════════════════════╝ -->
721
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
870
722
 
871
723
 
872
724
 
@@ -880,4 +732,4 @@
880
732
  <a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
881
733
  </div>
882
734
 
883
- <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
735
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->