@je-es/server 0.1.2 → 0.1.3

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,13 @@
8
8
  </div>
9
9
 
10
10
  <div align="center">
11
- <img src="https://img.shields.io/badge/v-0.1.2-black"/>
12
- <a href="https://github.com/maysara-elshewehy">
13
- </a>
14
- <a href="https://github.com/je-es"> <img src="https://img.shields.io/badge/@-je--es-black"/> </a>
11
+ <img src="https://img.shields.io/badge/v-0.1.3-black"/>
12
+ <img src="https://img.shields.io/badge/🔥-@je--es-black"/>
13
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
14
+ <img src="https://github.com/je-es/server/actions/workflows/ci.yml/badge.svg" alt="CI" />
15
+ <img src="https://img.shields.io/badge/coverage-100%25-brightgreen" alt="Test Coverage" />
16
+ <img src="https://img.shields.io/github/issues/je-es/server?style=flat" alt="Github Repo Issues" />
17
+ <img src="https://img.shields.io/github/stars/je-es/server?style=social" alt="GitHub Repo stars" />
15
18
  </div>
16
19
  <br>
17
20
 
@@ -24,181 +27,175 @@
24
27
 
25
28
  - ## Quick Start 🔥
26
29
 
27
- > _**The simplest, fastest, most organized and stable way to build servers.**_
30
+ > _**The simplest, fastest, and most organized way to build production-ready servers with Bun.**_
28
31
 
29
32
  > _We prefer to use [`space`](https://github.com//solution-lib/space) with [`@solution-dist/server`](https://github.com/solution-dist/server) for a better experience._
30
33
 
31
- - #### Setup
34
+ - ### Setup
32
35
 
33
36
  > install [`space`](https://github.com/solution-lib/space) first.
34
37
 
35
- - ##### Create
38
+ - #### Create
36
39
 
37
40
  ```bash
38
- space init <name> -t server # This will clone a ready-to-use repo and make some changes to suit your server.
39
- cd <name> # Go to the project directory
40
- space install # Install the dependencies
41
+ > space init <name> -t server # This will clone a ready-to-use repo and make some changes to suit your server.
42
+ > cd <name> # Go to the project directory
43
+ > space install # Install the dependencies
41
44
  ```
42
45
 
43
- - ##### Manage
46
+ - #### Manage
44
47
 
45
48
  ```bash
46
- space build # To build your server
47
- space test # To test your server
48
- space start # To start your server
49
+ > space lint
50
+ > space build
51
+ > space test
52
+ > space start
53
+ ```
54
+
55
+ - #### Usage
56
+
57
+ ```typescript
58
+ import { server } from '@je-es/server';
59
+
60
+ const app = server({
61
+ port : 3000,
62
+ routes : [
63
+ {
64
+ method : 'GET',
65
+ path : '/',
66
+ handler : (c) => c.json({ message: 'Hello World!' })
67
+ }
68
+ ]
69
+ });
70
+
71
+ await app.start();
49
72
  ```
50
73
 
51
74
  ```bash
52
- # example
53
75
  > space start
54
-
55
- # output
56
- 16:16:31 ✓ Server started at http://localhost:3000
57
- 16:17:25 GET / 200 4ms
76
+ 16:16:31 Server started at http://localhost:3000
77
+ 16:17:25 GET / 200 1ms
58
78
  ...
59
79
  ```
60
80
 
61
- <br>
62
-
81
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
63
82
 
64
83
  - ## Examples
65
84
 
66
85
  - ### Basic Server
67
86
 
68
87
  ```typescript
69
- // import
70
88
  import { server } from '@je-es/server';
71
- ```
72
89
 
73
- ```typescript
74
- // create
75
90
  const app = server({
76
91
  port : 3000,
92
+ logging : { level: 'info', pretty: true },
77
93
  routes : [
78
- {
79
- method : 'GET',
80
- path : '/',
81
- handler : (c) => c.json({ message: 'Hello World!' })
82
- },
83
94
  {
84
95
  method : 'GET',
85
96
  path : '/users/:id',
86
- handler : (c) => {
87
- const userId = c.params.id;
88
- return c.json({ userId, name: 'John Doe' });
89
- }
97
+ handler : (c) => c.json({
98
+ userId : c.params.id,
99
+ ip : c.ip,
100
+ requestId : c.requestId
101
+ })
90
102
  },
91
103
  {
92
- method : 'POST',
93
- path : '/users',
94
- handler : (c) => {
95
- const userData = c.body;
96
- return c.json({ created: true, data: userData });
97
- }
104
+ method : 'POST',
105
+ path : '/users',
106
+ handler : (c) => c.status(201).json({
107
+ created : true,
108
+ data : c.body
109
+ })
98
110
  }
99
111
  ]
100
112
  });
101
- ```
102
113
 
103
- ```typescript
104
- // start
105
114
  await app.start();
106
115
  ```
107
116
 
108
- > use `space start` to run your server.
109
-
110
- ```typescript
111
- > space start
112
-
113
- → URL: http://localhost:3000
114
- → Environment: test
115
- → Routes: N
116
- ...
117
- ```
118
-
119
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
117
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
120
118
 
121
119
  - ### With Database
122
120
 
123
121
  ```typescript
124
122
  import { server, table, integer, text, primaryKey, notNull } from '@je-es/server';
125
123
 
126
- // Define your schema using the built-in schema builder
127
124
  const users = table('users', [
128
- primaryKey(integer('id'), true), // auto-increment primary key
125
+ primaryKey(integer('id'), true),
129
126
  notNull(text('name')),
130
127
  notNull(text('email'))
131
128
  ]);
132
129
 
133
130
  const app = server({
134
- port : 3000,
135
- database: {
136
- connection : './my_app.db', // File-based SQLite database
131
+ port : 3000,
132
+ database : {
133
+ connection : './app.db',
137
134
  schema : { users }
138
135
  },
139
- routes : [
136
+ routes: [
140
137
  {
141
138
  method : 'GET',
142
139
  path : '/users',
143
- handler : (c) => {
144
- const allUsers = c.db.all('users');
145
- return c.json(allUsers);
146
- }
140
+ handler : (c) => c.json(c.db!.all('users'))
147
141
  },
148
142
  {
149
143
  method : 'POST',
150
144
  path : '/users',
145
+ handler : (c) => c.json(c.db!.insert('users', c.body))
146
+ },
147
+ {
148
+ method : 'GET',
149
+ path : '/users/:id',
151
150
  handler : (c) => {
152
- const newUser = c.db.insert('users', c.body);
153
- return c.json(newUser);
151
+ const user = c.db!.findById('users', parseInt(c.params.id));
152
+ return user
153
+ ? c.json(user)
154
+ : c.status(404).json({ error: 'Not found' });
154
155
  }
155
156
  }
156
157
  ]
157
158
  });
158
159
 
159
160
  await app.start();
160
- // Data persists in ./my_app.db file!
161
161
  ```
162
162
 
163
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
163
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
164
164
 
165
165
  - ### With Security
166
166
 
167
167
  ```typescript
168
- import { server } from '@je-es/server';
169
-
170
168
  const app = server({
171
- port : 3000,
172
- security: {
173
- // Rate limiting
174
- rateLimit: {
175
- max : 100,
176
- windowMs : 60000, // 1 minute
177
- message : 'Too many requests, please try again later'
178
- },
179
- // CORS configuration
180
- cors: {
181
- origin : ['http://localhost:3000', 'https://example.com'],
182
- credentials : true,
183
- methods : ['GET', 'POST', 'PUT', 'DELETE'],
184
- allowedHeaders : ['Content-Type', 'Authorization']
169
+ port : 3000,
170
+ security : {
171
+ rateLimit : { max: 100, windowMs: 60000 },
172
+ cors : {
173
+ origin : ['http://localhost:3000'],
174
+ credentials : true
185
175
  }
186
176
  },
187
- routes : [
188
- {
189
- method : 'GET',
190
- path : '/protected',
191
- handler : (c) => c.json({ message: 'This route is protected!' })
192
- }
193
- ]
177
+ routes: [/* your routes */]
194
178
  });
179
+ ```
195
180
 
196
- await app.start();
181
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
182
+
183
+ - ### Static Files
184
+
185
+ ```typescript
186
+ const app = server({
187
+ port : 3000,
188
+ static : {
189
+ path : '/public',
190
+ directory : './public',
191
+ maxAge : 3600
192
+ }
193
+ });
197
194
  ```
198
195
 
199
- <br>
196
+ <br>
200
197
 
201
- - ## API Reference
198
+ - ## API
202
199
 
203
200
  - ### Server Configuration
204
201
 
@@ -206,866 +203,364 @@
206
203
  import { server, type ServerConfig } from '@je-es/server';
207
204
 
208
205
  const config: ServerConfig = {
209
- // Basic settings
210
- port : 3000,
211
- hostname : 'localhost',
206
+ port : 3000,
207
+ hostname : 'localhost',
212
208
 
213
- // Request handling
214
- requestTimeout : 30000, // 30 seconds
215
- maxRequestSize : 10485760, // 10MB
216
- gracefulShutdownTimeout : 10000, // 10 seconds
209
+ // Timeouts & Limits
210
+ requestTimeout : 30000,
211
+ maxRequestSize : 10485760,
212
+ gracefulShutdownTimeout : 10000,
217
213
 
218
- // Logging
214
+ // Logging (via @je-es/slog)
219
215
  logging: {
220
- level : 'info', // 'debug' | 'info' | 'warn' | 'error'
221
- pretty : false // Enable pretty printing
216
+ level : 'info', // 'debug' | 'info' | 'warn' | 'error'
217
+ pretty : false
222
218
  },
223
219
 
224
- // Database
220
+ // Database (via @je-es/sdb)
225
221
  database: {
226
- connection : ':memory:', // or file path like './app.db'
227
- schema : {}
222
+ connection : './app.db', // or ':memory:'
223
+ schema : {}
228
224
  },
229
225
 
226
+ // Multiple databases
227
+ database: [
228
+ { name: 'default', connection: './main.db' },
229
+ { name: 'cache', connection: ':memory:' }
230
+ ],
231
+
230
232
  // Security
231
233
  security: {
232
- rateLimit : true,
233
- cors : true,
234
- csrf : true
234
+ rateLimit : { max: 100, windowMs: 60000 },
235
+ cors : { origin: '*', credentials: true }
235
236
  },
236
237
 
237
- // Routes
238
- routes: [],
239
-
240
- // Lifecycle hooks
241
- onShutdown: async () => {
242
- console.log('Server shutting down...');
243
- }
244
- };
245
-
246
- const app = server(config);
247
- await app.start();
248
- ```
249
-
250
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
251
-
252
- - ### Route Definition
253
-
254
- ```typescript
255
- import { type RouteDefinition, type AppContext } from '@je-es/server';
256
-
257
- // Single HTTP method
258
- const route: RouteDefinition = {
259
- method : 'GET',
260
- path : '/users/:id',
261
- handler : (c: AppContext) => {
262
- return c.json({ id: c.params.id });
263
- }
264
- };
265
-
266
- // Multiple HTTP methods
267
- const multiMethodRoute: RouteDefinition = {
268
- method : ['GET', 'POST'],
269
- path : '/api/resource',
270
- handler : (c: AppContext) => {
271
- if (c.request.method === 'GET') {
272
- return c.json({ method : 'GET' });
273
- }
274
- return c.json({ method : 'POST', body: c.body });
275
- }
276
- };
238
+ // Static files
239
+ static: {
240
+ path : '/public',
241
+ directory : './public',
242
+ maxAge : 3600
243
+ },
277
244
 
278
- // Dynamic routes with nested parameters
279
- const nestedRoute: RouteDefinition = {
280
- method : 'GET',
281
- path : '/posts/:postId/comments/:commentId',
282
- handler : (c: AppContext) => {
283
- return c.json({
284
- postId: c.params.postId,
285
- commentId: c.params.commentId
286
- });
287
- }
245
+ // Lifecycle
246
+ onShutdown : async () => { console.log('Shutting down...'); }
288
247
  };
289
248
  ```
290
249
 
291
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
250
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
292
251
 
293
252
  - ### Context API
294
253
 
295
254
  ```typescript
296
- import { type AppContext } from '@je-es/server';
297
-
298
- // Response methods
299
- handler : (c: AppContext) => {
300
- // JSON response
301
- c.json({ data: 'value' }, 200);
302
-
303
- // Text response
304
- c.text('Hello World', 200);
305
-
306
- // HTML response
307
- c.html('<h1>Hello</h1>', 200);
308
-
309
- // Redirect
310
- c.redirect('/new-location', 302);
311
-
312
- // File response
313
- c.file('./path/to/file.pdf', 'application/pdf');
314
-
315
- // Chain status
316
- c.status(201).json({ created: true });
317
- }
318
-
319
- // Request data
320
- handler : (c: AppContext) => {
321
- const params = c.params; // URL parameters
322
- const query = c.query; // Query string
323
- const body = c.body; // Request body
324
- const headers = c.headers; // Request headers
325
- const db = c.db; // Database instance
326
- const logger = c.logger; // Logger instance
327
- const requestId = c.requestId; // Unique request ID
328
- }
329
-
330
- // Headers
331
255
  handler : (c: AppContext) => {
332
- c.setHeader('X-Custom-Header', 'value');
333
- const auth = c.getHeader('Authorization');
334
- }
335
-
336
- // Cookies
337
- handler : (c: AppContext) => {
338
- c.setCookie('session', 'abc123', {
339
- maxAge: 3600,
340
- httpOnly: true,
341
- secure: true,
342
- sameSite: 'Strict'
343
- });
344
-
345
- const session = c.getCookie('session');
346
- c.deleteCookie('session');
256
+ // Request
257
+ c.params // URL parameters
258
+ c.query // Query string
259
+ c.body // Parsed body (JSON/form/multipart)
260
+ c.headers // Request headers
261
+ c.ip // Client IP
262
+ c.requestId // Unique request ID
263
+
264
+ // Resources
265
+ c.db // Database instance
266
+ c.logger // Logger instance
267
+
268
+ // Response
269
+ c.json({ data })
270
+ c.text('text')
271
+ c.html('HTML')
272
+ c.redirect('/path')
273
+ c.file('./file.pdf', 'application/pdf')
274
+ c.status(201).json({ created: true })
275
+
276
+ // Headers
277
+ c.setHeader('X-Custom', 'value')
278
+ c.getHeader('Authorization')
279
+
280
+ // Cookies
281
+ c.setCookie('session', 'token', { httpOnly: true })
282
+ c.getCookie('session')
283
+ c.deleteCookie('session')
347
284
  }
348
285
  ```
349
286
 
350
- <br>
287
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
351
288
 
352
- - ## Security Features
353
-
354
- - ### Rate Limiting
289
+ - ### Routes
355
290
 
356
291
  ```typescript
357
- const app = server({
358
- security: {
359
- rateLimit: {
360
- max: 100, // Max requests per window
361
- windowMs: 60000, // Time window in milliseconds
362
- keyGenerator: (c) => {
363
- // Custom key generation (default: IP address)
364
- return c.headers.get('x-api-key') || c.request.ip;
365
- },
366
- message: 'Rate limit exceeded'
367
- }
368
- }
369
- });
370
- ```
292
+ // Single method
293
+ { method : 'GET', path : '/users', handler }
371
294
 
372
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
295
+ // Multiple methods
296
+ { method : ['GET', 'POST'], path : '/api', handler }
373
297
 
374
- - ### CORS Configuration
298
+ // Dynamic parameters
299
+ { method : 'GET', path : '/users/:id', handler }
300
+ { method : 'GET', path : '/posts/:postId/comments/:commentId', handler }
375
301
 
376
- ```typescript
377
- const app = server({
378
- security: {
379
- cors: {
380
- // Allow specific origins
381
- origin: ['http://localhost:3000', 'https://example.com'],
382
-
383
- // Or use a function
384
- origin: (origin) => {
385
- return origin.endsWith('.example.com');
386
- },
387
-
388
- // Or allow all
389
- origin: '*',
390
-
391
- methods: ['GET', 'POST', 'PUT', 'DELETE'],
392
- allowedHeaders: ['Content-Type', 'Authorization'],
393
- credentials: true,
394
- maxAge: 86400 // 24 hours
395
- }
396
- }
397
- });
302
+ // Wildcards
303
+ { method : 'GET', path : '/files/*', handler }
398
304
  ```
399
305
 
400
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
306
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
401
307
 
402
- - ### CSRF Protection
308
+ - ### Database Operations
403
309
 
404
310
  ```typescript
405
- import { SecurityManager } from '@je-es/server';
406
-
407
- const security = new SecurityManager();
408
-
409
- // Generate CSRF token
410
- const token = security.generateCsrfToken('session-id', 3600000); // 1 hour TTL
411
-
412
- // Validate CSRF token
413
- const isValid = security.validateCsrfToken(token, 'session-id');
311
+ // CRUD
312
+ c.db!.all ('users')
313
+ c.db!.findById ('users', 1)
314
+ c.db!.find ('users', { role: 'admin' })
315
+ c.db!.insert ('users', { name: 'John' })
316
+ c.db!.update ('users', 1, { name: 'Jane' })
317
+ c.db!.delete ('users', 1)
318
+
319
+ // Multiple databases
320
+ const mainDb = app.db.get('default');
321
+ const cacheDb = app.db.get('cache');
414
322
  ```
415
323
 
416
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
324
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
417
325
 
418
- - ### Input Sanitization
326
+ - ### Dynamic Routes
419
327
 
420
328
  ```typescript
421
- import { SecurityManager } from '@je-es/server';
329
+ await app.start();
422
330
 
423
- const security = new SecurityManager();
331
+ // Add single route
332
+ app.addRoute({
333
+ method : 'POST',
334
+ path : '/dynamic',
335
+ handler : (c) => c.json({ dynamic: true })
336
+ });
424
337
 
425
- // Sanitize HTML
426
- const cleanHtml = security.sanitizeHtml('<script>alert("xss")</script>');
427
- // Output: &lt;script&gt;alert(&quot;xss&quot;)&lt;&#x2F;script&gt;
338
+ // Add multiple routes
339
+ app.addRoutes([
340
+ { method : 'GET', path : '/route1', handler },
341
+ { method : 'GET', path : '/route2', handler }
342
+ ]);
428
343
 
429
- // Sanitize SQL
430
- const cleanSql = security.sanitizeSql("'; DROP TABLE users--");
431
- // Output: ''; DROP TABLE users--
344
+ // Get all routes
345
+ const routes = app.getRoutes();
432
346
  ```
433
347
 
434
- <br>
348
+ <br>
435
349
 
436
- - ## Database Support
350
+ - ## Security
437
351
 
438
- - ### Single Database
352
+ - ### Rate Limiting
439
353
 
440
354
  ```typescript
441
- import { server } from '@je-es/server';
442
-
443
- const app = server({
444
- database: {
445
- connection: './my_app.db' // ✔ File-based SQLite - data persists!
446
- // or ':memory:' for in-memory database
355
+ security: {
356
+ rateLimit: {
357
+ max : 100,
358
+ windowMs : 60000,
359
+ keyGenerator : (c) => c.ip,
360
+ message : 'Too many requests'
447
361
  }
448
- });
449
-
450
- // Access in routes via c.db
451
- ```
452
-
453
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
454
-
455
- - ### Multiple Databases
456
-
457
- ```typescript
458
- import { server } from '@je-es/server';
459
-
460
- const app = server({
461
- database: [
462
- {
463
- name: 'default',
464
- connection: './main.db' // Main database file
465
- },
466
- {
467
- name: 'analytics',
468
- connection: './analytics.db' // Analytics database file
469
- }
470
- ],
471
- routes : [
472
- {
473
- method : 'GET',
474
- path : '/data',
475
- handler : (c) => {
476
- // Access default database
477
- const users = c.db.all('users');
478
-
479
- // Access named databases
480
- const mainDb = app.db.get('default');
481
- const analyticsDb = app.db.get('analytics');
482
-
483
- const mainData = mainDb.all('some_table');
484
- const analyticsData = analyticsDb.all('analytics_table');
485
-
486
- return c.json({ users, mainData, analyticsData });
487
- }
488
- }
489
- ]
490
- });
362
+ }
491
363
  ```
492
364
 
493
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
365
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
494
366
 
495
- - ### Schema Definition
367
+ - ### CORS
496
368
 
497
369
  ```typescript
498
- import {
499
- server,
500
- table,
501
- integer,
502
- text,
503
- real,
504
- blob,
505
- numeric,
506
- primaryKey,
507
- notNull,
508
- unique,
509
- defaultValue,
510
- references
511
- } from '@je-es/server';
512
-
513
- // Define products table
514
- const products = table('products', [
515
- primaryKey(integer('id'), true), // Auto-increment primary key
516
- notNull(text('name')),
517
- text('description'),
518
- notNull(real('price')),
519
- defaultValue(integer('stock'), 0)
520
- ]);
521
-
522
- // Define orders table with foreign key
523
- const orders = table('orders', [
524
- primaryKey(integer('id'), true),
525
- notNull(integer('product_id')),
526
- references(integer('product_id'), 'products', 'id'),
527
- notNull(integer('quantity')),
528
- defaultValue(text('status'), 'pending')
529
- ]);
530
-
531
- const app = server({
532
- database: {
533
- connection: './store.db',
534
- schema: { products, orders }
370
+ security: {
371
+ cors: {
372
+ origin : ['http://localhost:3000'],
373
+ // or: origin : '*',
374
+ // or: origin : (origin) => origin.endsWith('.example.com'),
375
+ methods : ['GET', 'POST', 'PUT', 'DELETE'],
376
+ credentials : true
535
377
  }
536
- });
537
- ```
538
-
539
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
540
-
541
- - ### Database Operations
542
-
543
- ```typescript
544
- import { server, table, integer, text, primaryKey, notNull } from '@je-es/server';
545
-
546
- const users = table('users', [
547
- primaryKey(integer('id'), true),
548
- notNull(text('name')),
549
- notNull(text('email')),
550
- integer('age')
551
- ]);
552
-
553
- const app = server({
554
- database: {
555
- connection: './app.db',
556
- schema: { users }
557
- },
558
- routes : [
559
- // Get all records
560
- {
561
- method : 'GET',
562
- path : '/users',
563
- handler : (c) => {
564
- const allUsers = c.db.all('users');
565
- return c.json(allUsers);
566
- }
567
- },
568
-
569
- // Find by ID
570
- {
571
- method : 'GET',
572
- path : '/users/:id',
573
- handler : (c) => {
574
- const user = c.db.findById('users', parseInt(c.params.id));
575
- if (!user) return c.status(404).json({ error: 'Not found' });
576
- return c.json(user);
577
- }
578
- },
579
-
580
- // Find with conditions
581
- {
582
- method : 'GET',
583
- path : '/users/search',
584
- handler : (c) => {
585
- const users = c.db.find('users', {
586
- name: c.query.name
587
- });
588
- return c.json(users);
589
- }
590
- },
591
-
592
- // Insert
593
- {
594
- method : 'POST',
595
- path : '/users',
596
- handler : (c) => {
597
- const newUser = c.db.insert('users', c.body);
598
- return c.json(newUser);
599
- }
600
- },
601
-
602
- // Update
603
- {
604
- method : 'PUT',
605
- path : '/users/:id',
606
- handler : (c) => {
607
- const updated = c.db.update(
608
- 'users',
609
- parseInt(c.params.id),
610
- c.body
611
- );
612
- if (!updated) return c.status(404).json({ error: 'Not found' });
613
- return c.json(updated);
614
- }
615
- },
616
-
617
- // Delete
618
- {
619
- method : 'DELETE',
620
- path : '/users/:id',
621
- handler : (c) => {
622
- c.db.delete('users', parseInt(c.params.id));
623
- return c.json({ deleted: true });
624
- }
625
- }
626
- ]
627
- });
378
+ }
628
379
  ```
629
380
 
630
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
381
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
631
382
 
632
- - ### Query Builder
383
+ - ### Input Sanitization
633
384
 
634
385
  ```typescript
635
- const app = server({
636
- database: {
637
- connection: './app.db',
638
- schema: { users }
639
- },
640
- routes : [
641
- {
642
- method : 'GET',
643
- path : '/advanced-search',
644
- handler : (c) => {
645
- // Complex queries with query builder
646
- const results = c.db.query()
647
- .select(['name', 'email', 'age'])
648
- .from('users')
649
- .where({
650
- column: 'age',
651
- operator: '>=',
652
- value: 18
653
- })
654
- .and({
655
- column: 'name',
656
- operator: 'LIKE',
657
- value: '%John%'
658
- })
659
- .orderBy('age', 'DESC')
660
- .limit(10)
661
- .offset(0)
662
- .execute();
663
-
664
- return c.json(results);
665
- }
666
- },
667
-
668
- // Multiple where conditions
669
- {
670
- method : 'GET',
671
- path : '/filter',
672
- handler : (c) => {
673
- const users = c.db.query()
674
- .select()
675
- .from('users')
676
- .where([
677
- { column: 'age', operator: '>', value: 25 },
678
- { column: 'age', operator: '<', value: 50 }
679
- ])
680
- .execute();
681
-
682
- return c.json(users);
683
- }
684
- },
685
-
686
- // OR conditions
687
- {
688
- method : 'GET',
689
- path : '/or-search',
690
- handler : (c) => {
691
- const users = c.db.query()
692
- .select()
693
- .from('users')
694
- .where({ column: 'name', operator: '=', value: 'John' })
695
- .or({ column: 'name', operator: '=', value: 'Jane' })
696
- .execute();
697
-
698
- return c.json(users);
699
- }
700
- },
386
+ import { SecurityManager } from '@je-es/server';
701
387
 
702
- // Get single result
703
- {
704
- method : 'GET',
705
- path : '/first-user',
706
- handler : (c) => {
707
- const user = c.db.query()
708
- .select()
709
- .from('users')
710
- .limit(1)
711
- .executeOne();
388
+ const security = new SecurityManager();
712
389
 
713
- return c.json(user);
714
- }
715
- }
716
- ]
717
- });
390
+ security.sanitizeHtml('xss');
391
+ security.sanitizeSql("'; DROP TABLE users--");
718
392
  ```
719
393
 
720
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
394
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
721
395
 
722
- - ### Transactions
396
+ - ### CSRF Protection
723
397
 
724
398
  ```typescript
725
- const app = server({
726
- database: {
727
- connection: './app.db',
728
- schema: { users, orders }
729
- },
730
- routes : [
731
- {
732
- method : 'POST',
733
- path : '/place-order',
734
- handler : (c) => {
735
- try {
736
- c.db.transaction((db) => {
737
- // Insert order
738
- const order = db.insert('orders', {
739
- product_id: c.body.productId,
740
- quantity: c.body.quantity
741
- });
742
-
743
- // Update product stock
744
- const product = db.findById('products', c.body.productId);
745
- db.update('products', c.body.productId, {
746
- stock: product.stock - c.body.quantity
747
- });
748
- });
749
-
750
- return c.json({ success: true });
751
- } catch (error) {
752
- return c.status(500).json({
753
- error: 'Transaction failed'
754
- });
755
- }
756
- }
757
- }
758
- ]
759
- });
399
+ const security = new SecurityManager();
400
+ const token = security.generateCsrfToken('session-id');
401
+ const valid = security.validateCsrfToken(token, 'session-id');
760
402
  ```
761
403
 
762
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
763
-
764
- - ### Raw SQL
765
-
766
- ```typescript
767
- const app = server({
768
- database: {
769
- connection: './app.db',
770
- schema: { users }
771
- },
772
- routes : [
773
- {
774
- method : 'GET',
775
- path : '/custom-query',
776
- handler : (c) => {
777
- // Execute raw SQL
778
- const results = c.db.raw(
779
- 'SELECT * FROM users WHERE age > ? AND name LIKE ?',
780
- [25, '%John%']
781
- );
782
-
783
- return c.json(results);
784
- }
785
- },
404
+ <br>
786
405
 
787
- {
788
- method : 'GET',
789
- path : '/single-result',
790
- handler : (c) => {
791
- // Get single row
792
- const user = c.db.rawOne(
793
- 'SELECT * FROM users WHERE id = ?',
794
- [1]
795
- );
406
+ - ## Error Handling
796
407
 
797
- return c.json(user);
798
- }
799
- },
408
+ ```typescript
409
+ import {
410
+ AppError, // Custom errors
411
+ ValidationError, // 400
412
+ DatabaseError, // 500
413
+ TimeoutError, // 408
414
+ RateLimitError // 429
415
+ } from '@je-es/server';
800
416
 
801
- {
802
- method : 'POST',
803
- path : '/execute-sql',
804
- handler : (c) => {
805
- // Execute without return
806
- c.db.exec('DELETE FROM users WHERE age < 18');
417
+ handler : (c) => {
418
+ if (!c.body?.email) {
419
+ throw new ValidationError('Email required');
420
+ }
807
421
 
808
- return c.json({ success: true });
809
- }
810
- }
811
- ]
812
- });
813
- ```
422
+ if (invalid) {
423
+ throw new AppError('Invalid data', 400, 'INVALID_DATA');
424
+ }
814
425
 
815
- <br>
426
+ return c.json({ success: true });
427
+ }
428
+ ```
816
429
 
817
- - ## Advanced Features
430
+ <br>
818
431
 
819
- - ### Logging
432
+ - ## Built-in Endpoints
820
433
 
821
- ```typescript
822
- import { server, Logger } from '@je-es/server';
434
+ ```typescript
435
+ // Health check
436
+ GET /health
437
+ // Response: { status, timestamp, uptime, activeRequests }
823
438
 
824
- // Enable logging
825
- const app = server({
826
- logging: {
827
- level: 'debug', // 'debug' | 'info' | 'warn' | 'error' | 'fatal'
828
- pretty: true // Pretty print for development
829
- }
830
- });
439
+ // Readiness check
440
+ GET /readiness
441
+ // Response: { ready, checks: { database, activeRequests }, timestamp }
442
+ ```
831
443
 
832
- // Use logger in routes
833
- const route = {
834
- method : 'GET',
835
- path : '/test',
836
- handler : (c) => {
837
- c.logger?.info({ userId: 123 }, 'User accessed endpoint');
838
- c.logger?.warn({ attempt: 3 }, 'Suspicious activity');
839
- c.logger?.error({ error: 'DB connection failed' }, 'Database error');
840
- return c.json({ ok: true });
841
- }
842
- };
843
- ```
444
+ <br>
844
445
 
845
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
446
+ - ## Advanced
846
447
 
847
448
  - ### Cookie Management
848
449
 
849
450
  ```typescript
850
- const app = server({
851
- routes : [
852
- {
853
- method : 'POST',
854
- path : '/login',
855
- handler : (c) => {
856
- // Set cookie with options
857
- c.setCookie('session', 'user-token-123', {
858
- maxAge: 3600, // 1 hour
859
- expires: new Date('2025-12-31'),
860
- path : '/',
861
- domain: 'example.com',
862
- secure: true, // HTTPS only
863
- httpOnly: true, // No JavaScript access
864
- sameSite: 'Strict' // CSRF protection
865
- });
866
-
867
- return c.json({ loggedIn: true });
868
- }
869
- },
870
- {
871
- method : 'GET',
872
- path : '/profile',
873
- handler : (c) => {
874
- const session = c.getCookie('session');
875
- if (!session) {
876
- return c.status(401).json({ error: 'Unauthorized' });
877
- }
878
- return c.json({ session });
879
- }
880
- },
881
- {
882
- method : 'POST',
883
- path : '/logout',
884
- handler : (c) => {
885
- c.deleteCookie('session');
886
- return c.json({ loggedOut: true });
887
- }
888
- }
889
- ]
890
- });
891
- ```
892
-
893
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
894
-
895
- - ### Dynamic Routing
896
-
897
- ```typescript
898
- const app = server({
899
- routes : [
900
- // Simple parameter
901
- {
902
- method : 'GET',
903
- path : '/users/:id',
904
- handler : (c) => c.json({ userId: c.params.id })
905
- },
906
-
907
- // Multiple parameters
908
- {
909
- method : 'GET',
910
- path : '/posts/:postId/comments/:commentId',
911
- handler : (c) => c.json({
912
- postId: c.params.postId,
913
- commentId: c.params.commentId
914
- })
915
- },
916
-
917
- // Complex patterns
918
- {
919
- method : 'GET',
920
- path : '/api/:version/:resource',
921
- handler : (c) => c.json({
922
- version: c.params.version,
923
- resource: c.params.resource
924
- })
925
- }
926
- ]
451
+ c.setCookie('session', 'token', {
452
+ maxAge : 3600,
453
+ httpOnly : true,
454
+ secure : true,
455
+ sameSite : 'Strict',
456
+ path : '/',
457
+ domain : 'example.com'
927
458
  });
928
459
  ```
929
460
 
930
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
461
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
931
462
 
932
- - ### Health Checks
463
+ - ### Static File Options
933
464
 
934
465
  ```typescript
935
- // Built-in health endpoints are automatically available
936
-
937
- // GET /health
938
- // Response:
939
- {
940
- status: 'healthy',
941
- timestamp: '2025-11-28T10:00:00.000Z',
942
- uptime: 3600,
943
- activeRequests: 5
944
- }
945
-
946
- // GET /readiness
947
- // Response:
948
- {
949
- ready: true,
950
- checks: {
951
- database: 'connected', // or 'not configured'
952
- activeRequests: 5
953
- },
954
- timestamp: '2025-11-28T10:00:00.000Z'
466
+ static: {
467
+ path : '/public',
468
+ directory : './public',
469
+ maxAge : 3600,
470
+ index : ['index.html'],
471
+ dotfiles : 'deny', // 'allow' | 'deny' | 'ignore'
472
+ etag : true,
473
+ lastModified : true,
474
+ immutable : false,
475
+ extensions : ['html', 'htm'],
476
+ setHeaders : (ctx, path) => {
477
+ ctx.setHeader('X-Custom', 'value');
478
+ }
955
479
  }
956
480
  ```
957
481
 
958
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
482
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
959
483
 
960
484
  - ### Graceful Shutdown
961
485
 
962
486
  ```typescript
963
487
  const app = server({
964
- gracefulShutdownTimeout: 10000, // 10 seconds
488
+ gracefulShutdownTimeout: 10000,
965
489
  onShutdown: async () => {
966
- console.log('Cleaning up resources...');
967
- // Close external connections, flush logs, etc.
490
+ // Cleanup
968
491
  }
969
492
  });
970
493
 
971
- await app.start();
972
-
973
- // Handle signals
974
494
  process.on('SIGTERM', async () => {
975
495
  await app.stop();
976
496
  process.exit(0);
977
497
  });
978
-
979
- process.on('SIGINT', async () => {
980
- await app.stop();
981
- process.exit(0);
982
- });
983
498
  ```
984
499
 
985
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
500
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
986
501
 
987
- - ### Dynamic Routes
502
+ - ### Logging
988
503
 
989
504
  ```typescript
990
- const app = server({
991
- routes : [
992
- {
993
- method : 'GET',
994
- path : '/initial',
995
- handler : (c) => c.json({ route: 'initial' })
996
- }
997
- ]
998
- });
999
-
1000
- await app.start();
1001
-
1002
- // Add routes after server starts
1003
- app.addRoute({
1004
- method : 'POST',
1005
- path : '/dynamic',
1006
- handler : (c) => c.json({ route: 'dynamic', body: c.body })
1007
- });
1008
-
1009
- // Get all registered routes
1010
- const routes = app.getRoutes();
1011
- console.log(routes);
505
+ handler : (c) => {
506
+ c.logger?.info( { userId: 123 }, 'User action');
507
+ c.logger?.warn( { attempt: 3 }, 'Warning');
508
+ c.logger?.error({ error: 'msg' }, 'Error');
509
+ }
1012
510
  ```
1013
511
 
1014
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
1015
-
1016
- - ### Request Timeout
1017
-
1018
- ```typescript
1019
- const app = server({
1020
- requestTimeout : 5000, // 5 seconds
1021
- routes : [
1022
- {
1023
- method : 'GET',
1024
- path : '/slow',
1025
- handler : async (c) => {
1026
- // If this takes more than 5 seconds, request will timeout
1027
- await someSlowOperation();
1028
- return c.json({ done: true });
512
+ <br>
513
+
514
+ - ## Complete Example
515
+
516
+ ```typescript
517
+ import { server, table, integer, text, primaryKey, notNull } from '@je-es/server';
518
+
519
+ const users = table('users', [
520
+ primaryKey(integer('id'), true),
521
+ notNull(text('name')),
522
+ notNull(text('email'))
523
+ ]);
524
+
525
+ const app = server({
526
+ port : 3000,
527
+ logging : { level: 'info', pretty: true },
528
+ database : { connection: './app.db', schema: { users } },
529
+ security : {
530
+ rateLimit : { max: 100, windowMs: 60000 },
531
+ cors : { origin: ['http://localhost:3000'] }
532
+ },
533
+ static : { path: '/public', directory: './public' },
534
+ routes : [
535
+ {
536
+ method : 'GET',
537
+ path : '/api/users',
538
+ handler : (c) => c.json(c.db!.all('users'))
539
+ },
540
+ {
541
+ method : 'POST',
542
+ path : '/api/users',
543
+ handler : (c) => {
544
+ if (!c.body?.name || !c.body?.email) {
545
+ throw new ValidationError('Name and email required');
1029
546
  }
547
+ const user = c.db!.insert('users', c.body);
548
+ return c.status(201).json(user);
1030
549
  }
1031
- ]
1032
- });
1033
- ```
1034
-
1035
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
1036
-
1037
- - ### Custom Error Handling
1038
-
1039
- ```typescript
1040
- import { AppError, ValidationError } from '@je-es/server';
1041
-
1042
- const app = server({
1043
- routes : [
1044
- {
1045
- method : 'POST',
1046
- path : '/validate',
1047
- handler : (c) => {
1048
- if (!c.body?.email) {
1049
- throw new ValidationError('Email is required');
1050
- }
1051
-
1052
- if (!c.body.email.includes('@')) {
1053
- throw new AppError('Invalid email format', 400, 'INVALID_EMAIL');
1054
- }
1055
-
1056
- return c.json({ valid: true });
1057
- }
550
+ },
551
+ {
552
+ method : 'GET',
553
+ path : '/api/users/:id',
554
+ handler : (c) => {
555
+ const user = c.db!.findById('users', parseInt(c.params.id));
556
+ return user ? c.json(user) : c.status(404).json({ error: 'Not found' });
1058
557
  }
1059
- ]
1060
- });
558
+ }
559
+ ]
560
+ });
1061
561
 
1062
- // Error responses are automatically formatted:
1063
- {
1064
- error : 'Email is required',
1065
- code : 'VALIDATION_ERROR',
1066
- requestId : 'unique-request-id'
1067
- }
1068
- ```
562
+ await app.start();
563
+ ```
1069
564
 
1070
565
  <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
1071
566