vortez 5.0.0-dev.18 → 5.0.0

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