fluxion-ts 0.0.12 → 0.1.2
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 +298 -102
- package/dist/{index.mjs → index.cjs} +322 -184
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +85 -1
- package/package.json +20 -21
- package/dist/index.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<a href="https://baendlorel.github.io/fluxion/">
|
|
9
|
-
<img src="https://raw.githubusercontent.com/baendlorel/fluxion/refs/heads/
|
|
9
|
+
<img src="https://raw.githubusercontent.com/baendlorel/fluxion/refs/heads/dev/assets/fluxion.png" width="240px" alt="fluxion logo" />
|
|
10
10
|
</a>
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
Fluxion is a filesystem-routing dynamic server for Node.js.
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
15
|
+
- Route files from a dynamic directory
|
|
16
|
+
- Load API handlers by extension, default: `.ts`
|
|
17
|
+
- Serve other files as static resources
|
|
18
|
+
- Run the business server in worker processes
|
|
19
|
+
- Expose runtime status from the primary process through meta APIs
|
|
20
|
+
- Automatically serialize handler return values as JSON
|
|
19
21
|
|
|
20
22
|
## Install
|
|
21
23
|
|
|
@@ -25,8 +27,6 @@ npm install fluxion
|
|
|
25
27
|
|
|
26
28
|
## Quick Start
|
|
27
29
|
|
|
28
|
-
### 1) Start the server
|
|
29
|
-
|
|
30
30
|
Create `server.mjs`:
|
|
31
31
|
|
|
32
32
|
```js
|
|
@@ -39,15 +39,13 @@ fluxion({
|
|
|
39
39
|
});
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Create `dynamicDirectory/hello.mjs`:
|
|
42
|
+
Create `dynamicDirectory/hello.ts`:
|
|
45
43
|
|
|
46
|
-
```
|
|
47
|
-
export default function handler(
|
|
44
|
+
```ts
|
|
45
|
+
export default async function handler(req) {
|
|
48
46
|
return {
|
|
49
47
|
message: 'hello fluxion',
|
|
50
|
-
|
|
48
|
+
path: req.url.pathname,
|
|
51
49
|
};
|
|
52
50
|
}
|
|
53
51
|
```
|
|
@@ -58,135 +56,333 @@ Run:
|
|
|
58
56
|
node server.mjs
|
|
59
57
|
```
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
Request:
|
|
62
60
|
|
|
63
61
|
```bash
|
|
64
|
-
curl http://127.0.0.1:3000/hello
|
|
62
|
+
curl http://127.0.0.1:3000/hello.ts
|
|
65
63
|
```
|
|
66
64
|
|
|
67
|
-
|
|
65
|
+
Response:
|
|
68
66
|
|
|
69
|
-
|
|
67
|
+
```json
|
|
68
|
+
{"message":"hello fluxion","path":"/hello.ts"}
|
|
69
|
+
```
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
- `dynamicDirectory/user.mjs` -> `/user`
|
|
73
|
-
- `dynamicDirectory/user/index.mjs` -> `/user`
|
|
74
|
-
- Non-`.mjs` files are served as static files (`GET/HEAD`)
|
|
75
|
-
- Directories/files starting with `_` are private and not routable
|
|
71
|
+
## Development Entry
|
|
76
72
|
|
|
77
|
-
|
|
73
|
+
In this repository, `pnpm dev` runs `src/index.ts` directly and starts Fluxion unless `NODE_ENV=production`.
|
|
78
74
|
|
|
79
|
-
|
|
75
|
+
Default development options:
|
|
80
76
|
|
|
81
|
-
```
|
|
82
|
-
|
|
77
|
+
```ts
|
|
78
|
+
fluxion({
|
|
79
|
+
dir: process.env.DYNAMIC_DIRECTORY ?? 'dynamicDirectory',
|
|
80
|
+
host: process.env.HOST ?? 'localhost',
|
|
81
|
+
port: process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3000,
|
|
82
|
+
reloadDelay: process.env.RELOAD_DELAY ? Number.parseInt(process.env.RELOAD_DELAY, 10) : undefined,
|
|
83
|
+
workerOptions: {
|
|
84
|
+
maxWorkerCount: 1,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pnpm dev
|
|
93
|
+
curl http://localhost:3000/test.ts
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Routing
|
|
97
|
+
|
|
98
|
+
Fluxion registers every file under `dir` recursively.
|
|
99
|
+
|
|
100
|
+
With default options:
|
|
101
|
+
|
|
102
|
+
- Files ending with `.ts` are API handlers.
|
|
103
|
+
- Files with other extensions are static resources.
|
|
104
|
+
- Request paths match file paths relative to `dir`.
|
|
105
|
+
- File extensions are part of the route path.
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
|
|
109
|
+
| File | Route | Type |
|
|
110
|
+
| ---------------------------------- | ------------------ | ----------- |
|
|
111
|
+
| `dynamicDirectory/test.ts` | `/test.ts` | API handler |
|
|
112
|
+
| `dynamicDirectory/user/profile.ts` | `/user/profile.ts` | API handler |
|
|
113
|
+
| `dynamicDirectory/index.html` | `/index.html` | Static file |
|
|
114
|
+
| `dynamicDirectory/assets/app.js` | `/assets/app.js` | Static file |
|
|
115
|
+
|
|
116
|
+
A request to `/hello` does not match `hello.ts`; request `/hello.ts` or change `apiExts`/routing behavior in code.
|
|
117
|
+
|
|
118
|
+
## API Handlers
|
|
119
|
+
|
|
120
|
+
An API file must export a function by one of these forms:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
export default async function handler(req, rawReq, rawRes) {
|
|
83
124
|
return { ok: true };
|
|
84
125
|
}
|
|
85
126
|
```
|
|
86
127
|
|
|
87
|
-
|
|
128
|
+
```ts
|
|
129
|
+
export async function handler(req, rawReq, rawRes) {
|
|
130
|
+
return { ok: true };
|
|
131
|
+
}
|
|
132
|
+
```
|
|
88
133
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
],
|
|
98
|
-
handler(_req, _res, context) {
|
|
99
|
-
return {
|
|
100
|
-
hash: context.crypto.createHash('sha1').update('abc').digest('hex'),
|
|
101
|
-
};
|
|
102
|
-
},
|
|
103
|
-
};
|
|
134
|
+
Common local style:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { defineFluxionHandler } from '@/index.js';
|
|
138
|
+
|
|
139
|
+
export default defineFluxionHandler(async (req) => {
|
|
140
|
+
return req.url.pathname + '成功';
|
|
141
|
+
});
|
|
104
142
|
```
|
|
105
143
|
|
|
106
|
-
|
|
144
|
+
### Handler Arguments
|
|
107
145
|
|
|
108
|
-
|
|
146
|
+
```ts
|
|
147
|
+
handler(normalizedRequest, rawRequest, rawResponse)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`normalizedRequest` contains:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
{
|
|
154
|
+
method: string;
|
|
155
|
+
ip: string;
|
|
156
|
+
url: URL;
|
|
157
|
+
query: Record<string, string | string[]>;
|
|
158
|
+
body: Record<string, any>;
|
|
159
|
+
headers: IncomingHttpHeaders;
|
|
160
|
+
cookie: Record<string, string>;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
109
163
|
|
|
110
|
-
|
|
111
|
-
- set `content-type` to `application/json; charset=utf-8` (if missing)
|
|
112
|
-
- serialize the return value with `JSON.stringify(...)`
|
|
164
|
+
`rawRequest` and `rawResponse` are Node.js HTTP objects.
|
|
113
165
|
|
|
114
|
-
|
|
166
|
+
## Response Behavior
|
|
115
167
|
|
|
116
|
-
|
|
117
|
-
2. Or fully control `res` manually (streaming, file download, etc.)
|
|
168
|
+
If the handler returns a value, Fluxion responds with JSON:
|
|
118
169
|
|
|
119
|
-
|
|
170
|
+
```ts
|
|
171
|
+
export default async function handler() {
|
|
172
|
+
return { ok: true };
|
|
173
|
+
}
|
|
174
|
+
```
|
|
120
175
|
|
|
121
|
-
|
|
176
|
+
Response:
|
|
122
177
|
|
|
123
|
-
|
|
178
|
+
```http
|
|
179
|
+
HTTP/1.1 200 OK
|
|
180
|
+
Content-Type: application/json; charset=utf-8
|
|
124
181
|
|
|
125
|
-
|
|
126
|
-
npm install mysql2
|
|
182
|
+
{"ok":true}
|
|
127
183
|
```
|
|
128
184
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
185
|
+
You can also write to `rawResponse` manually:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
export default async function handler(_req, _rawReq, res) {
|
|
189
|
+
res.statusCode = 201;
|
|
190
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
191
|
+
res.end('created');
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
When `res` has already ended, Fluxion will not send another JSON response.
|
|
196
|
+
|
|
197
|
+
## Request Body
|
|
198
|
+
|
|
199
|
+
Fluxion parses request bodies before calling the handler, except for `GET` and `HEAD`.
|
|
200
|
+
|
|
201
|
+
Supported parsing:
|
|
202
|
+
|
|
203
|
+
- JSON content types: object values are assigned directly; primitive values become `{ value }`; invalid JSON becomes `{ raw }`.
|
|
204
|
+
- `application/x-www-form-urlencoded`: parsed into key/value fields.
|
|
205
|
+
- Textual content types: stored as `{ raw }`.
|
|
206
|
+
- Other binary bodies are read for size checking/log preview but `body` remains `{}`.
|
|
207
|
+
|
|
208
|
+
Requests larger than `maxRequestBytes` return `413`.
|
|
209
|
+
|
|
210
|
+
## Static Files
|
|
211
|
+
|
|
212
|
+
Non-API files are served as static resources.
|
|
213
|
+
|
|
214
|
+
Supported methods:
|
|
215
|
+
|
|
216
|
+
- `GET`
|
|
217
|
+
- `HEAD`
|
|
218
|
+
|
|
219
|
+
Other methods return `405` with:
|
|
220
|
+
|
|
221
|
+
```http
|
|
222
|
+
Allow: GET, HEAD
|
|
149
223
|
```
|
|
150
224
|
|
|
151
|
-
|
|
225
|
+
Known content types include `.html`, `.css`, `.js`, `.json`, `.png`, `.jpg`, `.jpeg`, `.svg`, `.txt`, `.webp`, `.ico`, and `.map`. Unknown extensions use `application/octet-stream`.
|
|
152
226
|
|
|
153
|
-
|
|
154
|
-
- `injectKey`: target key in `context[injectKey]`
|
|
155
|
-
- `options`: custom config passed into `factory`
|
|
156
|
-
- `factory`: `(importedModule, options, runtime) => injectedValue`
|
|
227
|
+
## File Watching
|
|
157
228
|
|
|
158
|
-
|
|
229
|
+
Workers watch the dynamic directory recursively.
|
|
159
230
|
|
|
160
|
-
|
|
231
|
+
On file changes:
|
|
161
232
|
|
|
162
|
-
|
|
233
|
+
- existing files are re-registered;
|
|
234
|
+
- deleted files are removed from the router;
|
|
235
|
+
- updates are debounced by `reloadDelay`, default `300ms`.
|
|
163
236
|
|
|
164
|
-
|
|
237
|
+
## Cluster Runtime
|
|
165
238
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
- `
|
|
171
|
-
- `logger`: `one-line` / `json-line` / custom function
|
|
239
|
+
Fluxion uses Node.js `cluster`:
|
|
240
|
+
|
|
241
|
+
- The primary process starts meta APIs and manages worker state.
|
|
242
|
+
- Worker processes watch the dynamic directory and serve business traffic.
|
|
243
|
+
- `workerOptions.maxWorkerCount` controls worker count. Default is capped by CPU count.
|
|
172
244
|
|
|
173
245
|
## Meta APIs
|
|
174
246
|
|
|
175
|
-
Meta APIs are served by the primary process on `metaPort
|
|
247
|
+
Meta APIs are served by the primary process on `metaPort`.
|
|
176
248
|
|
|
177
|
-
|
|
178
|
-
- `GET /_fluxion/workers` (worker status + cpu/memory stats)
|
|
249
|
+
Default:
|
|
179
250
|
|
|
180
|
-
|
|
251
|
+
```ts
|
|
252
|
+
metaPort = port + 1
|
|
253
|
+
```
|
|
181
254
|
|
|
182
|
-
|
|
255
|
+
Endpoints:
|
|
183
256
|
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
257
|
+
```http
|
|
258
|
+
GET /_fluxion/healthz
|
|
259
|
+
GET /_fluxion/workers
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
curl http://127.0.0.1:3001/_fluxion/healthz
|
|
266
|
+
curl http://127.0.0.1:3001/_fluxion/workers
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
If a meta API path is requested on the business port, Fluxion returns `404` and points to the meta port.
|
|
270
|
+
|
|
271
|
+
## Options
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
interface FluxionOptions {
|
|
275
|
+
dir: string;
|
|
276
|
+
host: string;
|
|
277
|
+
port: number;
|
|
278
|
+
reloadDelay?: number;
|
|
279
|
+
metaPort?: number;
|
|
280
|
+
injections?: InjectionConfig[];
|
|
281
|
+
moduleDir?: string;
|
|
282
|
+
workerOptions?: Partial<WorkerOptions>;
|
|
283
|
+
maxRequestBytes?: number;
|
|
284
|
+
logger?: 'one-line' | 'json-line' | InjectionConfig;
|
|
285
|
+
apiExts?: string[];
|
|
286
|
+
routerExclude?: string[];
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### `dir`
|
|
291
|
+
|
|
292
|
+
Dynamic directory root. Created automatically if missing.
|
|
293
|
+
|
|
294
|
+
### `host`
|
|
295
|
+
|
|
296
|
+
Host passed to `server.listen`.
|
|
297
|
+
|
|
298
|
+
### `port`
|
|
299
|
+
|
|
300
|
+
Business server port.
|
|
301
|
+
|
|
302
|
+
### `metaPort`
|
|
303
|
+
|
|
304
|
+
Primary meta API port. Defaults to `port + 1` and must be different from `port`.
|
|
305
|
+
|
|
306
|
+
### `reloadDelay`
|
|
307
|
+
|
|
308
|
+
Debounce delay for file re-registration. Defaults to `300` and must be at least `50`.
|
|
309
|
+
|
|
310
|
+
### `apiExts`
|
|
311
|
+
|
|
312
|
+
Extensions registered as API handlers. Defaults to:
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
['.ts']
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Example:
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
fluxion({
|
|
322
|
+
dir: './dynamicDirectory',
|
|
323
|
+
host: '127.0.0.1',
|
|
324
|
+
port: 3000,
|
|
325
|
+
apiExts: ['.ts', '.mjs'],
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### `routerExclude`
|
|
330
|
+
|
|
331
|
+
Extensions excluded from both API and static registration.
|
|
332
|
+
|
|
333
|
+
Example:
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
routerExclude: ['.map']
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### `maxRequestBytes`
|
|
340
|
+
|
|
341
|
+
Maximum accepted request body size. Defaults to `8_000_000`.
|
|
342
|
+
|
|
343
|
+
### `logger`
|
|
344
|
+
|
|
345
|
+
Built-in modes:
|
|
346
|
+
|
|
347
|
+
- `one-line`
|
|
348
|
+
- `json-line`
|
|
349
|
+
|
|
350
|
+
A custom logger can be loaded through an injection config object whose module exports a function.
|
|
351
|
+
|
|
352
|
+
### `injections`
|
|
353
|
+
|
|
354
|
+
Worker startup injections. Each item is loaded with `require(modulePath)` and called as a factory. The resulting instances are stored on:
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
globalThis[Symbol.for('fluxion.injection')]
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### `workerOptions`
|
|
361
|
+
|
|
362
|
+
Runtime tuning options:
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
interface WorkerOptions {
|
|
366
|
+
maxWorkerCount: number;
|
|
367
|
+
requestTimeoutMs: number;
|
|
368
|
+
maxInflight: number;
|
|
369
|
+
memorySoftLimitMb: number;
|
|
370
|
+
memoryHardLimitMb: number;
|
|
371
|
+
memorySampleIntervalMs: number;
|
|
372
|
+
maxOldGenerationSizeMb: number;
|
|
373
|
+
maxYoungGenerationSizeMb: number;
|
|
374
|
+
stackSizeMb: number;
|
|
375
|
+
maxResponseBytes: number;
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Current implementation uses `maxWorkerCount` for process count and reports CPU/memory telemetry from workers.
|
|
380
|
+
|
|
381
|
+
## Build and Test
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
pnpm build
|
|
385
|
+
pnpm test
|
|
386
|
+
pnpm lint
|
|
190
387
|
```
|
|
191
388
|
|
|
192
|
-
Use `modules` for dependency injection.
|