@fluojs/runtime
v1.1.1
Published
Application bootstrap and runtime orchestration — module graph compilation, DI wiring, and diagnostics export for Fluo.
Maintainers
Readme
@fluojs/runtime
The assembly layer that compiles a module graph and wires DI and HTTP into a runnable application shell.
Table of Contents
- Installation
- When to Use
- Quick Start
- Common Patterns
- Behavioral Contracts
- Public API Overview
- Related Packages
- Example Sources
Installation
npm install @fluojs/runtimeWhen to Use
Use this package when you need to:
- Bootstrap a fluo application: Convert your modules into a running HTTP server or microservice.
- Orchestrate DI and Lifecycle: Manage module-graph compilation, provider wiring, and application hooks (
onModuleInit,onApplicationBootstrap). - Create Standalone Contexts: Run CLI tasks, scripts, or workers that need DI but not an HTTP server.
- Diagnostic Inspection: Produce machine-readable platform snapshots and diagnostic issues for CLI export while leaving graph viewing and Mermaid presentation to Studio.
Quick Start
Minimal HTTP Application
The fluoFactory is the primary entrypoint for creating applications.
import { Module } from '@fluojs/core';
import { Controller, Get } from '@fluojs/http';
import { fluoFactory } from '@fluojs/runtime';
import { createNodejsAdapter } from '@fluojs/platform-nodejs';
@Controller('/')
class AppController {
@Get()
index() {
return { hello: 'world' };
}
}
@Module({
controllers: [AppController],
})
class AppModule {}
// Create and start the application
const app = await fluoFactory.create(AppModule, {
adapter: createNodejsAdapter({ port: 3000 }),
});
await app.listen();Common Patterns
Application Context (No HTTP)
For background workers or scripts, use createApplicationContext to skip HTTP setup.
import { fluoFactory } from '@fluojs/runtime';
const context = await fluoFactory.createApplicationContext(AppModule);
// Resolve a service directly from the container
const userService = await context.get(UserService);
await userService.doWork();
await context.close();Global Exception Filters
Handle cross-cutting errors by registering filters during bootstrap.
import { fluoFactory, type ExceptionFilterHandler } from '@fluojs/runtime';
class GlobalErrorFilter implements ExceptionFilterHandler {
async catch(error, { response }) {
console.error('Caught error:', error);
response.setStatus(500);
void response.send({ error: 'Internal Server Error' });
return true; // Mark as handled
}
}
const app = await fluoFactory.create(AppModule, {
adapter: createNodejsAdapter({ port: 3000 }),
filters: [new GlobalErrorFilter()],
});Module Composition
fluo uses a strict module graph. Modules must explicitly export providers to make them available to importing modules.
@Module({
providers: [DatabaseService],
exports: [DatabaseService], // Make it available outside
})
class DatabaseModule {}
@Module({
imports: [DatabaseModule],
providers: [UsersService], // UsersService can inject DatabaseService
})
class UsersModule {}Behavioral Contracts
- Request body parsing enforces
maxBodySizewhile bytes are still streaming for both Web-standard and Node-backed requests. - On
@fluojs/runtime/node, Node request body parsing normalizes the primarycontent-typemedia type before JSON and multipart detection, so mixed-case JSON and multipart headers preserve the documented parser behavior. - Node-backed and Web-standard request wrappers snapshot cheap request metadata before body parsing, then materialize
body/rawBodyonce at the dispatch boundary so userland continues to observe synchronous parsed values. - Node-backed cookies/query values and Web-standard headers are snapshotted when the request wrapper is created, then lazily normalized and memoized per request; later upstream object mutations do not change the
FrameworkRequestview. - Node-backed request context IDs prefer
x-request-idand fall back tox-correlation-idwhenx-request-idis absent, so error responses and request-aware integrations keep the upstream correlation identifier. ApplicationContext.get()andApplication.get()memoize only direct root singleton class/factory provider lookups known at bootstrap, while preserving alias, request, transient, post-close, multi-provider, andcontainer.override()resolution semantics.multi: trueprovider tokens are not context-cache memoized: eachget()call delegates to DI so the container can assemble a fresh contribution array while still reusing each contribution according to its own provider scope.- When
duplicateProviderPolicyiswarnorignore, context-cache eligibility and lifecycle hook execution are based on the effective winning provider selected by bootstrap; stale losing providers do not seed cache entries or lifecycle hooks. - If application or context bootstrap fails after runtime resources or lifecycle instances have been created, fluo resets readiness, runs registered runtime cleanup callbacks, invokes shutdown hooks for instances resolved so far with
bootstrap-failed, disposes the container, logs cleanup failures, and rethrows the original bootstrap error. Application.listen()and microservicelisten()are serialized with shutdown: overlapping startup calls share the same in-flight startup, shutdown waits for in-flight startup to settle, and a startup that races with shutdown cannot transition the shell back toreadyafter close begins.- Shutdown signal registration failures are user-observable:
runNodeApplication(...),bootstrapNodeApplication(...), and adapter-owned runtime helpers close the already-started application withbootstrap-failed, log any close failure separately, and reject with the original registration error. - Shutdown signal unregistration failures do not skip application close:
app.close()always continues through adapter shutdown, lifecycle hooks, runtime cleanup callbacks, and container disposal; if close otherwise succeeds it rejects with the unregistration error, and if close also fails it rejects with an aggregate containing both failures. - Connected microservices are owned children of their parent
Application:startAllMicroservices()starts them sequentially and rolls back already-started children withbootstrap-failedif a later child fails, whileApplication.close(signal)closes connected children before parent lifecycle hooks, adapter shutdown, and container disposal. FluoFactory.createMicroservice()preserves the original bootstrap/runtime-resolution error when cleanup fails and logs cleanup failures separately.- Bootstrap resolves independent singleton lifecycle providers concurrently, then runs lifecycle hooks in deterministic provider order.
- Multipart parsing rejects payloads when the cumulative body size exceeds the configured
multipart.maxTotalSize; runtime adapters default that limit tomaxBodySizeunless you override it. createNodeHttpAdapter(...),bootstrapNodeApplication(...), andrunNodeApplication(...)acceptmaxBodySizeonly as a non-negative integer byte count and fail fast during adapter creation/bootstrap when the value is invalid.- Response stream backpressure helpers settle
waitForDrain()ondrain,close, orerrorso streaming writers do not hang on dead connections. - Runtime health modules report
/readyasstartingwith HTTP 503 until bootstrap marks them ready, and they return tostartingas soon as application/context shutdown begins, including failed shutdown attempts. - Runtime health module readiness checks receive the current
RequestContext, allowing public integrations to resolve runtime-exposed status providers without importing internal runtime tokens. - Signal-driven shutdown helpers preserve bounded drain semantics, log timeout/failure conditions, and set
process.exitCodewhen shutdown does not finish cleanly, but they leave final process termination ownership to the surrounding host runtime. - Platform snapshot and diagnostic issue production stay in runtime; graph viewing, filtering presentation, and Mermaid rendering are Studio-owned contracts consumed by CLI and automation callers.
- Platform component snapshots are runtime-owned contract payloads: each component reports
readiness,health, dependency ids, telemetry tags, diagnostic issues, and resource ownership throughownership.ownsResources/ownership.externallyManaged. Runtime preserves those ownership flags in shell snapshots so adapters and package integrations can distinguish resources fluo must stop from externally managed resources the host owns. - Module graph compile-result caching is opt-in through
moduleGraphCache: true; it keys entries by root module identity, runtime providers, validation tokens, core metadata versions, and the compile algorithm version, caches only successful compilations, and returns isolated graph copies so caller mutations cannot poison later bootstraps.
Public API Overview
fluoFactory: Lower-camel-case alias for the runtime bootstrap facade used in the package examples.FluoFactory: Class-based runtime bootstrap facade with explicit static access.Application: ExtendsApplicationContextwithlisten(),dispatch(), andstate.ApplicationContext: Providesget<T>(token),close(), and access tocontainer,modules, and bootstrap diagnostics.LifecycleHooks: Convenience union coveringOnModuleInit,OnApplicationBootstrap,OnModuleDestroy, andOnApplicationShutdown.HealthModule.forRoot(options): Runtime-owned/healthand/readymodule facade whose readiness marker follows bootstrap and shutdown lifecycle transitions.createHealthModule(options): Deprecated compatibility helper for the same runtime health module contract; preferHealthModule.forRoot(...)in application-facing module imports.ReadinessCheck: Function type used by runtime health modules. Checks receive the/readyrequest context and return a boolean or promise.defineModule(cls, metadata): Programmatic module definition helper.bootstrapApplication(options): Lower-level async bootstrap function.bootstrapModule(...): Lower-level module graph bootstrap helper.createBootstrapTimingDiagnostics(...),createRuntimeDiagnosticsGraph(...): Runtime-owned diagnostics snapshot helpers for CLI/support tooling. They produce machine-readable data; Studio owns viewer parsing, graph presentation, and Mermaid rendering.PlatformShell,PlatformComponent,PlatformShellSnapshot,PlatformSnapshot,PlatformDiagnosticIssue, and related platform report types: Public lifecycle diagnostics and resource-ownership contracts used by runtime-aware packages.RuntimePlatformShellpreserves component-provided ownership and emits validation/readiness/health diagnostics without requiring consumers to import internal runtime tokens.createRequestAbortContext(...),trackActiveRequestTransaction(...),untrackActiveRequestTransaction(...): Request abort and active transaction helpers used by runtime-aware integrations.
Platform-Specific Subpaths
Use @fluojs/runtime/node and @fluojs/runtime/web for application-facing runtime helpers. The published internal* subpaths are reserved package-integration seams for first-party adapters and runtime-aware packages; they are documented here so package authors can identify the boundary without treating those seams as application-level helper contracts.
| Subpath | Purpose |
| :--- | :--- |
| @fluojs/runtime/node | Supported Node.js entrypoint for logger factories, Node adapter/bootstrap helpers, and shutdown signal registration. |
| @fluojs/runtime/web | Shared Web-standard request/response utilities for Bun, Deno, and Cloudflare Workers, including createWebRequestResponseFactory, dispatchWebRequest, createWebFrameworkRequest, and parseMultipart. |
| @fluojs/runtime/internal | Token-only internal seam for package integrations. |
| @fluojs/runtime/internal-node | Node-only internal seam for adapter/runtime plumbing; prefer @fluojs/runtime/node in application code. |
| @fluojs/runtime/internal/http-adapter | Internal HTTP adapter seam for platform packages. |
| @fluojs/runtime/internal/request-response-factory | Internal request/response factory seam for platform packages. |
Node-Specific Subpath (@fluojs/runtime/node)
Logger factories and other supported Node-only helpers are not on the universal root entrypoint. Import them from the ./node subpath:
import {
bootstrapNodeApplication,
createConsoleApplicationLogger,
createJsonApplicationLogger,
createNodeHttpAdapter,
runNodeApplication,
} from '@fluojs/runtime/node';const adapter = createNodeHttpAdapter({
port: 3000,
maxBodySize: 1_048_576,
});For the public Node runtime surface, maxBodySize, retryDelayMs, retryLimit, and shutdownTimeoutMs are number-only non-negative integers. Values such as '1mb', fractional retry counts, or negative shutdown timeouts are rejected immediately during adapter creation instead of being coerced later. Node request context IDs prefer x-request-id; when it is absent, x-correlation-id is used as the request ID fallback for runtime error responses and request-aware integrations.
createConsoleApplicationLogger(): Colorized console logger usingprocess.stdout/process.stderr. The default remains the pretty format. Pass{ mode: 'minimal' }for concise[fluo] LEVEL [context] messagelines,{ mode: 'silent' }to suppress runtime logger output,{ level: 'warn' }or another threshold to filter lower-severity messages, and{ color: false }when you need deterministic non-colored output.createJsonApplicationLogger(): Structured JSON logger usingprocess.stdout/process.stderr.createNodeHttpAdapter(): Raw Nodehttp/httpsadapter factory for adapter-first runtime setup. The helper normalizes the primary Node requestcontent-typebefore JSON/multipart detection and acceptsmaxBodySize,retryDelayMs,retryLimit, andshutdownTimeoutMsonly as non-negative integers.bootstrapNodeApplication()/runNodeApplication(): Node-specific bootstrap helpers used by direct Node runtime flows.createNodeShutdownSignalRegistration(),defaultNodeShutdownSignals(),registerShutdownSignals(): Shutdown registration helpers for hosts that need explicit signal wiring.
Runtime app logging is separate from CLI lifecycle reporting. Configure ApplicationLogger when you want to change logs emitted by the application/runtime itself:
import { createConsoleApplicationLogger, createJsonApplicationLogger } from '@fluojs/runtime/node';
const minimalLogger = createConsoleApplicationLogger({ mode: 'minimal', level: 'warn' });
const jsonLogger = createJsonApplicationLogger();Use CLI reporter flags such as fluo dev --verbose when you need raw child-process output from the development command instead.
Lower-level Node compression internals stay behind the @fluojs/runtime/internal-node seam rather than the public @fluojs/runtime/node contract.
Related Packages
- @fluojs/core: Core decorators and metadata system.
- @fluojs/di: Dependency injection container implementation.
- @fluojs/http: HTTP routing, controllers, and dispatcher.
- @fluojs/platform-nodejs: Official Node.js HTTP adapter.
- @fluojs/studio: Viewer, filtering, and rendering helpers for runtime-produced snapshots and diagnostic issues.
Example Sources
- examples/minimal: Smallest possible bootstrap.
- examples/realworld-api: Full application with complex module wiring.
- packages/runtime/src/bootstrap.test.ts: Behavioral tests for bootstrap phases.
