@minejs/server 0.0.1

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 ADDED
@@ -0,0 +1,849 @@
1
+ <!-- ╔══════════════════════════════ BEG ══════════════════════════════╗ -->
2
+
3
+ <br>
4
+ <div align="center">
5
+ <p>
6
+ <img src="./assets/img/logo.png" alt="logo" style="" height="60" />
7
+ </p>
8
+ </div>
9
+
10
+ <div align="center">
11
+ <img src="https://img.shields.io/badge/v-0.0.1-black"/>
12
+ <img src="https://img.shields.io/badge/🔥-@minejs-black"/>
13
+ <br>
14
+ <img src="https://img.shields.io/badge/coverage-94.71%25-brightgreen" alt="Test Coverage" />
15
+ <img src="https://img.shields.io/github/issues/minejs/server?style=flat" alt="Github Repo Issues" />
16
+ <img src="https://img.shields.io/github/stars/minejs/server?style=social" alt="GitHub Repo stars" />
17
+ </div>
18
+ <br>
19
+
20
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
21
+
22
+
23
+
24
+ <!-- ╔══════════════════════════════ DOC ══════════════════════════════╗ -->
25
+
26
+ - ## Quick Start 🔥
27
+
28
+ > **_A lightweight, type-safe HTTP server framework for Bun with built-in security, routing, and database support._**
29
+
30
+ - ### Setup
31
+
32
+ > install [`space`](https://github.com/solution-lib/space) first.
33
+
34
+ ```bash
35
+ space i @minejs/server
36
+ ```
37
+
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>
39
+
40
+ - ### Usage
41
+
42
+ ```ts
43
+ import { server, type ServerInstance, type AppContext } from '@minejs/server'
44
+ ```
45
+
46
+ - ### 1. Basic Server
47
+
48
+ ```typescript
49
+ import { server } from '@minejs/server'
50
+
51
+ const app = 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
+ })
62
+
63
+ await app.start()
64
+ ```
65
+
66
+ - ### 2. Route Handling with Parameters
67
+
68
+ ```typescript
69
+ import { server, type AppContext } from '@minejs/server'
70
+
71
+ const app = 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
+ })
92
+
93
+ await app.start()
94
+ ```
95
+
96
+ - ### 3. Request Context (AppContext)
97
+
98
+ ```typescript
99
+ import { server, type AppContext } from '@minejs/server'
100
+
101
+ const app = 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
+ })
121
+
122
+ await app.start()
123
+ ```
124
+
125
+ - ### 4. Middleware
126
+
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)
136
+ }
137
+
138
+ // Set user in context
139
+ c.user = { id: 1, name: 'John' }
140
+
141
+ // Continue to next middleware/handler
142
+ await next()
143
+ }
144
+
145
+ const app = 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
+ })
158
+
159
+ await app.start()
160
+ ```
161
+
162
+ - ### 5. Security (CORS, Rate Limiting, Headers)
163
+
164
+ ```typescript
165
+ import { server } from '@minejs/server'
166
+
167
+ const app = 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
+ }
181
+ },
182
+ routes: [
183
+ {
184
+ method: 'GET',
185
+ path: '/api/data',
186
+ handler: (c) => c.json({ data: [] })
187
+ }
188
+ ]
189
+ })
190
+
191
+ await app.start()
192
+ ```
193
+
194
+ - ### 6. Cookie Management
195
+
196
+ ```typescript
197
+ import { server, type AppContext } from '@minejs/server'
198
+
199
+ const app = 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
+ }
225
+ },
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 })
233
+ }
234
+ }
235
+ ]
236
+ })
237
+
238
+ await app.start()
239
+ ```
240
+
241
+ - ### 7. Static File Serving
242
+
243
+ ```typescript
244
+ import { server } from '@minejs/server'
245
+
246
+ const app = 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
254
+ }
255
+ })
256
+
257
+ await app.start()
258
+ // Now http://localhost:3000/public/style.css serves ./public/style.css
259
+ ```
260
+
261
+ - ### 8. Database Integration
262
+
263
+ ```typescript
264
+ import { server, type AppContext } from '@minejs/server'
265
+
266
+ const app = server({
267
+ port: 3000,
268
+ database: {
269
+ connection: './data.db', // SQLite file path
270
+ name: 'default'
271
+ },
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
+
287
+ await app.start()
288
+ ```
289
+
290
+ - ### 9. Response Methods
291
+
292
+ ```typescript
293
+ import { server, type AppContext } from '@minejs/server'
294
+
295
+ const app = 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
+ })
325
+
326
+ await app.start()
327
+ ```
328
+
329
+ - ### 10. Lifecycle Hooks
330
+
331
+ ```typescript
332
+ import { server, type ServerInstance } from '@minejs/server'
333
+
334
+ const app = 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
+ })
363
+
364
+ await app.start()
365
+ ```
366
+
367
+ - ### 11. Internationalization (i18n)
368
+
369
+ ```typescript
370
+ import { server, type AppContext } from '@minejs/server'
371
+
372
+ const app = server({
373
+ port: 3000,
374
+ i18n: {
375
+ defaultLanguage: 'en',
376
+ supportedLanguages: ['en', 'ar', 'fr'],
377
+ staticPath: './src/i18n'
378
+ },
379
+ routes: [
380
+ {
381
+ method: 'GET',
382
+ path: '/greeting',
383
+ handler: (c: AppContext) => {
384
+ const language = c.lang // Detected from query, cookie, or header
385
+ return c.json({
386
+ language,
387
+ greeting: 'Hello'
388
+ })
389
+ }
390
+ }
391
+ ]
392
+ })
393
+
394
+ await app.start()
395
+ // Language detection priority: ?lang query param > lang cookie > Accept-Language header > default
396
+ ```
397
+
398
+ - ### 12. Logging
399
+
400
+ ```typescript
401
+ import { server } from '@minejs/server'
402
+
403
+ const app = server({
404
+ port: 3000,
405
+ logging: {
406
+ level: 'info', // 'debug' | 'info' | 'warn' | 'error'
407
+ pretty: true // Pretty-print logs with colors
408
+ }
409
+ })
410
+
411
+ await app.start()
412
+ ```
413
+
414
+ <br>
415
+
416
+ - ## API Reference 🔥
417
+
418
+ - #### `server(config?: ServerConfig): ServerInstance`
419
+ > Create and configure an HTTP server instance.
420
+
421
+ ```typescript
422
+ const app = server({
423
+ port: 3000,
424
+ hostname: 'localhost',
425
+ logging: true,
426
+ routes: [],
427
+ security: {},
428
+ database: {},
429
+ static: {}
430
+ })
431
+ ```
432
+
433
+ **Configuration Options:**
434
+ - `port` (number | string): Server port, default: `3000`
435
+ - `hostname` (string): Server hostname, default: `localhost`
436
+ - `requestTimeout` (number): Request timeout in ms, default: `30000`
437
+ - `maxRequestSize` (number): Max request body size, default: `10MB`
438
+ - `gracefulShutdownTimeout` (number): Shutdown timeout in ms, default: `10000`
439
+ - `logging` (boolean | LoggingConfig): Enable logging
440
+ - `security` (boolean | SecurityConfig): Security settings
441
+ - `database` (DatabaseConfig | DatabaseConfig[]): Database connections
442
+ - `i18n` (boolean | I18nConfig): Internationalization settings
443
+ - `static` (StaticConfig | StaticConfig[]): Static file serving
444
+ - `routes` (RouteDefinition[]): Route definitions
445
+ - `middlewares` (AppMiddleware[]): Global middlewares
446
+ - `onStartup` (fn): Called on startup
447
+ - `onReady` (fn): Called when server is ready
448
+ - `onShutdown` (fn): Called on shutdown
449
+ - `onError` (fn): Custom error page handler
450
+
451
+ - #### `ServerInstance`
452
+
453
+ ```typescript
454
+ interface ServerInstance {
455
+ app: unknown // Underlying Bun server
456
+ logger: Logger | null // Logger instance
457
+ db: Map<string, unknown> // Database connections
458
+ bunServer: unknown // Bun server object
459
+ start(): Promise<void> // Start the server
460
+ stop(): Promise<void> // Stop the server
461
+ addRoute(route: RouteDefinition): void
462
+ addRoutes(routes: RouteDefinition[]): void
463
+ getRoutes(): RouteDefinition[]
464
+ }
465
+ ```
466
+
467
+ **Methods:**
468
+ ```typescript
469
+ // Start server
470
+ await app.start()
471
+
472
+ // Stop server gracefully
473
+ await app.stop()
474
+
475
+ // Add route dynamically
476
+ app.addRoute({
477
+ method: 'GET',
478
+ path: '/dynamic',
479
+ handler: (c) => c.json({ dynamic: true })
480
+ })
481
+
482
+ // Add multiple routes
483
+ app.addRoutes([
484
+ { method: 'GET', path: '/route1', handler: (c) => c.json({}) },
485
+ { method: 'POST', path: '/route2', handler: (c) => c.json({}) }
486
+ ])
487
+
488
+ // Get all routes
489
+ const routes = app.getRoutes()
490
+ ```
491
+
492
+ - #### `AppContext`
493
+
494
+ > Request context passed to every route handler and middleware.
495
+
496
+ ```typescript
497
+ interface AppContext {
498
+ // Request info
499
+ ip: string // Client IP address
500
+ request: Request // Fetch API Request object
501
+ params: Record<string, string> // Route parameters
502
+ query: Record<string, string> // Query string parameters
503
+ body: unknown // Parsed request body
504
+ headers: Headers // Request headers
505
+
506
+ // Server info
507
+ db: DB | undefined // Database connection
508
+ logger: Logger | null // Logger instance
509
+ i18n: I18nManager | null // i18n manager
510
+ lang: string // Current language
511
+ user?: unknown // User (from middleware)
512
+ requestId: string // Unique request ID
513
+ state: Record<string, unknown> // Custom state
514
+
515
+ // Response methods
516
+ json(data: unknown, status?: number): Response
517
+ text(data: string, status?: number): Response
518
+ html(data: string, status?: number): Response
519
+ redirect(url: string, status?: number): Response
520
+ file(path: string, contentType?: string): Response
521
+
522
+ // Cookie methods
523
+ setCookie(name: string, value: string, options?: CookieOptions): AppContext
524
+ getCookie(name: string): string | undefined
525
+ deleteCookie(name: string, options?: CookieOptions): AppContext
526
+
527
+ // Header methods
528
+ setHeader(key: string, value: string): AppContext
529
+ getHeader(key: string): string | undefined
530
+
531
+ // Status code
532
+ status(code: number): AppContext
533
+ statusCode: number
534
+ }
535
+ ```
536
+
537
+ - #### `RouteDefinition`
538
+
539
+ ```typescript
540
+ interface RouteDefinition {
541
+ method: HttpMethod | HttpMethod[] // 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD'
542
+ path: string // Route path: '/users' or '/users/:id' or '/files/*'
543
+ handler: RouteHandler // Route handler function
544
+ middlewares?: AppMiddleware[] // Route-specific middlewares
545
+ validate?: ValidationSchema // Input validation schema
546
+ timeout?: number // Route-specific timeout
547
+ rateLimit?: RateLimitConfig // Route-specific rate limiting
548
+ cache?: number // Response cache duration in seconds
549
+ tags?: string[] // Route tags for documentation
550
+ }
551
+ ```
552
+
553
+ **Examples:**
554
+ ```typescript
555
+ // Static route
556
+ { method: 'GET', path: '/hello', handler: (c) => c.json({}) }
557
+
558
+ // Dynamic route with parameter
559
+ { method: 'GET', path: '/users/:id', handler: (c) => c.json({ id: c.params.id }) }
560
+
561
+ // Wildcard route
562
+ { method: 'GET', path: '/files/*', handler: (c) => c.json({}) }
563
+
564
+ // Multiple methods
565
+ { method: ['GET', 'POST'], path: '/data', handler: (c) => c.json({}) }
566
+
567
+ // With middlewares
568
+ {
569
+ method: 'POST',
570
+ path: '/admin',
571
+ handler: (c) => c.json({ admin: true }),
572
+ middlewares: [authMiddleware, adminMiddleware]
573
+ }
574
+ ```
575
+
576
+ - #### `CookieOptions`
577
+
578
+ ```typescript
579
+ interface CookieOptions {
580
+ maxAge?: number // Cookie lifetime in seconds
581
+ expires?: Date // Cookie expiration date
582
+ path?: string // Cookie path (default: '/')
583
+ domain?: string // Cookie domain
584
+ secure?: boolean // HTTPS only
585
+ httpOnly?: boolean // JavaScript cannot access
586
+ sameSite?: 'Strict' | 'Lax' | 'None' // CSRF protection
587
+ }
588
+ ```
589
+
590
+ - #### `SecurityConfig`
591
+
592
+ ```typescript
593
+ interface SecurityConfig {
594
+ cors?: CorsConfig | boolean
595
+ rateLimit?: RateLimitConfig | boolean
596
+ csrf?: CsrfConfig | boolean
597
+ helmet?: HelmetConfig | boolean
598
+ auth?: AuthConfig | boolean
599
+ }
600
+ ```
601
+
602
+ **CORS:**
603
+ ```typescript
604
+ security: {
605
+ cors: {
606
+ origin: '*' | string | string[] | (origin: string) => boolean
607
+ methods: ['GET', 'POST', 'PUT', 'DELETE']
608
+ allowedHeaders: ['Content-Type', 'Authorization']
609
+ credentials: true
610
+ maxAge: 3600
611
+ }
612
+ }
613
+ ```
614
+
615
+ **Rate Limiting:**
616
+ ```typescript
617
+ security: {
618
+ rateLimit: {
619
+ windowMs: 60000 // Time window in ms
620
+ max: 100 // Max requests per window
621
+ keyGenerator: (c) => c.ip // Custom key generator
622
+ message: 'Too many requests'
623
+ }
624
+ }
625
+ ```
626
+
627
+ - #### `LoggingConfig`
628
+
629
+ ```typescript
630
+ interface LoggingConfig {
631
+ level?: 'debug' | 'info' | 'warn' | 'error'
632
+ pretty?: boolean
633
+ }
634
+ ```
635
+
636
+ - #### `DatabaseConfig`
637
+
638
+ ```typescript
639
+ interface DatabaseConfig {
640
+ name?: string // Database name, default: 'default'
641
+ connection: string // Connection string or file path
642
+ timeout?: number // Connection timeout
643
+ }
644
+ ```
645
+
646
+ - #### `StaticConfig`
647
+
648
+ ```typescript
649
+ interface StaticConfig {
650
+ path: string // URL prefix (e.g., '/public')
651
+ directory: string // Local directory path
652
+ maxAge?: number // Cache duration in seconds
653
+ index?: string[] // Index files
654
+ dotfiles?: 'allow' | 'deny' | 'ignore'
655
+ etag?: boolean // Enable ETag headers
656
+ lastModified?: boolean // Enable Last-Modified headers
657
+ immutable?: boolean // Add immutable cache directive
658
+ extensions?: string[] // Try extensions
659
+ fallthrough?: boolean // Continue if file not found
660
+ setHeaders?: (ctx: AppContext, path: string) => void
661
+ }
662
+ ```
663
+
664
+ - #### `Error Classes`
665
+
666
+ ```typescript
667
+ class AppError extends Error {
668
+ statusCode: number
669
+ code?: string
670
+ }
671
+
672
+ class ValidationError extends AppError {
673
+ issues?: unknown
674
+ }
675
+
676
+ class DatabaseError extends AppError {}
677
+ class TimeoutError extends AppError {}
678
+ class RateLimitError extends AppError {}
679
+ ```
680
+
681
+ **Usage:**
682
+ ```typescript
683
+ import { server, AppError, ValidationError } from '@minejs/server'
684
+
685
+ const app = server({
686
+ port: 3000,
687
+ routes: [
688
+ {
689
+ method: 'GET',
690
+ path: '/error',
691
+ handler: (c) => {
692
+ throw new AppError('Something went wrong', 500, 'INTERNAL_ERROR')
693
+ }
694
+ },
695
+ {
696
+ method: 'POST',
697
+ path: '/validate',
698
+ handler: (c) => {
699
+ if (!c.body.email) {
700
+ throw new ValidationError('Email is required')
701
+ }
702
+ return c.json({ valid: true })
703
+ }
704
+ }
705
+ ]
706
+ })
707
+ ```
708
+
709
+ <br>
710
+
711
+ - ## Advanced Features 🚀
712
+
713
+ - ### Middleware Chain
714
+
715
+ Middlewares execute in order and can short-circuit the chain:
716
+
717
+ ```typescript
718
+ const authMiddleware: AppMiddleware = async (c, next) => {
719
+ const token = c.getHeader('Authorization')
720
+ if (!token) return c.json({ error: 'Unauthorized' }, 401)
721
+ await next() // Continue to next middleware/handler
722
+ }
723
+
724
+ const loggingMiddleware: AppMiddleware = async (c, next) => {
725
+ console.log('Before:', c.request.method, c.request.url)
726
+ await next()
727
+ console.log('After:', c.statusCode)
728
+ }
729
+
730
+ app.addRoute({
731
+ method: 'GET',
732
+ path: '/protected',
733
+ handler: (c) => c.json({ data: 'secret' }),
734
+ middlewares: [loggingMiddleware, authMiddleware]
735
+ })
736
+ ```
737
+
738
+ - ### Multiple Databases
739
+
740
+ ```typescript
741
+ const app = server({
742
+ port: 3000,
743
+ database: [
744
+ { name: 'primary', connection: './primary.db' },
745
+ { name: 'cache', connection: './cache.db' },
746
+ { name: 'logs', connection: ':memory:' }
747
+ ]
748
+ })
749
+
750
+ await app.start()
751
+
752
+ // Access in routes
753
+ const route = {
754
+ method: 'GET',
755
+ path: '/data',
756
+ handler: (c: AppContext) => {
757
+ const primary = app.db.get('primary')
758
+ const cache = app.db.get('cache')
759
+ return c.json({ primary, cache })
760
+ }
761
+ }
762
+ ```
763
+
764
+ - ### Dynamic Routes
765
+
766
+ ```typescript
767
+ const app = server({
768
+ port: 3000,
769
+ logging: false,
770
+ routes: [
771
+ {
772
+ method: 'GET',
773
+ path: '/api/users/:id',
774
+ handler: (c) => c.json({ userId: c.params.id })
775
+ },
776
+ {
777
+ method: 'GET',
778
+ path: '/download/*',
779
+ handler: (c) => c.file('./downloads/' + c.params[0])
780
+ }
781
+ ]
782
+ })
783
+
784
+ await app.start()
785
+ ```
786
+
787
+ - ### Error Handling
788
+
789
+ ```typescript
790
+ const app = server({
791
+ port: 3000,
792
+ errorHandler: async (error, context) => {
793
+ // Custom error handling
794
+ console.error('Error:', error.message)
795
+ },
796
+ onError: async (statusCode, path, method) => {
797
+ // Custom error pages
798
+ if (statusCode === 404) {
799
+ return new Response(
800
+ JSON.stringify({ error: 'Page not found', path }),
801
+ { status: 404, headers: { 'Content-Type': 'application/json' } }
802
+ )
803
+ }
804
+ return new Response('Error', { status: statusCode })
805
+ }
806
+ })
807
+ ```
808
+
809
+ - ### Health & Readiness Endpoints
810
+
811
+ Built-in endpoints for monitoring:
812
+
813
+ ```typescript
814
+ // GET /health - Server health status
815
+ // Response: { status: 'healthy', uptime: 123.45, activeRequests: 5, ... }
816
+
817
+ // GET /readiness - Server readiness status
818
+ // Response: { ready: true, checks: { database: 'connected', ... }, ... }
819
+ ```
820
+
821
+ - ### Request Lifecycle
822
+
823
+ 1. **Receive** - Request received by server
824
+ 2. **Parse** - URL, method, headers, body parsed
825
+ 3. **Security** - CORS, rate limiting, validation
826
+ 4. **Match** - Route matching from router
827
+ 5. **Context** - AppContext created
828
+ 6. **Middlewares** - Route middlewares execute in order
829
+ 7. **Handler** - Route handler executes
830
+ 8. **Response** - Response sent with security headers
831
+ 9. **Log** - Request logged with duration
832
+
833
+ <br>
834
+
835
+ <!-- ╚══════════════════════════════════════════════════════════════════╝ -->
836
+
837
+
838
+
839
+ <!-- ╔══════════════════════════════ END ══════════════════════════════╗ -->
840
+
841
+ <br>
842
+
843
+ ---
844
+
845
+ <div align="center">
846
+ <a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
847
+ </div>
848
+
849
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->