npm.io
0.14.1 • Published 6h agoCLI

fluxion-ts

Licence
MIT
Version
0.14.1
Deps
4
Size
372 kB
Vulns
0
Weekly
2.4K

Fluxion

npm version npm downloads License: MIT

fluxion logo

Fluxion is a filesystem-routing dynamic server for Node.js.

  • Route files from a dynamic directory by chokidar or native fs.watch
  • Load API handlers by extension patterns (default: *.ts)
  • Serve other files as static resources
  • Run the business server in worker processes
  • Expose runtime status from the primary process through meta APIs
  • Automatically serialize handler return values as JSON
  • Built-in middleware system and HTTP exception handling

Install

pnpm add fluxion

Command Line Interface

You need tsx to run fluxion

fluxion # loads fluxion.config.ts by default
fluxion --config custom-fluxion.config.ts

Quick Start

Create server.mjs:

import { fluxion } from 'fluxion';

fluxion({
  dir: './dynamicDirectory',
  host: '127.0.0.1',
  port: 3000,
});

Create dynamicDirectory/hello.ts:

import { defineFluxionModule } from 'fluxion';

export default defineFluxionModule(async (req, cx) => {
  return {
    message: 'hello fluxion',
    path: req.url.pathname,
  };
});

Run:

node server.mjs

Request:

curl http://127.0.0.1:3000/hello.ts

Response:

{"message":"hello fluxion","path":"/hello.ts"}

Development Entry

In this repository, pnpm dev runs src/index.ts directly and starts Fluxion unless NODE_ENV=production.

Default development options:

fluxion({
  dir: process.env.DYNAMIC_DIRECTORY ?? 'dynamicDirectory',
  host: process.env.HOST ?? 'localhost',
  port: Number.parseInt(process.env.PORT ?? '9000', 10),
  metaPort: Number.parseInt(process.env.META_PORT ?? '9001', 10),
  workerOptions: {
    maxWorkerCount: 4,
  },
});

Routing

Fluxion registers files under dir based on glob patterns:

  • Files matching apiInclude (default: *.ts) are API handlers.
  • Other files are static resources.
  • Request paths match file paths relative to dir.
  • File extensions are part of the route path.

Examples:

File Route Type
dynamicDirectory/test.ts /test.ts API handler
dynamicDirectory/user/profile.ts /user/profile.ts API handler
dynamicDirectory/index.html /index.html Static file
dynamicDirectory/assets/app.js /assets/app.js Static file

API Handlers

An API handler MUST use defineFluxionModule() to define the module. This provides type safety and ensures proper module structure.

Basic Handler
import { defineFluxionModule } from 'fluxion';

export default defineFluxionModule(async (req, cx) => {
  return { ok: true };
});
Handler Arguments

Handlers receive 4 parameters:

handler(req, cx, rawReq, rawRes)
  • req: Normalized request object

    {
      method: string;           // HTTP method
      ip: string;               // Client IP
      url: URL;                 // Parsed URL
      query: Record<string, string | string[]>;  // Query params
      body: Record<string, any>; // Parsed body
      headers: IncomingHttpHeaders;
      cookie: Record<string, string>;
      meta: Record<any, any>;   // Custom metadata
    }
  • cx: Module context

    {
      logger: FluxionLogger;    // Logger instance
    }
  • rawReq: Node.js http.IncomingMessage

  • rawRes: Node.js http.ServerResponse

Advanced Module Configuration
import { defineFluxionModule, defineFluxionMiddleware } from 'fluxion';

const logMiddleware = defineFluxionMiddleware(async (req, cx) => {
  cx.logger.info('Request received', { path: req.url.pathname });
});

export default defineFluxionModule({
  handler: async (req, cx) => {
    return { message: 'hello' };
  },
  middlewares: [logMiddleware],
  methods: ['GET', 'POST'],
  handlerTimeoutMs: 10000,
});
Module Options
interface FluxionModule {
  handler: FluxionHandler;          // Required: main handler function
  middlewares?: FluxionMiddleware[];  // Optional: middleware array
  methods?: HTTPMethod[];           // Optional: allowed HTTP methods
  handlerTimeoutMs?: number;         // Optional: handler timeout (ms)
  disposer?: FluxionDispose;        // Optional: cleanup function
}

Middleware

Middleware functions execute sequentially before the handler. They can modify request parameters through side effects.

import { defineFluxionMiddleware, defineFluxionModule } from 'fluxion';

const authMiddleware = defineFluxionMiddleware(async (req, cx, rawReq, rawRes) => {
  const token = req.headers.authorization;
  if (!token) {
    rawRes.statusCode = 401;
    rawRes.end('Unauthorized');
    return;
  }
  // Modify request for next middleware/handler
  req.meta.user = await verifyToken(token);
});

export default defineFluxionModule({
  handler: async (req) => {
    return { user: req.meta.user };
  },
  middlewares: [authMiddleware],
});

Important: Middleware timeout defaults to 3000ms. Configure via middlewareTimeoutMs option.

HTTP Exceptions

Fluxion provides built-in HTTP exception classes for better error handling:

import {
  defineFluxionModule,
  BadRequestException,
  UnauthorizedException,
  NotFoundException,
} from 'fluxion';

export default defineFluxionModule(async (req) => {
  if (!req.query.id) {
    throw new BadRequestException('Missing required parameter: id');
  }

  const user = await getUser(req.query.id);
  if (!user) {
    throw new NotFoundException('User not found');
  }

  return { user };
});

Available exception classes:

  • BadRequestException (400)
  • UnauthorizedException (401)
  • ForbiddenException (403)
  • NotFoundException (404)
  • MethodNotAllowedException (405)
  • RequestTimeoutException (408)
  • ConflictException (409)
  • UnsupportedMediaTypeException (415)
  • UnprocessableEntityException (422)
  • TooManyRequestsException (429)
  • InternalServerErrorException (500)
  • NotImplementedException (501)
  • BadGatewayException (502)
  • ServiceUnavailableException (503)
  • GatewayTimeoutException (504)

Request Body

Fluxion parses request bodies before calling handlers (except for GET and HEAD).

Supported parsing:

  • JSON: Objects assigned directly; primitives become { value }; invalid JSON becomes { raw }
  • Form data: Parsed into key/value fields
  • Text: Stored as { raw }
  • Binary: Read for size checking; body remains {}

Requests larger than maxRequestBytes return 413 Payload Too Large.

Response Behavior

If the handler returns a value, Fluxion responds with JSON:

export default defineFluxionModule(async () => {
  return { ok: true };
});

Response:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"ok":true}

You can also write to rawRes manually:

export default defineFluxionModule(async (_req, _cx, _rawReq, res) => {
  res.statusCode = 201;
  res.setHeader('Content-Type', 'text/plain; charset=utf-8');
  res.end('created');
});

When res has already ended, Fluxion will not send another JSON response.

Static Files

Non-API files are served as static resources.

Supported methods:

  • GET
  • HEAD

Other methods return 405 Method Not Allowed.

Known content types: .html, .css, .js, .json, .png, .jpg, .jpeg, .svg, .txt, .webp, .ico, .map. Unknown extensions use application/octet-stream.

File Watching

Workers watch the dynamic directory recursively.

On file changes:

  • Existing files are re-registered
  • Deleted files are removed from the router
  • Updates are debounced by reloadDelay (default: 500ms)

Cluster Runtime

Fluxion uses Node.js cluster:

  • Primary process: Starts meta APIs and manages worker state
  • Worker processes: Watch the dynamic directory and serve business traffic
  • Worker count: Controlled by workerOptions.maxWorkerCount (default: 4, capped by CPU count)

Meta APIs

Meta APIs are served by the primary process on metaPort (default: port + 1).

Available endpoints:

GET /_fluxion/healthz                 # Health check
GET /_fluxion/workers                 # Worker status
GET /_fluxion/routes?secret=<secret>  # Router snapshot, enabled when metaSecret is >= 20 chars with letters and digits, no whitespace

Example:

curl http://127.0.0.1:3001/_fluxion/healthz
curl http://127.0.0.1:3001/_fluxion/workers
curl 'http://127.0.0.1:3001/_fluxion/routes?secret=your-20-char-secret1'

Options

interface FluxionOptions {
  dir: string;                    // Required: dynamic directory
  host: string;                   // Required: server host
  port: number;                   // Required: business server port

  // Optional timeout configurations
  handlerTimeoutMs?: number;       // Default: 5000ms
  middlewareTimeoutMs?: number;   // Default: 3000ms
  staticResourceTimeoutMs?: number; // Default: 10min

  // File watching
  reloadDelay?: number;            // Default: 500ms
  nativeWatcher?: boolean;        // Use fs.watch instead of chokidar

  // File registration patterns
  include?: string[];             // Files to register (default: all)
  apiInclude?: string[];          // Files as API handlers (default: ['*.ts'])
  exclude?: string[];              // Files to exclude

  // Meta API
  metaPort?: number;               // Default: port + 1
  metaSecret?: string;             // Enables routes meta API when >= 20 chars, letters+digits, no whitespace

  // Worker management
  workerOptions?: {
    maxWorkerCount?: number;      // Default: 4
    restartWhen?: {
      memoryUsageGreaterThan?: number;  // MB
      healthzTimeout?: number;         // Default: 30000ms
      uptimeGreaterThan?: number;      // ms
    };
  };

  // Request handling
  maxRequestBytes?: number;        // Default: 8_000_000

  // Logging
  logger?: 'one-line' | 'json-line' | FluxionLoggerFn;

  // Module system
  moduleDir?: string;              // Default: process.cwd()

  // HTTPS
  https?: {
    key: string | Buffer;
    cert: string | Buffer;
    ca?: string | Buffer | Array<string | Buffer>;
  };
}
Timeout Configurations
fluxion({
  // ...other options
  handlerTimeoutMs: 10000,         // Handler execution timeout
  middlewareTimeoutMs: 5000,       // Middleware execution timeout
  staticResourceTimeoutMs: 600000, // Static file serving timeout
});
File Registration Patterns
fluxion({
  apiInclude: ['*.ts', '*.api.js'],  // Register as API handlers
  include: ['*.ts', '*.js', '*.html'], // Register any matching file
  exclude: ['*.test.ts', '*.spec.ts'], // Exclude from registration
});
Worker Restart Conditions
fluxion({
  workerOptions: {
    maxWorkerCount: 4,
    restartWhen: {
      memoryUsageGreaterThan: 256,  // MB - recycle at 256MB RSS
      healthzTimeout: 30000,        // ms - recycle after 30s no response
      uptimeGreaterThan: 6 * 3600_000, // ms - rotate every 6 hours
    },
  },
});
HTTPS Configuration
fluxion({
  dir: './dynamicDirectory',
  host: '127.0.0.1',
  port: 9443,
  https: {
    key: './certs/private-key.pem',
    cert: './certs/certificate.pem',
    ca: './certs/ca-bundle.crt',  // Optional
  },
});

Relative paths are resolved relative to moduleDir. PEM content can be passed directly as strings.

Recent Updates

v0.11.x

Middleware & Module System

  • Added defineFluxionModule() and defineFluxionMiddleware() for type safety
  • Middleware execution with timeout support via middlewareTimeoutMs
  • Module context includes logger support
  • Enhanced module type validation
  • Added meta field for custom metadata

Logging

  • Unified logging interface: merged event and message into single message field
  • Simplified logger API across all methods

Handler Parameters

  • Handler signature: (req, cx, rawReq, rawRes) - 4 parameters for better ergonomics
  • Module context (cx) provides logger access
v0.10.x

HTTP Exception Handling

  • Refactored HTTP exception classes with proper error codes
  • Expanded HttpCode enum with additional status codes
  • Added comprehensive HTTP exception classes
  • Exported exception classes for user applications

Worker Management

  • Proactive worker recycling (memory, health, uptime)
  • Enhanced worker pool tuning with restartWhen options
v0.9.x
  • Initial middleware support
  • Worker restart conditions for memory management
  • Restructured build and publish flow

Build and Test

pnpm build
pnpm test
pnpm lint

Keywords