fluxion-ts 0.1.5 → 0.2.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
@@ -288,6 +288,11 @@ interface FluxionOptions {
288
288
  logger?: 'one-line' | 'json-line' | InjectionConfig;
289
289
  apiExts?: string[];
290
290
  routerExclude?: string[];
291
+ https?: {
292
+ key: string | Buffer;
293
+ cert: string | Buffer;
294
+ ca?: string | Buffer | Array<string | Buffer>;
295
+ };
291
296
  }
292
297
  ```
293
298
 
@@ -382,6 +387,116 @@ interface WorkerOptions {
382
387
 
383
388
  Current implementation uses `maxWorkerCount` for process count and reports CPU/memory telemetry from workers.
384
389
 
390
+ ### `https`
391
+
392
+ HTTPS server configuration. When provided, Fluxion creates an HTTPS server instead of HTTP.
393
+
394
+ ```ts
395
+ fluxion({
396
+ dir: './dynamicDirectory',
397
+ host: '127.0.0.1',
398
+ port: 9443,
399
+ https: {
400
+ key: './certs/private-key.pem', // 私钥文件路径或内容
401
+ cert: './certs/certificate.pem', // 证书文件路径或内容
402
+ ca: './certs/ca-bundle.crt', // 可选:CA 证书链
403
+ },
404
+ });
405
+ ```
406
+
407
+ Relative paths are resolved relative to `moduleDir`. PEM content can be passed directly as strings.
408
+
409
+ ### `dir`
410
+
411
+ Dynamic directory root. Created automatically if missing.
412
+
413
+ ### `host`
414
+
415
+ Host passed to `server.listen`.
416
+
417
+ ### `port`
418
+
419
+ Business server port.
420
+
421
+ ### `metaPort`
422
+
423
+ Primary meta API port. Defaults to `port + 1` and must be different from `port`.
424
+
425
+ ### `reloadDelay`
426
+
427
+ Debounce delay for file re-registration. Defaults to `300` and must be at least `50`.
428
+
429
+ ### `apiExts`
430
+
431
+ Extensions registered as API handlers. Defaults to:
432
+
433
+ ```ts
434
+ ['.ts']
435
+ ```
436
+
437
+ Example:
438
+
439
+ ```ts
440
+ fluxion({
441
+ dir: './dynamicDirectory',
442
+ host: '127.0.0.1',
443
+ port: 3000,
444
+ apiExts: ['.ts', '.mjs'],
445
+ });
446
+ ```
447
+
448
+ ### `routerExclude`
449
+
450
+ Extensions excluded from both API and static registration.
451
+
452
+ Example:
453
+
454
+ ```ts
455
+ routerExclude: ['.map']
456
+ ```
457
+
458
+ ### `maxRequestBytes`
459
+
460
+ Maximum accepted request body size. Defaults to `8_000_000`.
461
+
462
+ ### `logger`
463
+
464
+ Built-in modes:
465
+
466
+ - `one-line`
467
+ - `json-line`
468
+
469
+ A custom logger can be loaded through an injection config object whose module exports a function.
470
+
471
+ ### `injections`
472
+
473
+ Worker startup injections. Each item is loaded with `require(modulePath)` and called as a factory. The resulting instances are stored on:
474
+
475
+ ```ts
476
+ globalThis[Symbol.for('fluxion.injection')]
477
+ ```
478
+
479
+ ### `workerOptions`
480
+
481
+ Runtime tuning options:
482
+
483
+ ```ts
484
+ interface WorkerOptions {
485
+ maxWorkerCount: number;
486
+ requestTimeoutMs: number;
487
+ maxInflight: number;
488
+ memorySoftLimitMb: number;
489
+ memoryHardLimitMb: number;
490
+ memorySampleIntervalMs: number;
491
+ maxOldGenerationSizeMb: number;
492
+ maxYoungGenerationSizeMb: number;
493
+ stackSizeMb: number;
494
+ maxResponseBytes: number;
495
+ }
496
+ ```
497
+
498
+ Current implementation uses `maxWorkerCount` for process count and reports CPU/memory telemetry from workers.
499
+
385
500
  ## Build and Test
386
501
 
387
502
  ```bash
package/dist/index.cjs CHANGED
@@ -1,10 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var fs = require('node:fs');
4
+ var path = require('node:path');
4
5
  var cluster = require('node:cluster');
5
6
  var os = require('node:os');
6
7
  var http = require('node:http');
7
- var path = require('node:path');
8
+ var https = require('node:https');
8
9
 
9
10
  const ANSI_BACKGROUND_OFFSET = 10;
10
11
 
@@ -660,12 +661,56 @@ function expectLoggerOption(o) {
660
661
  }
661
662
  throw new Error(`[FluxionTs error] Invalid logger option, must be 'one-line', 'json-line' or { modulePath: string; name: string; }`);
662
663
  }
664
+ /**
665
+ * Read certificate content from a file path or return the content directly.
666
+ */
667
+ function readCertificateContent(content, moduleDir) {
668
+ if (Buffer.isBuffer(content)) {
669
+ return content;
670
+ }
671
+ if (typeof content === 'string') {
672
+ // Check if it looks like a file path (not a PEM certificate)
673
+ // PEM certificates start with "-----BEGIN"
674
+ if (!content.startsWith('-----BEGIN')) {
675
+ const filePath = path.isAbsolute(content) ? content : path.join(moduleDir, content);
676
+ if (fs.existsSync(filePath)) {
677
+ return fs.readFileSync(filePath);
678
+ }
679
+ }
680
+ return Buffer.from(content);
681
+ }
682
+ throw new Error('[FluxionTs error] Certificate content must be a string or Buffer');
683
+ }
684
+ /**
685
+ * Normalize HTTPS options.
686
+ */
687
+ function normalizeHttpsOptions(https, moduleDir) {
688
+ if (!https) {
689
+ return undefined;
690
+ }
691
+ expect.isObject(https, 'FluxionOptions.https must be an object');
692
+ expect.isString(https.key, 'FluxionOptions.https.key must be a string');
693
+ expect.isString(https.cert, 'FluxionOptions.https.cert must be a string');
694
+ const result = {
695
+ key: readCertificateContent(https.key, moduleDir),
696
+ cert: readCertificateContent(https.cert, moduleDir),
697
+ };
698
+ if (https.ca !== undefined) {
699
+ if (Array.isArray(https.ca)) {
700
+ result.ca = https.ca.map((item) => readCertificateContent(item, moduleDir));
701
+ }
702
+ else {
703
+ result.ca = readCertificateContent(https.ca, moduleDir);
704
+ }
705
+ }
706
+ return result;
707
+ }
663
708
  /**
664
709
  * Normalize options and create necessary resources like the dynamic directory and logger.
665
710
  */
666
711
  function normalizeOptions(options) {
667
712
  expect.isObject(options, 'FluxionOptions must be an object');
668
- let { dir, host, port, metaPort, injections = [], moduleDir = process.cwd(), workerOptions = {}, maxRequestBytes = 8000000, reloadDelay = 300, apiExts = ['.ts'], routerExclude = [], } = options;
713
+ let { dir, host, port, metaPort, injections = [], moduleDir = process.cwd(), workerOptions = {}, maxRequestBytes = 8_000_000, reloadDelay = 300, apiExts = ['.ts'], routerExclude = [], https, } = options;
669
714
  const logger = options.logger ?? 'one-line';
670
715
  expectLoggerOption(logger);
671
716
  expect.isString(dir, 'FluxionOptions.dir must be a string');
@@ -679,7 +724,7 @@ function normalizeOptions(options) {
679
724
  if (port > 65535) {
680
725
  throw new Error('[FluxionTs error] FluxionOptions.port must be less than or equal to 65535');
681
726
  }
682
- metaPort ?? (metaPort = port + 1);
727
+ metaPort ??= port + 1;
683
728
  expect.isPositiveInteger(metaPort, 'FluxionOptions.metaPort must be a positive integer');
684
729
  if (metaPort > 65535) {
685
730
  throw new Error('[FluxionTs error] FluxionOptions.metaPort must be less than or equal to 65535');
@@ -706,6 +751,7 @@ function normalizeOptions(options) {
706
751
  logger,
707
752
  apiExts,
708
753
  routerExclude,
754
+ https: normalizeHttpsOptions(https, moduleDir),
709
755
  };
710
756
  }
711
757
 
@@ -933,9 +979,13 @@ function PromiseTry(fn, ...args) {
933
979
  return new Promise((resolve, reject) => {
934
980
  // in case `fn` throws synchronously, we catch it and reject the promise
935
981
  try {
936
- fn(...args)
937
- .then(resolve)
938
- .catch(reject);
982
+ const r = fn(...args);
983
+ if (r instanceof Promise) {
984
+ r.then(resolve).catch(reject);
985
+ }
986
+ else {
987
+ resolve(r);
988
+ }
939
989
  }
940
990
  catch (error) {
941
991
  reject(error);
@@ -1200,7 +1250,7 @@ function parseCookie(cookieHeader) {
1200
1250
  }
1201
1251
 
1202
1252
  function createWorkerServer(cx) {
1203
- const server = http.createServer(async (req, res) => {
1253
+ const requestHandler = async (req, res) => {
1204
1254
  const method = req.method ?? 'GET';
1205
1255
  const ip = getRealIp(req);
1206
1256
  const url = toURL(req.url);
@@ -1276,7 +1326,14 @@ function createWorkerServer(cx) {
1276
1326
  safeSendJson(res, { message: 'Internal Server Error' }, 500 /* HttpCode.InternalServerError */);
1277
1327
  }
1278
1328
  }
1279
- });
1329
+ };
1330
+ const server = cx.options.https
1331
+ ? https.createServer({
1332
+ key: cx.options.https.key,
1333
+ cert: cx.options.https.cert,
1334
+ ca: cx.options.https.ca,
1335
+ }, requestHandler)
1336
+ : http.createServer(requestHandler);
1280
1337
  server.on('close', () => {
1281
1338
  cx.logger.info('ServerClosed', {
1282
1339
  host: cx.options.host,
@@ -1286,6 +1343,7 @@ function createWorkerServer(cx) {
1286
1343
  server.listen(cx.options.port, cx.options.host, () => {
1287
1344
  cx.logger.info('ServerStarted', {
1288
1345
  pid: process.pid,
1346
+ protocol: cx.options.https ? 'https' : 'http',
1289
1347
  host: cx.options.host,
1290
1348
  port: cx.options.port,
1291
1349
  });