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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Fluxion
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/fluxion.svg)](https://www.npmjs.com/package/fluxion)
4
- [![npm downloads](https://img.shields.io/npm/dm/fluxion.svg)](https://www.npmjs.com/package/fluxion)
3
+ [![npm version](https://img.shields.io/npm/v/fluxion.svg)](https://www.npmjs.org/package/fluxion)
4
+ [![npm downloads](https://img.shields.io/npm/dm/fluxion.svg)](https://www.npmjs.org/package/fluxion)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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, default: `.ts`
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 { defineFluxionHandler } from 'fluxion';
46
+ import { defineFluxionModule } from 'fluxion';
46
47
 
47
- // defineFluxionHandler is only for better type inference and editor support.
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: process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3000,
88
- reloadDelay: process.env.RELOAD_DELAY ? Number.parseInt(process.env.RELOAD_DELAY, 10) : undefined,
84
+ port: Number.parseInt(process.env.PORT ?? '9000', 10),
85
+ metaPort: Number.parseInt(process.env.META_PORT ?? '9001', 10),
89
86
  workerOptions: {
90
- maxWorkerCount: 1,
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 every file under `dir` recursively.
105
-
106
- With default options:
94
+ Fluxion registers files under `dir` based on glob patterns:
107
95
 
108
- - Files ending with `.ts` are API handlers.
109
- - Files with other extensions are static resources.
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 | Route | Type |
116
- | ---------------------------------- | ------------------ | ----------- |
117
- | `dynamicDirectory/test.ts` | `/test.ts` | API handler |
118
- | `dynamicDirectory/user/profile.ts` | `/user/profile.ts` | API handler |
119
- | `dynamicDirectory/index.html` | `/index.html` | Static file |
120
- | `dynamicDirectory/assets/app.js` | `/assets/app.js` | Static file |
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 file must export a function by one of these forms:
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
- export default async function handler(req, rawReq, rawRes) {
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
- export async function handler(req, rawReq, rawRes) {
136
- return { ok: true };
137
- }
129
+ handler(req, cx, rawReq, rawRes)
138
130
  ```
139
131
 
140
- Common local style:
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 { defineFluxionHandler } from '@/index.js';
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 defineFluxionHandler(async (req) => {
146
- return req.url.pathname + '成功';
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
- ### Handler Arguments
176
+ ### Module Options
151
177
 
152
178
  ```ts
153
- handler(normalizedRequest, rawRequest, rawResponse)
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
- `normalizedRequest` contains:
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
- method: string;
161
- ip: string;
162
- url: URL;
163
- query: Record<string, string | string[]>;
164
- body: Record<string, any>;
165
- headers: IncomingHttpHeaders;
166
- cookie: Record<string, string>;
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
- `rawRequest` and `rawResponse` are Node.js HTTP objects.
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 function handler() {
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 `rawResponse` manually:
292
+ You can also write to `rawRes` manually:
192
293
 
193
294
  ```ts
194
- export default async function handler(_req, _rawReq, res) {
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` with:
226
-
227
- ```http
228
- Allow: GET, HEAD
229
- ```
313
+ Other methods return `405 Method Not Allowed`.
230
314
 
231
- Known content types include `.html`, `.css`, `.js`, `.json`, `.png`, `.jpg`, `.jpeg`, `.svg`, `.txt`, `.webp`, `.ico`, and `.map`. Unknown extensions use `application/octet-stream`.
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
- - existing files are re-registered;
240
- - deleted files are removed from the router;
241
- - updates are debounced by `reloadDelay`, default `300ms`.
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
- - The primary process starts meta APIs and manages worker state.
248
- - Worker processes watch the dynamic directory and serve business traffic.
249
- - `workerOptions.maxWorkerCount` controls worker count. Default is capped by CPU count.
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
- Endpoints:
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
- reloadDelay?: number;
285
- metaPort?: number;
286
- nativeWatcher?: boolean;
287
- injections?: InjectionConfig[];
288
- moduleDir?: string;
289
- workerOptions?: WorkerOptions;
290
- maxRequestBytes?: number;
291
- logger?: 'one-line' | 'json-line' | InjectionConfig;
292
- apiExts?: string[];
293
- routerExclude?: string[];
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
- ### `dir`
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
- dir: './dynamicDirectory',
338
- host: '127.0.0.1',
339
- port: 3000,
340
- nativeWatcher: true, // use native fs.watch instead of chokidar
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
- ### `apiExts`
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
- dir: './dynamicDirectory',
357
- host: '127.0.0.1',
358
- port: 3000,
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
- ### `routerExclude`
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, // MB; recycle a leaking worker at 256MB RSS
430
- // healthzTimeout defaults to 30000 recycle wedged workers after 30s
431
- // uptimeGreaterThan: 6 * 3600_000, // optionally rotate every 6h
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
- ### `https`
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', // 可选:CA 证书链
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
- ### `dir`
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
- Business server port.
461
+ ### v0.11.x
467
462
 
468
- ### `metaPort`
463
+ **Middleware & Module System**
469
464
 
470
- Primary meta API port. Defaults to `port + 1` and must be different from `port`.
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
- ### `reloadDelay`
471
+ **Logging**
473
472
 
474
- Debounce delay for file re-registration. Defaults to `300` and must be at least `50`.
473
+ - 🔄 Unified logging interface: merged `event` and `message` into single `message` field
474
+ - ✨ Simplified logger API across all methods
475
475
 
476
- ### `nativeWatcher`
476
+ **Handler Parameters**
477
477
 
478
- Use native file watcher (`fs.watch`) instead of chokidar. Defaults to `false`.
478
+ - 🔄 Handler signature: `(req, cx, rawReq, rawRes)` - 4 parameters for better ergonomics
479
+ - ✨ Module context (`cx`) provides logger access
479
480
 
480
- 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.
481
+ ### v0.10.x
481
482
 
482
- **Trade-offs:**
483
+ **HTTP Exception Handling**
483
484
 
484
- - `chokidar` (default): Better cross-platform support, more stable, handles edge cases
485
- - `fs.watch`: Native implementation, lighter weight, but may have platform-specific quirks
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
- Example:
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
- ### `apiExts`
492
+ - ✨ Proactive worker recycling (memory, health, uptime)
493
+ - ✨ Enhanced worker pool tuning with `restartWhen` options
499
494
 
500
- Extensions registered as API handlers. Defaults to:
495
+ ### v0.9.x
501
496
 
502
- ```ts
503
- ['.ts']
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
-