fluxion-ts 0.10.1 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +259 -350
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +132 -6
- package/dist/index.d.mts +132 -6
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Fluxion
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.
|
|
4
|
-
[](https://www.npmjs.
|
|
3
|
+
[](https://www.npmjs.org/package/fluxion)
|
|
4
|
+
[](https://www.npmjs.org/package/fluxion)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
Fluxion is a filesystem-routing dynamic server for Node.js.
|
|
14
14
|
|
|
15
15
|
- Route files from a dynamic directory by chokidar or native `fs.watch`
|
|
16
|
-
- Load API handlers by extension
|
|
16
|
+
- Load API handlers by extension patterns (default: `*.ts`)
|
|
17
17
|
- Serve other files as static resources
|
|
18
18
|
- Run the business server in worker processes
|
|
19
19
|
- Expose runtime status from the primary process through meta APIs
|
|
20
20
|
- Automatically serialize handler return values as JSON
|
|
21
|
+
- Built-in middleware system and HTTP exception handling
|
|
21
22
|
|
|
22
23
|
## Install
|
|
23
24
|
|
|
@@ -42,16 +43,14 @@ fluxion({
|
|
|
42
43
|
Create `dynamicDirectory/hello.ts`:
|
|
43
44
|
|
|
44
45
|
```ts
|
|
45
|
-
import {
|
|
46
|
+
import { defineFluxionModule } from 'fluxion';
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
// You can export the handler function directly without it.
|
|
49
|
-
export default defineFluxionHandler(async function handler(req) {
|
|
48
|
+
export default defineFluxionModule(async (req, cx) => {
|
|
50
49
|
return {
|
|
51
50
|
message: 'hello fluxion',
|
|
52
51
|
path: req.url.pathname,
|
|
53
52
|
};
|
|
54
|
-
})
|
|
53
|
+
});
|
|
55
54
|
```
|
|
56
55
|
|
|
57
56
|
Run:
|
|
@@ -79,104 +78,206 @@ In this repository, `pnpm dev` runs `src/index.ts` directly and starts Fluxion u
|
|
|
79
78
|
Default development options:
|
|
80
79
|
|
|
81
80
|
```ts
|
|
82
|
-
// if process.env.FLUXION_COLORS === '0', colors will be disabled in logs
|
|
83
|
-
|
|
84
81
|
fluxion({
|
|
85
82
|
dir: process.env.DYNAMIC_DIRECTORY ?? 'dynamicDirectory',
|
|
86
83
|
host: process.env.HOST ?? 'localhost',
|
|
87
|
-
port:
|
|
88
|
-
|
|
84
|
+
port: Number.parseInt(process.env.PORT ?? '9000', 10),
|
|
85
|
+
metaPort: Number.parseInt(process.env.META_PORT ?? '9001', 10),
|
|
89
86
|
workerOptions: {
|
|
90
|
-
maxWorkerCount:
|
|
87
|
+
maxWorkerCount: 4,
|
|
91
88
|
},
|
|
92
89
|
});
|
|
93
90
|
```
|
|
94
91
|
|
|
95
|
-
Example:
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
pnpm dev
|
|
99
|
-
curl http://localhost:3000/test.ts
|
|
100
|
-
```
|
|
101
|
-
|
|
102
92
|
## Routing
|
|
103
93
|
|
|
104
|
-
Fluxion registers
|
|
105
|
-
|
|
106
|
-
With default options:
|
|
94
|
+
Fluxion registers files under `dir` based on glob patterns:
|
|
107
95
|
|
|
108
|
-
- Files
|
|
109
|
-
-
|
|
96
|
+
- Files matching `apiInclude` (default: `*.ts`) are API handlers.
|
|
97
|
+
- Other files are static resources.
|
|
110
98
|
- Request paths match file paths relative to `dir`.
|
|
111
99
|
- File extensions are part of the route path.
|
|
112
100
|
|
|
113
101
|
Examples:
|
|
114
102
|
|
|
115
|
-
| File
|
|
116
|
-
|
|
|
117
|
-
| `dynamicDirectory/test.ts`
|
|
118
|
-
| `dynamicDirectory/user/profile.ts`
|
|
119
|
-
| `dynamicDirectory/index.html`
|
|
120
|
-
| `dynamicDirectory/assets/app.js`
|
|
121
|
-
|
|
122
|
-
A request to `/hello` does not match `hello.ts`; request `/hello.ts` or change `apiExts`/routing behavior in code.
|
|
103
|
+
| File | Route | Type |
|
|
104
|
+
| --------------------------------------- | ------------------ | ----------- |
|
|
105
|
+
| `dynamicDirectory/test.ts` | `/test.ts` | API handler |
|
|
106
|
+
| `dynamicDirectory/user/profile.ts` | `/user/profile.ts` | API handler |
|
|
107
|
+
| `dynamicDirectory/index.html` | `/index.html` | Static file |
|
|
108
|
+
| `dynamicDirectory/assets/app.js` | `/assets/app.js` | Static file |
|
|
123
109
|
|
|
124
110
|
## API Handlers
|
|
125
111
|
|
|
126
|
-
An API
|
|
112
|
+
An API handler **MUST** use `defineFluxionModule()` to define the module. This provides type safety and ensures proper module structure.
|
|
113
|
+
|
|
114
|
+
### Basic Handler
|
|
127
115
|
|
|
128
116
|
```ts
|
|
129
|
-
|
|
117
|
+
import { defineFluxionModule } from 'fluxion';
|
|
118
|
+
|
|
119
|
+
export default defineFluxionModule(async (req, cx) => {
|
|
130
120
|
return { ok: true };
|
|
131
|
-
}
|
|
121
|
+
});
|
|
132
122
|
```
|
|
133
123
|
|
|
124
|
+
### Handler Arguments
|
|
125
|
+
|
|
126
|
+
Handlers receive 4 parameters:
|
|
127
|
+
|
|
134
128
|
```ts
|
|
135
|
-
|
|
136
|
-
return { ok: true };
|
|
137
|
-
}
|
|
129
|
+
handler(req, cx, rawReq, rawRes)
|
|
138
130
|
```
|
|
139
131
|
|
|
140
|
-
|
|
132
|
+
- **`req`**: Normalized request object
|
|
133
|
+
```ts
|
|
134
|
+
{
|
|
135
|
+
method: string; // HTTP method
|
|
136
|
+
ip: string; // Client IP
|
|
137
|
+
url: URL; // Parsed URL
|
|
138
|
+
query: Record<string, string | string[]>; // Query params
|
|
139
|
+
body: Record<string, any>; // Parsed body
|
|
140
|
+
headers: IncomingHttpHeaders;
|
|
141
|
+
cookie: Record<string, string>;
|
|
142
|
+
meta: Record<any, any>; // Custom metadata
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- **`cx`**: Module context
|
|
147
|
+
```ts
|
|
148
|
+
{
|
|
149
|
+
logger: FluxionLogger; // Logger instance
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- **`rawReq`**: Node.js `http.IncomingMessage`
|
|
154
|
+
|
|
155
|
+
- **`rawRes`**: Node.js `http.ServerResponse`
|
|
156
|
+
|
|
157
|
+
### Advanced Module Configuration
|
|
141
158
|
|
|
142
159
|
```ts
|
|
143
|
-
import {
|
|
160
|
+
import { defineFluxionModule, defineFluxionMiddleware } from 'fluxion';
|
|
161
|
+
|
|
162
|
+
const logMiddleware = defineFluxionMiddleware(async (req, cx) => {
|
|
163
|
+
cx.logger.info('Request received', { path: req.url.pathname });
|
|
164
|
+
});
|
|
144
165
|
|
|
145
|
-
export default
|
|
146
|
-
|
|
166
|
+
export default defineFluxionModule({
|
|
167
|
+
handler: async (req, cx) => {
|
|
168
|
+
return { message: 'hello' };
|
|
169
|
+
},
|
|
170
|
+
middlewares: [logMiddleware],
|
|
171
|
+
methods: ['GET', 'POST'],
|
|
172
|
+
handlerTimeoutMs: 10000,
|
|
147
173
|
});
|
|
148
174
|
```
|
|
149
175
|
|
|
150
|
-
###
|
|
176
|
+
### Module Options
|
|
151
177
|
|
|
152
178
|
```ts
|
|
153
|
-
|
|
179
|
+
interface FluxionModule {
|
|
180
|
+
handler: FluxionHandler; // Required: main handler function
|
|
181
|
+
middlewares?: FluxionMiddleware[]; // Optional: middleware array
|
|
182
|
+
methods?: HTTPMethod[]; // Optional: allowed HTTP methods
|
|
183
|
+
handlerTimeoutMs?: number; // Optional: handler timeout (ms)
|
|
184
|
+
disposer?: FluxionDispose; // Optional: cleanup function
|
|
185
|
+
}
|
|
154
186
|
```
|
|
155
187
|
|
|
156
|
-
|
|
188
|
+
## Middleware
|
|
189
|
+
|
|
190
|
+
Middleware functions execute sequentially before the handler. They can modify request parameters through side effects.
|
|
157
191
|
|
|
158
192
|
```ts
|
|
159
|
-
{
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
193
|
+
import { defineFluxionMiddleware, defineFluxionModule } from 'fluxion';
|
|
194
|
+
|
|
195
|
+
const authMiddleware = defineFluxionMiddleware(async (req, cx, rawReq, rawRes) => {
|
|
196
|
+
const token = req.headers.authorization;
|
|
197
|
+
if (!token) {
|
|
198
|
+
rawRes.statusCode = 401;
|
|
199
|
+
rawRes.end('Unauthorized');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
// Modify request for next middleware/handler
|
|
203
|
+
req.meta.user = await verifyToken(token);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
export default defineFluxionModule({
|
|
207
|
+
handler: async (req) => {
|
|
208
|
+
return { user: req.meta.user };
|
|
209
|
+
},
|
|
210
|
+
middlewares: [authMiddleware],
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Important**: Middleware timeout defaults to 3000ms. Configure via `middlewareTimeoutMs` option.
|
|
215
|
+
|
|
216
|
+
## HTTP Exceptions
|
|
217
|
+
|
|
218
|
+
Fluxion provides built-in HTTP exception classes for better error handling:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import {
|
|
222
|
+
defineFluxionModule,
|
|
223
|
+
BadRequestException,
|
|
224
|
+
UnauthorizedException,
|
|
225
|
+
NotFoundException,
|
|
226
|
+
} from 'fluxion';
|
|
227
|
+
|
|
228
|
+
export default defineFluxionModule(async (req) => {
|
|
229
|
+
if (!req.query.id) {
|
|
230
|
+
throw new BadRequestException('Missing required parameter: id');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const user = await getUser(req.query.id);
|
|
234
|
+
if (!user) {
|
|
235
|
+
throw new NotFoundException('User not found');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return { user };
|
|
239
|
+
});
|
|
168
240
|
```
|
|
169
241
|
|
|
170
|
-
|
|
242
|
+
Available exception classes:
|
|
243
|
+
|
|
244
|
+
- `BadRequestException` (400)
|
|
245
|
+
- `UnauthorizedException` (401)
|
|
246
|
+
- `ForbiddenException` (403)
|
|
247
|
+
- `NotFoundException` (404)
|
|
248
|
+
- `MethodNotAllowedException` (405)
|
|
249
|
+
- `RequestTimeoutException` (408)
|
|
250
|
+
- `ConflictException` (409)
|
|
251
|
+
- `UnsupportedMediaTypeException` (415)
|
|
252
|
+
- `UnprocessableEntityException` (422)
|
|
253
|
+
- `TooManyRequestsException` (429)
|
|
254
|
+
- `InternalServerErrorException` (500)
|
|
255
|
+
- `NotImplementedException` (501)
|
|
256
|
+
- `BadGatewayException` (502)
|
|
257
|
+
- `ServiceUnavailableException` (503)
|
|
258
|
+
- `GatewayTimeoutException` (504)
|
|
259
|
+
|
|
260
|
+
## Request Body
|
|
261
|
+
|
|
262
|
+
Fluxion parses request bodies before calling handlers (except for `GET` and `HEAD`).
|
|
263
|
+
|
|
264
|
+
Supported parsing:
|
|
265
|
+
|
|
266
|
+
- **JSON**: Objects assigned directly; primitives become `{ value }`; invalid JSON becomes `{ raw }`
|
|
267
|
+
- **Form data**: Parsed into key/value fields
|
|
268
|
+
- **Text**: Stored as `{ raw }`
|
|
269
|
+
- **Binary**: Read for size checking; body remains `{}`
|
|
270
|
+
|
|
271
|
+
Requests larger than `maxRequestBytes` return `413 Payload Too Large`.
|
|
171
272
|
|
|
172
273
|
## Response Behavior
|
|
173
274
|
|
|
174
275
|
If the handler returns a value, Fluxion responds with JSON:
|
|
175
276
|
|
|
176
277
|
```ts
|
|
177
|
-
export default async
|
|
278
|
+
export default defineFluxionModule(async () => {
|
|
178
279
|
return { ok: true };
|
|
179
|
-
}
|
|
280
|
+
});
|
|
180
281
|
```
|
|
181
282
|
|
|
182
283
|
Response:
|
|
@@ -188,31 +289,18 @@ Content-Type: application/json; charset=utf-8
|
|
|
188
289
|
{"ok":true}
|
|
189
290
|
```
|
|
190
291
|
|
|
191
|
-
You can also write to `
|
|
292
|
+
You can also write to `rawRes` manually:
|
|
192
293
|
|
|
193
294
|
```ts
|
|
194
|
-
export default async
|
|
295
|
+
export default defineFluxionModule(async (_req, _cx, _rawReq, res) => {
|
|
195
296
|
res.statusCode = 201;
|
|
196
297
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
197
298
|
res.end('created');
|
|
198
|
-
}
|
|
299
|
+
});
|
|
199
300
|
```
|
|
200
301
|
|
|
201
302
|
When `res` has already ended, Fluxion will not send another JSON response.
|
|
202
303
|
|
|
203
|
-
## Request Body
|
|
204
|
-
|
|
205
|
-
Fluxion parses request bodies before calling the handler, except for `GET` and `HEAD`.
|
|
206
|
-
|
|
207
|
-
Supported parsing:
|
|
208
|
-
|
|
209
|
-
- JSON content types: object values are assigned directly; primitive values become `{ value }`; invalid JSON becomes `{ raw }`.
|
|
210
|
-
- `application/x-www-form-urlencoded`: parsed into key/value fields.
|
|
211
|
-
- Textual content types: stored as `{ raw }`.
|
|
212
|
-
- Other binary bodies are read for size checking/log preview but `body` remains `{}`.
|
|
213
|
-
|
|
214
|
-
Requests larger than `maxRequestBytes` return `413`.
|
|
215
|
-
|
|
216
304
|
## Static Files
|
|
217
305
|
|
|
218
306
|
Non-API files are served as static resources.
|
|
@@ -222,13 +310,9 @@ Supported methods:
|
|
|
222
310
|
- `GET`
|
|
223
311
|
- `HEAD`
|
|
224
312
|
|
|
225
|
-
Other methods return `405
|
|
226
|
-
|
|
227
|
-
```http
|
|
228
|
-
Allow: GET, HEAD
|
|
229
|
-
```
|
|
313
|
+
Other methods return `405 Method Not Allowed`.
|
|
230
314
|
|
|
231
|
-
Known content types
|
|
315
|
+
Known content types: `.html`, `.css`, `.js`, `.json`, `.png`, `.jpg`, `.jpeg`, `.svg`, `.txt`, `.webp`, `.ico`, `.map`. Unknown extensions use `application/octet-stream`.
|
|
232
316
|
|
|
233
317
|
## File Watching
|
|
234
318
|
|
|
@@ -236,33 +320,27 @@ Workers watch the dynamic directory recursively.
|
|
|
236
320
|
|
|
237
321
|
On file changes:
|
|
238
322
|
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
323
|
+
- Existing files are re-registered
|
|
324
|
+
- Deleted files are removed from the router
|
|
325
|
+
- Updates are debounced by `reloadDelay` (default: `500ms`)
|
|
242
326
|
|
|
243
327
|
## Cluster Runtime
|
|
244
328
|
|
|
245
329
|
Fluxion uses Node.js `cluster`:
|
|
246
330
|
|
|
247
|
-
-
|
|
248
|
-
- Worker processes
|
|
249
|
-
- `workerOptions.maxWorkerCount`
|
|
331
|
+
- **Primary process**: Starts meta APIs and manages worker state
|
|
332
|
+
- **Worker processes**: Watch the dynamic directory and serve business traffic
|
|
333
|
+
- **Worker count**: Controlled by `workerOptions.maxWorkerCount` (default: `4`, capped by CPU count)
|
|
250
334
|
|
|
251
335
|
## Meta APIs
|
|
252
336
|
|
|
253
|
-
Meta APIs are served by the primary process on `metaPort
|
|
254
|
-
|
|
255
|
-
Default:
|
|
256
|
-
|
|
257
|
-
```ts
|
|
258
|
-
metaPort = port + 1
|
|
259
|
-
```
|
|
337
|
+
Meta APIs are served by the primary process on `metaPort` (default: `port + 1`).
|
|
260
338
|
|
|
261
|
-
|
|
339
|
+
Available endpoints:
|
|
262
340
|
|
|
263
341
|
```http
|
|
264
|
-
GET /_fluxion/healthz
|
|
265
|
-
GET /_fluxion/workers
|
|
342
|
+
GET /_fluxion/healthz # Health check
|
|
343
|
+
GET /_fluxion/workers # Worker status
|
|
266
344
|
```
|
|
267
345
|
|
|
268
346
|
Example:
|
|
@@ -272,25 +350,51 @@ curl http://127.0.0.1:3001/_fluxion/healthz
|
|
|
272
350
|
curl http://127.0.0.1:3001/_fluxion/workers
|
|
273
351
|
```
|
|
274
352
|
|
|
275
|
-
If a meta API path is requested on the business port, Fluxion returns `404` and points to the meta port.
|
|
276
|
-
|
|
277
353
|
## Options
|
|
278
354
|
|
|
279
355
|
```ts
|
|
280
356
|
interface FluxionOptions {
|
|
281
|
-
dir: string;
|
|
282
|
-
host: string;
|
|
283
|
-
port: number;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
357
|
+
dir: string; // Required: dynamic directory
|
|
358
|
+
host: string; // Required: server host
|
|
359
|
+
port: number; // Required: business server port
|
|
360
|
+
|
|
361
|
+
// Optional timeout configurations
|
|
362
|
+
handlerTimeoutMs?: number; // Default: 5000ms
|
|
363
|
+
middlewareTimeoutMs?: number; // Default: 3000ms
|
|
364
|
+
staticResourceTimeoutMs?: number; // Default: 10min
|
|
365
|
+
|
|
366
|
+
// File watching
|
|
367
|
+
reloadDelay?: number; // Default: 500ms
|
|
368
|
+
nativeWatcher?: boolean; // Use fs.watch instead of chokidar
|
|
369
|
+
|
|
370
|
+
// File registration patterns
|
|
371
|
+
include?: string[]; // Files to register (default: all)
|
|
372
|
+
apiInclude?: string[]; // Files as API handlers (default: ['*.ts'])
|
|
373
|
+
exclude?: string[]; // Files to exclude
|
|
374
|
+
|
|
375
|
+
// Meta API
|
|
376
|
+
metaPort?: number; // Default: port + 1
|
|
377
|
+
|
|
378
|
+
// Worker management
|
|
379
|
+
workerOptions?: {
|
|
380
|
+
maxWorkerCount?: number; // Default: 4
|
|
381
|
+
restartWhen?: {
|
|
382
|
+
memoryUsageGreaterThan?: number; // MB
|
|
383
|
+
healthzTimeout?: number; // Default: 30000ms
|
|
384
|
+
uptimeGreaterThan?: number; // ms
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// Request handling
|
|
389
|
+
maxRequestBytes?: number; // Default: 8_000_000
|
|
390
|
+
|
|
391
|
+
// Logging
|
|
392
|
+
logger?: 'one-line' | 'json-line' | FluxionLoggerFn;
|
|
393
|
+
|
|
394
|
+
// Module system
|
|
395
|
+
moduleDir?: string; // Default: process.cwd()
|
|
396
|
+
|
|
397
|
+
// HTTPS
|
|
294
398
|
https?: {
|
|
295
399
|
key: string | Buffer;
|
|
296
400
|
cert: string | Buffer;
|
|
@@ -299,144 +403,43 @@ interface FluxionOptions {
|
|
|
299
403
|
}
|
|
300
404
|
```
|
|
301
405
|
|
|
302
|
-
###
|
|
303
|
-
|
|
304
|
-
Dynamic directory root. Created automatically if missing.
|
|
305
|
-
|
|
306
|
-
### `host`
|
|
307
|
-
|
|
308
|
-
Host passed to `server.listen`.
|
|
309
|
-
|
|
310
|
-
### `port`
|
|
311
|
-
|
|
312
|
-
Business server port.
|
|
313
|
-
|
|
314
|
-
### `metaPort`
|
|
315
|
-
|
|
316
|
-
Primary meta API port. Defaults to `port + 1` and must be different from `port`.
|
|
317
|
-
|
|
318
|
-
### `reloadDelay`
|
|
319
|
-
|
|
320
|
-
Debounce delay for file re-registration. Defaults to `300` and must be at least `50`.
|
|
321
|
-
|
|
322
|
-
### `nativeWatcher`
|
|
323
|
-
|
|
324
|
-
Use native file watcher (`fs.watch`) instead of chokidar. Defaults to `false`.
|
|
325
|
-
|
|
326
|
-
When set to `true`, Fluxion uses Node.js built-in `fs.watch()` for file watching. When `false` (default), it uses `chokidar` for better cross-platform compatibility.
|
|
327
|
-
|
|
328
|
-
**Trade-offs:**
|
|
329
|
-
|
|
330
|
-
- `chokidar` (default): Better cross-platform support, more stable, handles edge cases
|
|
331
|
-
- `fs.watch`: Native implementation, lighter weight, but may have platform-specific quirks
|
|
332
|
-
|
|
333
|
-
Example:
|
|
406
|
+
### Timeout Configurations
|
|
334
407
|
|
|
335
408
|
```ts
|
|
336
409
|
fluxion({
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
410
|
+
// ...other options
|
|
411
|
+
handlerTimeoutMs: 10000, // Handler execution timeout
|
|
412
|
+
middlewareTimeoutMs: 5000, // Middleware execution timeout
|
|
413
|
+
staticResourceTimeoutMs: 600000, // Static file serving timeout
|
|
341
414
|
});
|
|
342
415
|
```
|
|
343
416
|
|
|
344
|
-
###
|
|
345
|
-
|
|
346
|
-
Extensions registered as API handlers. Defaults to:
|
|
347
|
-
|
|
348
|
-
```ts
|
|
349
|
-
['.ts']
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
Example:
|
|
417
|
+
### File Registration Patterns
|
|
353
418
|
|
|
354
419
|
```ts
|
|
355
420
|
fluxion({
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
apiExts: ['.ts', '.mjs'],
|
|
421
|
+
apiInclude: ['*.ts', '*.api.js'], // Register as API handlers
|
|
422
|
+
include: ['*.ts', '*.js', '*.html'], // Register any matching file
|
|
423
|
+
exclude: ['*.test.ts', '*.spec.ts'], // Exclude from registration
|
|
360
424
|
});
|
|
361
425
|
```
|
|
362
426
|
|
|
363
|
-
###
|
|
364
|
-
|
|
365
|
-
Extensions excluded from both API and static registration.
|
|
366
|
-
|
|
367
|
-
Example:
|
|
368
|
-
|
|
369
|
-
```ts
|
|
370
|
-
routerExclude: ['.map']
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
### `maxRequestBytes`
|
|
374
|
-
|
|
375
|
-
Maximum accepted request body size. Defaults to `8_000_000`.
|
|
376
|
-
|
|
377
|
-
### `logger`
|
|
378
|
-
|
|
379
|
-
Built-in modes:
|
|
380
|
-
|
|
381
|
-
- `one-line`
|
|
382
|
-
- `json-line`
|
|
383
|
-
|
|
384
|
-
A custom logger can be loaded through an injection config object whose module exports a function.
|
|
385
|
-
|
|
386
|
-
### `injections`
|
|
387
|
-
|
|
388
|
-
Worker startup injections. Each item is loaded with `require(modulePath)` and called as a factory. The resulting instances are stored on:
|
|
389
|
-
|
|
390
|
-
```ts
|
|
391
|
-
globalThis[Symbol.for('fluxion.injection')]
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
### `workerOptions`
|
|
395
|
-
|
|
396
|
-
Worker pool tuning: how many workers to spawn, and when to proactively recycle one.
|
|
397
|
-
|
|
398
|
-
```ts
|
|
399
|
-
interface WorkerOptions {
|
|
400
|
-
maxWorkerCount?: number;
|
|
401
|
-
restartWhen?: Partial<WorkerRestartWhen>;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
interface WorkerRestartWhen {
|
|
405
|
-
/** Recycle when RSS exceeds this many MB. Infinity (default) = disabled. */
|
|
406
|
-
memoryUsageGreaterThan: number;
|
|
407
|
-
/** Recycle when no Ping answer within this many ms. Default 30000. */
|
|
408
|
-
healthzTimeout: number;
|
|
409
|
-
/** Recycle after this many ms of uptime (scheduled rotation). Infinity (default) = disabled. */
|
|
410
|
-
uptimeGreaterThan: number;
|
|
411
|
-
}
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
- `maxWorkerCount` defaults to `4`, clamped to the CPU count (minimum 1).
|
|
415
|
-
- `restartWhen` lets the primary proactively recycle an unhealthy worker. The worker is hard-killed and immediately respawned when **any** configured condition is met (OR semantics):
|
|
416
|
-
- `memoryUsageGreaterThan` — RSS growth / native leak, caught before the OS OOM-killer. Disabled by default.
|
|
417
|
-
- `healthzTimeout` — a wedged event loop (worker stopped answering Ping: infinite loop, deadlock, GC storm). **Defaults to `30000`ms.**
|
|
418
|
-
- `uptimeGreaterThan` — scheduled rotation to reclaim slow growth / fragmentation. Disabled by default.
|
|
419
|
-
- Conditions are evaluated by the primary against the telemetry it already collects (RSS from stats every ~2s, liveness from Ping every 5s, uptime).
|
|
420
|
-
- A shared **anti-storm guard** bounds recycling: a given slot is restarted at most 3 times per rolling 60s, after which further restarts are suppressed and alerted instead of fork-bombing.
|
|
421
|
-
- Independently of `restartWhen`, **any worker exit — crash, OOM, or proactive recycle — triggers a respawn**, so the pool stays at `maxWorkerCount`.
|
|
427
|
+
### Worker Restart Conditions
|
|
422
428
|
|
|
423
429
|
```ts
|
|
424
430
|
fluxion({
|
|
425
|
-
// ...
|
|
426
431
|
workerOptions: {
|
|
427
432
|
maxWorkerCount: 4,
|
|
428
433
|
restartWhen: {
|
|
429
|
-
memoryUsageGreaterThan: 256,
|
|
430
|
-
|
|
431
|
-
|
|
434
|
+
memoryUsageGreaterThan: 256, // MB - recycle at 256MB RSS
|
|
435
|
+
healthzTimeout: 30000, // ms - recycle after 30s no response
|
|
436
|
+
uptimeGreaterThan: 6 * 3600_000, // ms - rotate every 6 hours
|
|
432
437
|
},
|
|
433
438
|
},
|
|
434
439
|
});
|
|
435
440
|
```
|
|
436
441
|
|
|
437
|
-
###
|
|
438
|
-
|
|
439
|
-
HTTPS server configuration. When provided, Fluxion creates an HTTPS server instead of HTTP.
|
|
442
|
+
### HTTPS Configuration
|
|
440
443
|
|
|
441
444
|
```ts
|
|
442
445
|
fluxion({
|
|
@@ -444,149 +447,56 @@ fluxion({
|
|
|
444
447
|
host: '127.0.0.1',
|
|
445
448
|
port: 9443,
|
|
446
449
|
https: {
|
|
447
|
-
key: './certs/private-key.pem',
|
|
448
|
-
cert: './certs/certificate.pem',
|
|
449
|
-
ca: './certs/ca-bundle.crt',
|
|
450
|
+
key: './certs/private-key.pem',
|
|
451
|
+
cert: './certs/certificate.pem',
|
|
452
|
+
ca: './certs/ca-bundle.crt', // Optional
|
|
450
453
|
},
|
|
451
454
|
});
|
|
452
455
|
```
|
|
453
456
|
|
|
454
457
|
Relative paths are resolved relative to `moduleDir`. PEM content can be passed directly as strings.
|
|
455
458
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
Dynamic directory root. Created automatically if missing.
|
|
459
|
-
|
|
460
|
-
### `host`
|
|
461
|
-
|
|
462
|
-
Host passed to `server.listen`.
|
|
463
|
-
|
|
464
|
-
### `port`
|
|
459
|
+
## Recent Updates
|
|
465
460
|
|
|
466
|
-
|
|
461
|
+
### v0.11.x
|
|
467
462
|
|
|
468
|
-
|
|
463
|
+
**Middleware & Module System**
|
|
469
464
|
|
|
470
|
-
|
|
465
|
+
- ✨ Added `defineFluxionModule()` and `defineFluxionMiddleware()` for type safety
|
|
466
|
+
- ✨ Middleware execution with timeout support via `middlewareTimeoutMs`
|
|
467
|
+
- ✨ Module context includes logger support
|
|
468
|
+
- ✨ Enhanced module type validation
|
|
469
|
+
- ✨ Added `meta` field for custom metadata
|
|
471
470
|
|
|
472
|
-
|
|
471
|
+
**Logging**
|
|
473
472
|
|
|
474
|
-
|
|
473
|
+
- 🔄 Unified logging interface: merged `event` and `message` into single `message` field
|
|
474
|
+
- ✨ Simplified logger API across all methods
|
|
475
475
|
|
|
476
|
-
|
|
476
|
+
**Handler Parameters**
|
|
477
477
|
|
|
478
|
-
|
|
478
|
+
- 🔄 Handler signature: `(req, cx, rawReq, rawRes)` - 4 parameters for better ergonomics
|
|
479
|
+
- ✨ Module context (`cx`) provides logger access
|
|
479
480
|
|
|
480
|
-
|
|
481
|
+
### v0.10.x
|
|
481
482
|
|
|
482
|
-
**
|
|
483
|
+
**HTTP Exception Handling**
|
|
483
484
|
|
|
484
|
-
-
|
|
485
|
-
-
|
|
485
|
+
- 🔄 Refactored HTTP exception classes with proper error codes
|
|
486
|
+
- ✨ Expanded `HttpCode` enum with additional status codes
|
|
487
|
+
- ✨ Added comprehensive HTTP exception classes
|
|
488
|
+
- 📦 Exported exception classes for user applications
|
|
486
489
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
```ts
|
|
490
|
-
fluxion({
|
|
491
|
-
dir: './dynamicDirectory',
|
|
492
|
-
host: '127.0.0.1',
|
|
493
|
-
port: 3000,
|
|
494
|
-
nativeWatcher: true, // use native fs.watch instead of chokidar
|
|
495
|
-
});
|
|
496
|
-
```
|
|
490
|
+
**Worker Management**
|
|
497
491
|
|
|
498
|
-
|
|
492
|
+
- ✨ Proactive worker recycling (memory, health, uptime)
|
|
493
|
+
- ✨ Enhanced worker pool tuning with `restartWhen` options
|
|
499
494
|
|
|
500
|
-
|
|
495
|
+
### v0.9.x
|
|
501
496
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
Example:
|
|
507
|
-
|
|
508
|
-
```ts
|
|
509
|
-
fluxion({
|
|
510
|
-
dir: './dynamicDirectory',
|
|
511
|
-
host: '127.0.0.1',
|
|
512
|
-
port: 3000,
|
|
513
|
-
apiExts: ['.ts', '.mjs'],
|
|
514
|
-
});
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
### `routerExclude`
|
|
518
|
-
|
|
519
|
-
Extensions excluded from both API and static registration.
|
|
520
|
-
|
|
521
|
-
Example:
|
|
522
|
-
|
|
523
|
-
```ts
|
|
524
|
-
routerExclude: ['.map']
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
### `maxRequestBytes`
|
|
528
|
-
|
|
529
|
-
Maximum accepted request body size. Defaults to `8_000_000`.
|
|
530
|
-
|
|
531
|
-
### `logger`
|
|
532
|
-
|
|
533
|
-
Built-in modes:
|
|
534
|
-
|
|
535
|
-
- `one-line`
|
|
536
|
-
- `json-line`
|
|
537
|
-
|
|
538
|
-
A custom logger can be loaded through an injection config object whose module exports a function.
|
|
539
|
-
|
|
540
|
-
### `injections`
|
|
541
|
-
|
|
542
|
-
Worker startup injections. Each item is loaded with `require(modulePath)` and called as a factory. The resulting instances are stored on:
|
|
543
|
-
|
|
544
|
-
```ts
|
|
545
|
-
globalThis[Symbol.for('fluxion.injection')]
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### `workerOptions`
|
|
549
|
-
|
|
550
|
-
Worker pool tuning: how many workers to spawn, and when to proactively recycle one.
|
|
551
|
-
|
|
552
|
-
```ts
|
|
553
|
-
interface WorkerOptions {
|
|
554
|
-
maxWorkerCount?: number;
|
|
555
|
-
restartWhen?: Partial<WorkerRestartWhen>;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
interface WorkerRestartWhen {
|
|
559
|
-
/** Recycle when RSS exceeds this many MB. Infinity (default) = disabled. */
|
|
560
|
-
memoryUsageGreaterThan: number;
|
|
561
|
-
/** Recycle when no Ping answer within this many ms. Default 30000. */
|
|
562
|
-
healthzTimeout: number;
|
|
563
|
-
/** Recycle after this many ms of uptime (scheduled rotation). Infinity (default) = disabled. */
|
|
564
|
-
uptimeGreaterThan: number;
|
|
565
|
-
}
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
- `maxWorkerCount` defaults to `4`, clamped to the CPU count (minimum 1).
|
|
569
|
-
- `restartWhen` lets the primary proactively recycle an unhealthy worker. The worker is hard-killed and immediately respawned when **any** configured condition is met (OR semantics):
|
|
570
|
-
- `memoryUsageGreaterThan` — RSS growth / native leak, caught before the OS OOM-killer. Disabled by default.
|
|
571
|
-
- `healthzTimeout` — a wedged event loop (worker stopped answering Ping: infinite loop, deadlock, GC storm). **Defaults to `30000`ms.**
|
|
572
|
-
- `uptimeGreaterThan` — scheduled rotation to reclaim slow growth / fragmentation. Disabled by default.
|
|
573
|
-
- Conditions are evaluated by the primary against the telemetry it already collects (RSS from stats every ~2s, liveness from Ping every 5s, uptime).
|
|
574
|
-
- A shared **anti-storm guard** bounds recycling: a given slot is restarted at most 3 times per rolling 60s, after which further restarts are suppressed and alerted instead of fork-bombing.
|
|
575
|
-
- Independently of `restartWhen`, **any worker exit — crash, OOM, or proactive recycle — triggers a respawn**, so the pool stays at `maxWorkerCount`.
|
|
576
|
-
|
|
577
|
-
```ts
|
|
578
|
-
fluxion({
|
|
579
|
-
// ...
|
|
580
|
-
workerOptions: {
|
|
581
|
-
maxWorkerCount: 4,
|
|
582
|
-
restartWhen: {
|
|
583
|
-
memoryUsageGreaterThan: 256, // MB; recycle a leaking worker at 256MB RSS
|
|
584
|
-
// healthzTimeout defaults to 30000 — recycle wedged workers after 30s
|
|
585
|
-
// uptimeGreaterThan: 6 * 3600_000, // optionally rotate every 6h
|
|
586
|
-
},
|
|
587
|
-
},
|
|
588
|
-
});
|
|
589
|
-
```
|
|
497
|
+
- ✨ Initial middleware support
|
|
498
|
+
- ✨ Worker restart conditions for memory management
|
|
499
|
+
- ✨ Restructured build and publish flow
|
|
590
500
|
|
|
591
501
|
## Build and Test
|
|
592
502
|
|
|
@@ -595,4 +505,3 @@ pnpm build
|
|
|
595
505
|
pnpm test
|
|
596
506
|
pnpm lint
|
|
597
507
|
```
|
|
598
|
-
|