vortez 5.0.0-dev.18 → 5.0.0-dev.19

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.
Files changed (66) hide show
  1. package/.gitignore +9 -4
  2. package/README.md +681 -176
  3. package/build/Vortez.d.ts +1 -0
  4. package/build/Vortez.js +1 -0
  5. package/build/Vortez.js.map +1 -1
  6. package/build/server/Response.d.ts +1 -1
  7. package/build/server/Response.js +1 -1
  8. package/build/server/Response.js.map +1 -1
  9. package/build/server/Server.d.ts +4 -4
  10. package/build/server/Server.js +5 -5
  11. package/build/server/Server.js.map +1 -1
  12. package/build/server/ServerDebug.d.ts +10 -1
  13. package/build/server/ServerDebug.js +85 -17
  14. package/build/server/ServerDebug.js.map +1 -1
  15. package/build/server/config/Config.d.ts +274 -47
  16. package/build/server/config/Config.js +68 -47
  17. package/build/server/config/Config.js.map +1 -1
  18. package/build/server/config/{ConfigLoader.d.ts → Loader.d.ts} +4 -5
  19. package/build/server/config/{ConfigLoader.js → Loader.js} +7 -10
  20. package/build/server/config/Loader.js.map +1 -0
  21. package/build/server/router/Router.d.ts +87 -30
  22. package/build/server/router/Router.js +110 -48
  23. package/build/server/router/Router.js.map +1 -1
  24. package/build/server/router/algorithm/Algorithm.d.ts +39 -0
  25. package/build/server/router/algorithm/Algorithm.js +20 -0
  26. package/build/server/router/algorithm/Algorithm.js.map +1 -0
  27. package/build/server/router/algorithm/FIFO.d.ts +15 -0
  28. package/build/server/router/algorithm/FIFO.js +24 -0
  29. package/build/server/router/algorithm/FIFO.js.map +1 -0
  30. package/build/server/router/algorithm/Tree.d.ts +38 -0
  31. package/build/server/router/algorithm/Tree.js +126 -0
  32. package/build/server/router/algorithm/Tree.js.map +1 -0
  33. package/build/server/router/middleware/WsMiddleware.js +1 -1
  34. package/build/server/router/middleware/WsMiddleware.js.map +1 -1
  35. package/build/utilities/Flatten.d.ts +56 -0
  36. package/build/utilities/Flatten.js +59 -0
  37. package/build/utilities/Flatten.js.map +1 -0
  38. package/build/utilities/Utilities.d.ts +7 -58
  39. package/build/utilities/Utilities.js +8 -33
  40. package/build/utilities/Utilities.js.map +1 -1
  41. package/build/utilities/schema/Introspection.d.ts +24 -0
  42. package/build/utilities/schema/Introspection.js +87 -0
  43. package/build/utilities/schema/Introspection.js.map +1 -0
  44. package/build/utilities/schema/JSONSchema.d.ts +68 -0
  45. package/build/utilities/schema/JSONSchema.js +13 -0
  46. package/build/utilities/schema/JSONSchema.js.map +1 -0
  47. package/build/utilities/schema/Schema.d.ts +253 -0
  48. package/build/utilities/schema/Schema.js +241 -0
  49. package/build/utilities/schema/Schema.js.map +1 -0
  50. package/build/utilities/schema/SchemaError.d.ts +10 -0
  51. package/build/utilities/schema/SchemaError.js +13 -0
  52. package/build/utilities/schema/SchemaError.js.map +1 -0
  53. package/build/utilities/schema/Validator.d.ts +94 -0
  54. package/build/utilities/schema/Validator.js +246 -0
  55. package/build/utilities/schema/Validator.js.map +1 -0
  56. package/package.json +1 -1
  57. package/tests/config/config.js +233 -0
  58. package/tests/router.js +596 -0
  59. package/tests/schema/schema.js +368 -0
  60. package/tests/test.env +0 -0
  61. package/tests/test.js +3 -3
  62. package/build/server/config/ConfigLoader.js.map +0 -1
  63. package/build/server/config/ConfigValidator.d.ts +0 -71
  64. package/build/server/config/ConfigValidator.js +0 -131
  65. package/build/server/config/ConfigValidator.js.map +0 -1
  66. package/examples/in-docs.js +0 -96
package/README.md CHANGED
@@ -1,52 +1,299 @@
1
- # 🚀 Vortez
1
+ # Vortez
2
2
 
3
- This project began as a **personal journey** to deeply understand how web servers work in **Node.js**.
4
- Throughout its development, I’ve gained **tons of new skills and insights** that I’m excited to share.
3
+ A lightweight, production-ready Node.js web framework for building APIs, websites, single-page applications (SPAs), and progressive web apps (PWAs). Built with TypeScript and designed for simplicity without sacrificing power.
5
4
 
6
- 🛠️ **Continuous Improvement:**
7
- I constantly refactor the code whenever I spot areas that can be polished or optimized.
5
+ The package exports a default `Vortez` server class, plus named exports for `Router`, `Config`, `Template`, `Utilities`, `ServerError`, `Logger`, and `Beta`.
8
6
 
9
- 🌟 **Real-World Usage:**
10
- I actively use this module in my own web projects, which means I’m always finding new ideas, enhancements, and opportunities to fix bugs based on feedback.
7
+ [![npm version](https://img.shields.io/npm/v/vortez?style=flat-square)](https://www.npmjs.com/package/vortez)
8
+ [![License](https://img.shields.io/npm/l/vortez?style=flat-square)](LICENSE)
9
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D%2016.0.0-brightgreen?style=flat-square)](https://nodejs.org/)
11
10
 
12
- 💡 **Vision:**
13
- My goal is to make **Vortez** a tool that helps developers build **APIs**, **PWAs**, **websites**, and—thanks to the [Vizui module](https://github.com/NetFeez/Vizui)—**SPAs** with ease and confidence.
11
+ ## Features
14
12
 
15
- Stay tuned for more updates and features! 🚀
16
- That’s all for now, [NetFeez](https://NetFeez.github.io) out.
13
+ - 🚀 **Fast and Lightweight** Minimal overhead, maximum performance
14
+ - 📦 **Built with TypeScript** — Full type safety with compiled JavaScript output
15
+ - 🔄 **Flexible Routing** — Pattern-based routing with wildcards and dynamic parameters
16
+ - 🔌 **WebSocket Support** — Real-time bidirectional communication out of the box
17
+ - 🛡️ **HTTPS/SSL Ready** — Secure connections with easy configuration
18
+ - 🎯 **Middleware Pipeline** — Composable, snapshot-based middleware system
19
+ - 📝 **Request/Response Helpers** — Simplified APIs for JSON, files, and text responses
20
+ - ⚙️ **Modular Architecture** — Use what you need, extend what you want
21
+ - 🔐 **Beta Features** — JWT authentication and email support (development version)
22
+
23
+ ## Table of Contents
24
+
25
+ - [Quick Start](#quick-start)
26
+ - [Installation](#installation)
27
+ - [Getting Started](#getting-started)
28
+ - [Configuration Files](#configuration-files)
29
+ - [Examples Repository](#examples-repository)
30
+ - [Core Concepts](#core-concepts)
31
+ - [Routing Rules](#routing-rules)
32
+ - [Server Configuration](#server-configuration)
33
+ - [Use Cases](#use-cases)
34
+ - [Development Features](#development-features)
17
35
 
18
36
  ---
19
37
 
20
- # Installation
38
+ ## Quick Start
21
39
 
22
- You can use **npm** to install Vortez:
40
+ ### Installation
23
41
 
24
- * **Stable version**
42
+ ```console
43
+ npm install vortez
44
+ ```
25
45
 
26
- ```console
27
- mpm install vortez
28
- ```
29
- * **Development version**
46
+ **Development version** (with beta features):
30
47
 
31
- ```console
32
- mpm install vortez@dev
33
- ```
48
+ ```console
49
+ npm install vortez@dev
50
+ ```
51
+
52
+ ### Requirements
53
+
54
+ - **Node.js** ≥ 16.0.0
55
+ - **ES Modules** — Set `"type": "module"` in your `package.json`
56
+
57
+ ```json
58
+ {
59
+ "name": "my-app",
60
+ "type": "module",
61
+ "main": "index.js"
62
+ }
63
+ ```
34
64
 
35
65
  > [!IMPORTANT]
36
- > You need to set `"type": "module"` in your `package.json` to use **Vortez**.
37
- > This requirement will be removed in a future version, but for now please configure it like this:
38
- >
39
- > ```json
40
- > {
41
- > "name": "my-project",
42
- > "main": "index.js",
43
- > "type": "module"
44
- > }
45
- > ```
66
+ > CommonJS support will be added in a future release. For now, ES modules are required.
67
+
68
+ ### Minimal Example
69
+
70
+ ```js
71
+ import Vortez from 'vortez';
72
+
73
+ const server = new Vortez({ port: 3000 });
74
+
75
+ // Define a route
76
+ server.router.addAction('GET', '/hello', (request, response) => {
77
+ response.sendJson({ message: 'Hello World' });
78
+ });
79
+
80
+ // Start the server
81
+ await server.start();
82
+ // Server running on http://localhost:3000
83
+ ```
46
84
 
47
85
  ---
48
86
 
49
- # Documentation
87
+ ## Installation
88
+
89
+ Vortez is available on npm:
90
+
91
+ ```console
92
+ npm install vortez
93
+ ```
94
+
95
+ For the latest development version with experimental features:
96
+
97
+ ```console
98
+ npm install vortez@dev
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Getting Started
104
+
105
+ ### Creating a Server
106
+
107
+ ```js
108
+ import Vortez from 'vortez';
109
+
110
+ const server = new Vortez();
111
+
112
+ // Configure (optional)
113
+ server.config.set('port', 3000);
114
+ server.config.set('host', 'localhost');
115
+
116
+ // Add routes
117
+ server.router.addAction('GET', '/', (request, response) => {
118
+ response.sendJson({ status: 'ok' });
119
+ });
120
+ server.router.addFile('/favicon.ico', '/assets/icon.ico');
121
+
122
+ // Start listening
123
+ await server.start();
124
+ ```
125
+
126
+ ### Constructor Options
127
+
128
+ ```js
129
+ const server = new Vortez({
130
+ host: 'localhost', // Default: 'localhost'
131
+ port: 80, // Default: 80
132
+ ssl: null // Optional HTTPS configuration
133
+ });
134
+ ```
135
+
136
+ ### Configuration Files
137
+
138
+ You can also build a `Config` directly or load one from disk.
139
+
140
+ ```js
141
+ import Vortez, { Config } from 'vortez';
142
+
143
+ const config = new Config({
144
+ host: 'localhost',
145
+ port: 3000,
146
+ routing: {
147
+ algorithm: 'FIFO'
148
+ }
149
+ });
150
+
151
+ console.log(config.toJson());
152
+ ```
153
+
154
+ ```js
155
+ import Vortez, { Config } from 'vortez';
156
+
157
+ const config = await Config.Loader.load('config.json');
158
+ // You can directly use of Vortez import
159
+ const config = await Vortez.Config.Loader.load('config.json');
160
+
161
+ const server = new Vortez(config);
162
+ await server.start();
163
+ ```
164
+
165
+ This workflow is also covered in the dedicated examples repository.
166
+
167
+ ### Examples Repository
168
+
169
+ The runnable examples are being moved to a separate repository to keep this package focused.
170
+
171
+ Until that repository is published, the snippets in this README remain the canonical reference.
172
+
173
+ ---
174
+
175
+ ## Core Concepts
176
+
177
+ ### URL Rules
178
+
179
+ URL rules define how requests are matched to routes. They use patterns with special markers:
180
+
181
+ | Pattern | Behavior | Example |
182
+ |---------|----------|---------|
183
+ | `/` | Root path | `/` |
184
+ | `/path` | Literal segment | `/api/users` |
185
+ | `/$param` | Dynamic parameter | `/$id` captures `123` as `id` |
186
+ | `/*` | Wildcard (matches all sub-routes) | `/files/*` matches `/files/a/b/c` |
187
+ | `/path/$id/sub` | Mixed patterns | `/user/$id/posts` |
188
+
189
+ **Examples:**
190
+
191
+ ```js
192
+ // Static routes
193
+ server.router.addAction('GET', '/', homeHandler);
194
+ server.router.addAction('GET', '/api/status', statusHandler);
195
+
196
+ // Dynamic routes with parameters
197
+ server.router.addAction('GET', '/api/users/$id', (request, response) => {
198
+ const userId = request.ruleParams.id;
199
+ // Handle request...
200
+ });
201
+
202
+ // Wildcard routes
203
+ server.router.addAction('GET', '/api/*', catchAllHandler);
204
+ server.router.addFile('/docs/*', 'public/docs/index.html');
205
+ ```
206
+
207
+ ### Request Object
208
+
209
+ The `request` object contains information about the incoming HTTP request:
210
+
211
+ ```js
212
+ server.router.addAction('GET', '/api/$action/$id', (request, response) => {
213
+ console.log(request.method); // 'GET', 'POST', etc.
214
+ console.log(request.url); // Full URL
215
+ console.log(request.ruleParams); // { action: '...', id: '...' }
216
+ console.log(request.searchParams); // Query parameters object (Record<string, string | undefined>)
217
+ console.log(request.headers); // HTTP headers
218
+ // For body, use: const body = await request.post;
219
+ });
220
+ ```
221
+
222
+ ### Response Object
223
+
224
+ The `response` object provides methods to send data back to the client:
225
+
226
+ ```js
227
+ // Send JSON
228
+ response.sendJson({ key: 'value' });
229
+
230
+ // Send HTML/text
231
+ response.send('Hello World');
232
+
233
+ // Send file
234
+ response.sendFile('path/to/file.html');
235
+
236
+ // Send with custom status and headers
237
+ response.sendJson({ id: 123 }, {
238
+ status: 201,
239
+ headers: {
240
+ 'X-Test': 'TestHeader'
241
+ }
242
+ });
243
+
244
+ // The same options shape is supported by send, sendJson, sendFile and sendTemplate
245
+ ```
246
+
247
+ ### Middleware Execution Model
248
+
249
+ Vortez uses a **snapshot middleware composition model**:
250
+
251
+ - Global middleware registered via `router.httpMiddleware.use()` / `router.httpMiddleware.useError()`
252
+ and `router.wsMiddleware.use()` / `router.wsMiddleware.useError()` is captured at rule registration time
253
+ - Middleware is not updated retroactively for existing rules
254
+ - When mounting sub-routers, child middleware is appended after parent middleware
255
+
256
+ **Key principle:** Register all global middleware first, then add routes.
257
+
258
+ The `router.use()` helper merges middleware instances; individual middleware functions are added on `router.httpMiddleware` or `router.wsMiddleware`.
259
+
260
+ ```js
261
+ const router = server.router;
262
+
263
+ // Register middleware first
264
+ router.httpMiddleware.use(authMiddleware);
265
+ router.httpMiddleware.use(loggingMiddleware);
266
+
267
+ // Add routes (they capture current middleware)
268
+ router.addAction('GET', '/old', oldHandler);
269
+ // old → [authMiddleware, loggingMiddleware]
270
+
271
+ // Add more middleware
272
+ router.httpMiddleware.use(newMiddleware);
273
+
274
+ // New routes get updated middleware
275
+ router.addAction('GET', '/new', newHandler)
276
+ // Add a middleware only to a single rule
277
+ .httpMiddleware.use(otherMiddleware);
278
+ // new → [authMiddleware, loggingMiddleware, newMiddleware]
279
+
280
+ // Old routes still use original middleware!
281
+ // old → [authMiddleware, loggingMiddleware]
282
+ ```
283
+
284
+ **Practical example with sub-routers:**
285
+
286
+ ```js
287
+ const apiRouter = new Vortez.Router();
288
+ apiRouter.httpMiddleware.use(apiAuthMiddleware);
289
+ apiRouter.addAction('GET', '/users', getUsersHandler);
290
+
291
+ server.router.httpMiddleware.use(globalMiddleware);
292
+ server.router.mount(apiRouter, '/api');
293
+ // Final middleware chain: [globalMiddleware, apiAuthMiddleware]
294
+ ```
295
+
296
+ ---
50
297
 
51
298
  ## Static Pages
52
299
 
@@ -63,256 +310,514 @@ const server = new Vortez();
63
310
  server.router.addFolder('/source', 'source');
64
311
  server.router.addFile('/', 'source/index.html');
65
312
 
313
+ // Set all other routes or an specific rule for the static page
314
+ server.router.addFile('/*', 'source/index.html');
315
+ server.router.addFile('/app/*', 'source/index.html');
316
+
66
317
  // Starting the server
67
- server.start();
318
+ await server.start();
68
319
  ```
69
320
 
70
321
  ---
71
322
 
72
- ## APIs and Websites
323
+ ## REST APIs
73
324
 
74
- You can use **actions** to execute code and send responses to the client:
325
+ Build a complete REST API:
75
326
 
76
327
  ```js
77
328
  import Vortez from 'vortez';
78
329
 
79
- const server = new Vortez();
330
+ const server = new Vortez({ port: 3000 });
80
331
 
81
- // Action with a static response
82
- server.router.addAction('GET', '/api/test', (request, response) => {
83
- response.sendJson({
84
- message: 'Hello World',
85
- route: `[${request.method}] -> ${request.url}`
86
- });
332
+ // Users resource
333
+ server.router.addAction('GET', '/api/users', (req, res) => {
334
+ res.sendJson([
335
+ { id: 1, name: 'Alice' },
336
+ { id: 2, name: 'Bob' }
337
+ ]);
87
338
  });
88
339
 
89
- // Route params and query params
90
- // Example route param: `/api/params/$id`
91
- // Example request: `http://localhost/api/params/123`
92
- server.router.addAction('GET', '/api/params/$id', (request, response) => {
93
- response.sendJson({
94
- message: 'Hello World',
95
- route: `[${request.method}] -> ${request.url}`,
96
- params: request.ruleParams,
97
- query: request.searchParams
98
- });
340
+ server.router.addAction('GET', '/api/users/$id', (req, res) => {
341
+ const id = req.ruleParams.id;
342
+ res.sendJson({ id, name: `User ${id}` });
99
343
  });
100
344
 
101
- // Serving files
102
- server.router.addAction('GET', '/api/file', (request, response) => {
103
- response.sendFile('source/index.html');
345
+ server.router.addAction('POST', '/api/users', async (req, res) => {
346
+ const user = await req.post;
347
+ res.sendJson({ id: 123, ...user }, { status: 201 });
104
348
  });
105
349
 
106
- // Sending simple text
107
- server.router.addAction('GET', '/api/string', (request, response) => {
108
- response.send('Hello World');
350
+ server.router.addAction('PUT', '/api/users/$id', async (req, res) => {
351
+ const id = req.ruleParams.id;
352
+ const body = await req.post;
353
+ res.sendJson({ id, ...body });
109
354
  });
110
355
 
111
- // Starting the server
112
- server.start();
356
+ server.router.addAction('DELETE', '/api/users/$id', (req, res) => {
357
+ res.send('', { status: 204 });
358
+ });
359
+
360
+ await server.start();
113
361
  ```
114
362
 
115
363
  ---
116
364
 
117
- ## SPAs
365
+ ## Single Page Applications
118
366
 
119
- You can serve a single file for multiple URLs, including recursive routes:
367
+ Serve an SPA with client-side routing:
120
368
 
121
369
  ```js
122
370
  import Vortez from 'vortez';
123
371
 
124
372
  const server = new Vortez();
125
373
 
126
- server.router.addFile('/', 'main.html');
127
- server.router.addFile('/app/*', 'main.html');
374
+ // Serve the main app shell for all app routes
375
+ server.router.addFile('/app', 'public/app.html');
376
+ server.router.addFile('/app/*', 'public/app.html');
377
+
378
+ // Serve static assets
128
379
  server.router.addFolder('/public', 'public');
129
380
 
130
- /*
131
- You can also add other features like actions, files, folders, etc.
132
- Use addWebSocket for real-time connections.
133
- */
381
+ // Optional: API routes
382
+ server.router.addAction('GET', '/api/data', (req, res) => {
383
+ res.sendJson({ /* ... */ });
384
+ });
134
385
 
135
- server.start();
386
+ await server.start();
136
387
  ```
137
388
 
138
389
  ---
139
390
 
391
+ ## Routing Rules
392
+
393
+ ### Serving Folders
394
+
395
+ Serve an entire directory and its subdirectories:
396
+
397
+ ```js
398
+ server.router.addFolder('/public', 'public');
399
+ server.router.addFolder('/docs', 'documentation');
400
+
401
+ // Absolute paths are supported
402
+ server.router.addFolder('/cdn', '/var/www/cdn');
403
+ ```
404
+
405
+ > [!WARNING]
406
+ > Never expose sensitive directories:
407
+ > - ❌ `server.router.addFolder('/', '.')` — Exposes the entire project
408
+ > - ❌ `server.router.addFolder('/', 'src')` — Exposes source code
409
+ >
410
+ > Exposed contents include:
411
+ > - Private certificate keys
412
+ > - Database credentials in configuration files
413
+ > - API tokens and secrets
414
+ > - Source code and internal structure
415
+
416
+ ### Serving Files
417
+
418
+ Serve a single file at a specific route:
419
+
420
+ ```js
421
+ server.router.addFile('/', 'public/index.html');
422
+ server.router.addFile('/sitemap.xml', 'public/sitemap.xml');
423
+
424
+ // Useful for SPAs - serve the same file for multiple routes
425
+ server.router.addFile('/app/*', 'public/app.html');
426
+ ```
427
+
428
+ ### Action Routes
429
+
430
+ Execute code to handle requests dynamically:
431
+
432
+ ```js
433
+ // Simple JSON API
434
+ server.router.addAction('GET', '/api/status', (request, response) => {
435
+ response.sendJson({
436
+ status: 'online',
437
+ timestamp: new Date().toISOString()
438
+ });
439
+ });
440
+
441
+ // With route parameters
442
+ server.router.addAction('GET', '/api/users/$id', (request, response) => {
443
+ const userId = request.ruleParams.id;
444
+ response.sendJson({ id: userId, name: 'User ' + userId });
445
+ });
446
+
447
+ // With query parameters
448
+ server.router.addAction('GET', '/api/search', (request, response) => {
449
+ const query = request.searchParams.q;
450
+ response.sendJson({ results: [], query });
451
+ });
452
+
453
+ // POST with body
454
+ server.router.addAction('POST', '/api/data', async (request, response) => {
455
+ const body = await request.post;
456
+ response.sendJson({ received: body }, { status: 201 });
457
+ });
458
+
459
+ // Multiple methods
460
+ server.router.addAction('GET', '/resource/$id', getResourceHandler);
461
+ server.router.addAction('PUT', '/resource/$id', updateResourceHandler);
462
+ server.router.addAction('DELETE', '/resource/$id', deleteResourceHandler);
463
+ ```
464
+
465
+ ### WebSocket Routes
466
+
467
+ Handle real-time WebSocket connections:
468
+
469
+ ```js
470
+ const connections = new Set();
471
+
472
+ server.router.addWebsocket('/chat', (request, socket) => {
473
+ console.log('[WS] New connection from:', request.url);
474
+
475
+ // Notify others
476
+ connections.forEach(conn => {
477
+ conn.send('A user connected');
478
+ });
479
+ connections.add(socket);
480
+
481
+ // Handle incoming messages
482
+ socket.on('message', (data, info) => {
483
+ console.log('Message:', data.toString());
484
+
485
+ // Broadcast to all connections
486
+ connections.forEach(conn => {
487
+ if (conn !== socket) {
488
+ conn.send(data);
489
+ }
490
+ });
491
+ });
492
+
493
+ // Handle disconnection
494
+ socket.on('finish', () => {
495
+ connections.delete(socket);
496
+ connections.forEach(conn => {
497
+ conn.send('A user disconnected');
498
+ });
499
+ });
500
+
501
+ // Handle errors
502
+ socket.on('error', (error) => {
503
+ console.error('[WS-Error]:', error);
504
+ });
505
+ });
506
+ ```
507
+
508
+ > [!NOTE]
509
+ > WebSocket routes use a separate namespace from HTTP routes, so you can have `/api` as both an HTTP action and a WebSocket route without conflicts.
510
+
511
+ ---
512
+
140
513
  ## Server Configuration
141
514
 
142
- You can configure the server using the `server.config` object:
515
+ ### Configuration Methods
516
+
517
+ Configure the server using `server.config`:
143
518
 
144
519
  ```js
145
520
  import Vortez from 'vortez';
146
521
  const server = new Vortez();
147
522
 
148
- server.config.port = 3000;
149
- server.config.host = 'localhost';
150
- server.config.https = {
151
- key: 'path/to/key.pem',
152
- cert: 'path/to/cert.pem'
153
- };
154
- server.config.templates.error = 'error.html';
155
- server.config.templates.folder = 'folder.html';
523
+ // Set individual values
524
+ server.config.set('port', 3000);
525
+ server.config.set('host', '0.0.0.0');
526
+
527
+ // Set nested values
528
+ server.config.set('ssl.cert', 'path/to/cert.pem');
529
+ server.config.set('ssl.key', 'path/to/key.pem');
530
+
531
+ // Get values
532
+ const port = server.config.get('port');
533
+ const algorithm = server.config.get('routing.algorithm');
156
534
  ```
157
535
 
158
- You can also create the server with a configuration object passed to the constructor:
536
+ ### Constructor Configuration
159
537
 
160
- * **Using options:**
538
+ Pass configuration to the constructor:
161
539
 
162
540
  ```js
163
541
  const server = new Vortez({
164
- port: 3000,
165
- host: 'localhost',
166
- ssl: null
542
+ host: '0.0.0.0',
543
+ port: 8080,
544
+ ssl: {
545
+ cert: 'path/to/cert.pem',
546
+ key: 'path/to/key.pem',
547
+ port: 443
548
+ }
167
549
  });
168
550
  ```
169
551
 
170
- * **Using the Config instance:**
552
+ ### HTTPS/SSL Configuration
553
+
554
+ Enable HTTPS with SSL certificates:
171
555
 
172
556
  ```js
173
- const config = new Vortez.Config();
174
- config.port = 3000;
175
- config.host = 'localhost';
176
- config.ssl = null;
557
+ server.config.set('ssl', {
558
+ cert: 'path/to/certificate.pem',
559
+ key: 'path/to/private-key.pem',
560
+ port: 443 // Optional: HTTPS port (default: 443)
561
+ });
562
+ ```
177
563
 
178
- const server = new Vortez(config);
564
+ The framework will automatically create an HTTPS server alongside the HTTP server.
565
+
566
+ ### Available Configuration Keys
567
+
568
+ ```js
569
+ server.config.set('host', 'localhost'); // Server hostname
570
+ server.config.set('port', 80); // HTTP port
571
+ server.config.set('ssl.cert', 'cert.pem'); // SSL certificate path
572
+ server.config.set('ssl.key', 'key.pem'); // SSL private key path
573
+ server.config.set('ssl.port', 443); // HTTPS port
574
+ server.config.set('templates.error', 'error.html'); // Error page template
575
+ server.config.set('templates.folder', 'folder.html'); // Folder listing template
576
+ server.config.set('routing.algorithm', 'FIFO'); // Routing algorithm ('FIFO' or 'Tree')
179
577
  ```
180
578
 
181
579
  ---
182
580
 
183
- ## URL Rules
581
+ ## Real-World Example (ArtFolder Style)
582
+
583
+ This is a complete composition pattern based on the real-world project structure used in ArtFolder,
584
+ with separated routers for UI pages, API endpoints, and WebSocket endpoints.
585
+
586
+ ```js
587
+ import Vortez, { ServerError, Template } from 'vortez';
588
+
589
+ const config = await Vortez.Config.Loader.load('.config.json');
590
+ const server = new Vortez(config);
184
591
 
185
- URL rules are strings used to define routes handled by the router.
592
+ const clientRouter = new Vortez.Router();
593
+ const apiRouter = new Vortez.Router();
594
+ const socketRouter = new Vortez.Router();
595
+
596
+ // -------------------------
597
+ // Shared API middleware
598
+ // -------------------------
599
+ apiRouter.httpMiddleware.useError((error, request, response, next, state) => {
600
+ if (error instanceof ServerError) {
601
+ return response.sendJson({ error: error.message }, { status: error.status });
602
+ }
603
+ const message = error instanceof Error ? error.message : 'Unknown error';
604
+ return response.sendJson({ error: message }, { status: 500 });
605
+ });
186
606
 
187
- The main separator is `/` which indicates a new sub-route:
607
+ apiRouter.httpMiddleware.use(async (request, response, next, state) => {
608
+ const page = Number(request.searchParams.page ?? '1');
609
+ const limit = Number(request.searchParams.limit ?? '20');
610
+ if (!Number.isFinite(page) || !Number.isFinite(limit)) {
611
+ throw new ServerError('Invalid pagination params', 400);
612
+ }
613
+ state.page = page;
614
+ state.limit = limit;
615
+ return next();
616
+ });
188
617
 
189
- * **`*`**: A wildcard that captures all sub-routes.
190
- * **`$<name>`**: A dynamic parameter you can access via `request.ruleParams`.
191
- * **`<string>`**: A literal segment that must match exactly.
618
+ // -------------------------
619
+ // Client router (SSR + static)
620
+ // -------------------------
621
+ clientRouter.addAction('GET', '/', async (request, response) => {
622
+ const importMap = await Template.load('assets/importmap.json', {});
623
+ await response.sendTemplate('assets/app.html', {
624
+ title: 'Art Folder',
625
+ style: '/client/styles/styles.css',
626
+ logic: '/client/logic/build/logic.js',
627
+ importMap
628
+ });
629
+ });
192
630
 
193
- **Examples**:
631
+ clientRouter.addAction('GET', '/app/*', async (request, response) => {
632
+ const importMap = await Template.load('assets/importmap.json', {});
633
+ await response.sendTemplate('assets/app.html', {
634
+ title: 'Art Folder',
635
+ style: '/client/styles/styles.css',
636
+ logic: '/client/logic/build/logic.js',
637
+ importMap
638
+ });
639
+ });
194
640
 
195
- * `/api/*` — Matches `/api/users`, `/api/posts/comments`, etc.
196
- * `/user/$id` — Matches `/user/123`, capturing `123` as `id`.
197
- * `/blog/$category/$postId` — Matches `/blog/tech/42`, capturing `tech` as `category` and `42` as `postId`.
641
+ clientRouter.addFolder('/client', 'client');
642
+ clientRouter.addFile('/favicon.ico', 'client/source/images/Mochis.gif');
198
643
 
199
- ---
644
+ // -------------------------
645
+ // API router
646
+ // -------------------------
647
+ apiRouter.addAction('GET', '/health', (request, response, state) => {
648
+ response.sendJson({ ok: true, page: state.page, limit: state.limit });
649
+ });
200
650
 
201
- ## Rules
651
+ apiRouter.addAction('POST', '/echo', async (request, response) => {
652
+ const body = await request.post;
653
+ response.sendJson({ received: body }, { status: 201 });
654
+ });
202
655
 
203
- In **Vortez**, there are four types of routers:
656
+ apiRouter.addAction('GET', '/user/$uuid', (request, response) => {
657
+ response.sendJson({ userId: request.ruleParams.uuid });
658
+ });
204
659
 
205
- | Type | Description |
206
- | ----------------------- | ------------------------------------------------------ |
207
- | [Folder](#folder) | Serves a folder and its sub-folders |
208
- | [File](#file) | Serves a single file |
209
- | [Action](#action) | Lets you handle requests programmatically |
210
- | [WebSocket](#websocket) | Allows managing WebSocket connections on a given route |
660
+ apiRouter.addAction('GET', '*', (request) => {
661
+ throw new ServerError(`No route found for ${request.method} -> ${request.url}`, 404);
662
+ });
211
663
 
212
- ### Folder
664
+ // -------------------------
665
+ // WebSocket router
666
+ // -------------------------
667
+ socketRouter.addWebsocket('/user/$uuid', (request, socket) => {
668
+ socket.sendJson({
669
+ type: 'welcome',
670
+ uuid: request.ruleParams.uuid,
671
+ queryUuid: request.searchParams.uuid
672
+ });
213
673
 
214
- Serves a folder and its sub-folders:
674
+ socket.on('message', (data) => {
675
+ socket.sendJson({ type: 'echo', data: data.toString('utf8') });
676
+ });
677
+ });
215
678
 
216
- > [!WARNING]
217
- > Do not share the root of your project, as this would expose **ALL** its contents:
218
- >
219
- > * Private certificate keys
220
- > * Database passwords in server-side `.js` files
221
- > * Security tokens
222
- > * And any other sensitive data
223
- >
224
- > Also:
225
- >
226
- > * The entire assigned path will be exposed.
227
- >
228
- > * **Example:** assigning `/src` would include all sub-routes like `/src/styles`.
679
+ // Mount everything
680
+ server.router.mount(clientRouter);
681
+ server.router.mount(apiRouter, '/api');
682
+ server.router.mount(socketRouter, '/rtc');
683
+
684
+ await server.start();
685
+ ```
686
+
687
+ This pattern keeps each concern isolated and mirrors how larger applications organize routes in production.
688
+
689
+ ---
690
+
691
+ ## Use Cases
692
+
693
+ ### Static Website
694
+
695
+ Simple static site with assets:
229
696
 
230
697
  ```js
231
- server.router.addFolder('/my-folder', 'path/to/folder');
232
- server.router.addFolder('/my-folder-2', '/path/to/folder/absolute');
698
+ import Vortez from 'vortez';
699
+
700
+ const server = new Vortez();
701
+
702
+ server.router.addFile('/', 'public/index.html');
703
+ server.router.addFolder('/assets', 'public');
704
+
705
+ await server.start();
233
706
  ```
234
707
 
235
- ### File
708
+ ### Full-Stack Application
236
709
 
237
- Serves a single file:
710
+ Combine API endpoints with static frontend:
238
711
 
239
712
  ```js
240
- server.router.addFile('/my-file', 'path/to/file');
241
- server.router.addFile('/my-file-2', '/path/to/file/absolute');
713
+ import Vortez from 'vortez';
714
+
715
+ const server = new Vortez();
716
+
717
+ // API
718
+ server.router.addAction('GET', '/api/posts', getPosts);
719
+ server.router.addAction('POST', '/api/posts', createPost);
720
+
721
+ // Frontend
722
+ server.router.addFile('/', 'public/index.html');
723
+ server.router.addFolder('/assets', 'public');
724
+
725
+ await server.start();
242
726
  ```
243
727
 
244
- ### Action
728
+ ### Single Page Application
245
729
 
246
- Lets you handle requests programmatically:
730
+ Serve a client-side routed application shell:
247
731
 
248
732
  ```js
249
- server.router.addAction('GET', '/my-action', (request, response) => {
250
- response.sendJson({
251
- message: 'Hello World',
252
- route: `[${request.method}] -> ${request.url}`
253
- });
733
+ import Vortez from 'vortez';
734
+
735
+ const server = new Vortez();
736
+
737
+ server.router.addFile('/', 'public/app.html');
738
+ server.router.addFile('/app/*', 'public/app.html');
739
+ server.router.addFolder('/assets', 'public');
740
+
741
+ server.router.addAction('GET', '/api/status', (request, response) => {
742
+ response.sendJson({ status: 'ok' });
254
743
  });
744
+
745
+ await server.start();
255
746
  ```
256
747
 
257
- ### WebSocket
748
+ ### Real-Time Chat Application
258
749
 
259
- Allows managing WebSocket connections on a given route:
260
-
261
- > [!NOTE]
262
- > WebSocket URLs use a separate namespace from Files, Folders, and Actions,
263
- > so they won’t conflict even if they share the same route patterns.
750
+ WebSocket-powered chat:
264
751
 
265
752
  ```js
266
- const connections = new Set();
267
-
268
- server.addWebSocket('/Test/WS-Chat', (request, socket) => {
269
- console.log('[WS] New connection');
270
- connections.forEach(user => user.Send('A user has connected.'));
271
- connections.add(socket);
753
+ import Vortez from 'vortez';
272
754
 
273
- socket.on('finish', () => connections.delete(socket));
274
- socket.on('error', error => console.log('[WS-Error]:', error));
755
+ const server = new Vortez();
756
+ const clients = new Set();
275
757
 
276
- socket.on('message', (data, info) => {
277
- if (info.opCode === 1) {
278
- console.log('[WS] Message:', data.toString());
279
- connections.forEach(user => {
280
- if (user !== socket) user.Send(data.toString());
281
- });
282
- } else if (info.opCode === 8) {
283
- connections.forEach(user => user.Send('A user has disconnected.'));
284
- }
758
+ server.router.addWebsocket('/chat', (request, socket) => {
759
+ clients.add(socket);
760
+
761
+ socket.on('message', (data) => {
762
+ clients.forEach(client => client.send(data));
285
763
  });
764
+
765
+ socket.on('finish', () => clients.delete(socket));
286
766
  });
767
+
768
+ server.router.addFile('/', 'public/index.html');
769
+ await server.start();
287
770
  ```
288
771
 
289
772
  ---
290
773
 
291
- # Development Version
774
+ # Development Features
292
775
 
293
- ## Currently in Development
776
+ ## Beta Features
294
777
 
295
- The following features are under active development:
778
+ The development version includes experimental functionality:
296
779
 
297
- * **\[JsonWT]**: JSON Web Token (JWT) support.
298
- * **\[Mail]**: Email sending functionality.
299
- * **\[Server]**: Dynamic authentication system for routing.
780
+ ```console
781
+ npm install vortez@dev
782
+ ```
300
783
 
301
- ## Installation
784
+ Access beta features:
302
785
 
303
- To install the development version:
786
+ ```js
787
+ import { Beta } from 'vortez';
788
+ const { Mail, JwtManager } = Beta;
304
789
 
305
- ```console
306
- mpm install vortez@dev
790
+ // JWT token management
791
+ const jwt = new JwtManager({ alg: 'HS256', key: 'secret' });
792
+ const token = jwt.sign({ userId: 1 });
793
+
794
+ // Email functionality
795
+ const mailer = new Mail({
796
+ host: 'smtp.example.com',
797
+ port: 587,
798
+ username: 'user',
799
+ password: 'secret',
800
+ email: 'user@example.com',
801
+ useStartTLS: true
802
+ });
307
803
  ```
308
804
 
309
805
  > [!WARNING]
310
- > This version may contain bugs.
311
- > It includes the latest features that may not yet be fully tested.
806
+ > Beta features are experimental and may change significantly. Use with caution in production.
312
807
 
313
- To access development features not yet listed in `changes.md`:
808
+ ---
809
+
810
+ ## Contributing
811
+
812
+ Contributions are welcome! Feel free to submit issues and pull requests on the repository.
813
+
814
+ ## License
815
+
816
+ Licensed under the terms specified in the [LICENSE](LICENSE) file.
817
+
818
+ ---
819
+
820
+ ## Support
821
+
822
+ For questions, issues, or feature requests, please open an issue on the GitHub repository.
314
823
 
315
- ```js
316
- import { Beta } from 'vortez';
317
- const { Mail, JwtManager } = Beta;
318
- ```