Skip to content

Latest commit

 

History

History
209 lines (162 loc) · 53.5 KB

File metadata and controls

209 lines (162 loc) · 53.5 KB

package surface

English 한국어

@fluojs/openapi exposes OpenApiModule.forRoot(...) / forRootAsync(...) for live documentation and OpenApiDocumentBuilder.build(...) for offline documents; legacy builder functions and registries are not public. Legacy nullable and boolean exclusive bounds are rejected in favor of OpenAPI 3.1 null unions and finite numeric bounds. See OpenAPI 3 Migration Guide.

Cloudflare Worker close ownership

@fluojs/platform-cloudflare-workers exposes no host-invoked shutdown callback through its exported fetch handler. CloudflareWorkerApplicationHost owns lazy bootstrap sharing, first-environment configuration reuse, retry, generation replacement, and drain recovery; direct applications instead use the adapter Factory and no-socket app.listen(). A trigger outside worker.fetch may call await worker.close() directly. A management route inside that fetch must return its current response, then use executionContext.waitUntil(worker.close()) or an equivalent non-self-awaiting mechanism; awaiting it waits for its own active request to drain and reaches the shutdown timeout. A successful host close is restartable: the next fetch(...) creates a fresh application, reruns bootstrap lifecycle hooks, and reconstructs application singleton providers.

public package families

family description packages
Core Shared contracts and DI. @fluojs/core, @fluojs/di, @fluojs/config, @fluojs/i18n, @fluojs/runtime
HTTP Web API execution, routing, and structured access logging. @fluojs/http, @fluojs/graphql, @fluojs/validation, @fluojs/serialization, @fluojs/openapi
Auth Authentication and authorization. @fluojs/jwt, @fluojs/passport
Platform Runtime adapters. @fluojs/platform-fastify, @fluojs/platform-nodejs, @fluojs/platform-express, @fluojs/platform-nextjs, @fluojs/platform-bun, @fluojs/platform-deno, @fluojs/platform-cloudflare-workers
Realtime WebSocket and Socket.IO. @fluojs/websockets, @fluojs/socket.io
Persistence Database and cache. @fluojs/prisma, @fluojs/drizzle, @fluojs/mongoose, @fluojs/redis, @fluojs/cache-manager
Patterns Messaging and architecture. @fluojs/microservices, @fluojs/cqrs, @fluojs/event-bus, @fluojs/cron, @fluojs/queue, @fluojs/notifications, @fluojs/email, @fluojs/slack, @fluojs/discord
Operations Health and monitoring. @fluojs/metrics, @fluojs/terminus, @fluojs/throttler
UI React integration. @fluojs/react
Tooling CLI inspection export, CLI-launched Studio sidecar/viewer diagnostics, inspect artifact rendering, testing diagnostics, and Vite build integration. @fluojs/cli, @fluojs/studio, @fluojs/testing, @fluojs/vite

GraphQL async module registration

@fluojs/graphql exposes GraphqlModule.forRootAsync({ inject, useFactory }) alongside GraphqlModule.forRoot(...). GraphqlAsyncModuleOptions<TTokens> ties each explicit inject token to its ordered useFactory parameter and models an optional token's value as T | undefined. This is not NestJS dynamic-module compatibility: imports, useClass, useExisting, and implicit provider discovery are rejected.

canonical runtime package matrix

runtime target adapter package notes
Node.js (Default) @fluojs/platform-fastify Recommended starter path for high performance on Node.js >=24.0.0 <27. The package declares that exact engines.node range, owns the Fastify-backed Node http/https listener, and accepts Node.js https server options through FastifyHttpApplicationAdapter.create(options) when the process terminates TLS directly. This bounded range excludes Node versions below 24 and Node 27+ so listener-level RFC QUERY requests reach Fastify wildcard fallback and fluo dispatch.
Node.js (Bare) @fluojs/platform-nodejs Use when you need direct control over the Node HTTP listener. The package targets Node.js >=24.0.0 <27 and declares that exact engines.node range so listener-level RFC QUERY requests reach fluo dispatch.
Node.js (Express) @fluojs/platform-express Express host adapter for Node.js >=24.0.0 <27, with the same exact engines.node range. Use it when existing Express hosting or server integrations must remain at the platform boundary. The adapter constructs and owns its Express application; adopting or reusing an existing Express application is unsupported. Replacing the host does not preserve NestJS legacy decorator or reflection-metadata semantics; complete the TC39 standard decorator and explicit DI/module wiring migration first. Application-level middleware still uses fluo's Middleware contract; native Express/Connect (req, res, next) functions are not portable fluo middleware and must be supplied through the adapter's explicit pre-router nativeMiddleware option at construction time when a migration must retain them. After bootstrap, calling use(...) to append to the native stack is not supported. Prefer rewriting portable behavior as fluo Middleware.
Next.js (Node.js) @fluojs/platform-nextjs App Router Route Handlers and Pages Router API Routes on Next.js 16.x (peer >=16.0.0 <17), Node.js >=24.0.0 <27, and @fluojs/runtime >=3.0.0 <4. Request-lazy Fluo bootstrap binds the dispatcher without opening a socket; Next.js owns the server and process lifecycle. No Edge Runtime or raw WebSocket upgrade seam; HTTP methods remain bounded by Next.js routing. Packaged decorator integration is Turbopack-only. Hybrid App/Pages server bundles use separate Fluo applications with the default per-closure lazy recipe. Consumers explicitly using defineNextApplication with the same key share one application Promise only within the same JS global (globalThis). Different processes, workers, serverless instances, and JS globals remain isolated.
Bun @fluojs/platform-bun Official Bun-native fetch-style startup path with native-route registration that preserves observer-compatible dispatcher fallback.
Deno @fluojs/platform-deno Official managed Deno.serve() startup path plus createDenoFetchHandler(...) for hosts that bootstrap fluo but own server startup, shutdown, signals, and websocket upgrades.
Cloudflare Workers @fluojs/platform-cloudflare-workers Stateless isolate lifecycle built on the fetch-style adapter seam. CloudflareWorkerHttpApplicationAdapter.create(...) plus FluoFactory.create(...) is the direct path; CloudflareWorkerApplicationHost.create(...) owns lazy lifecycle. listen() binds the dispatcher without opening a socket, Worker fetch() registers active work with executionContext.waitUntil(...), SSE (text/event-stream) drains follow the response body until completion or cancellation, WebSocket upgrades use a binding frozen before the first listen boundary, and shutdown returns JSON 503 for new HTTP/WebSocket ingress while active work drains for the bounded close window. If a lazy host close() times out, it keeps the shutdown gate until the underlying drain settles, then clears the temporary gate so a later request can bootstrap a fresh application.

portable multipart capability matrix

The normal runtime dispatch pipeline retains buffered multipart materialization so its existing FrameworkRequest.body and files contract stays stable. Applications select streaming explicitly with multipart: { strategy: 'stream' }; supported adapters then expose RequestContext.request.body as an AsyncIterable<MultipartPart> without pre-reading or buffering the iterator. File bytes are pulled only while its file stream is consumed, and one request body cannot be consumed through both modes.

adapter family portable input to parseMultipartStream(...) buffered mode streaming mode
Node.js, Express, Fastify Native async-iterable request directly or as MultipartRequestLike Supported by the runtime request pipeline. Supported by the shared parser with typed parts and Web streams.
Bun, Deno, Cloudflare Workers Native Fetch Request Supported by the runtime request pipeline. Supported by the shared parser with typed parts and Web streams.

Both rows enforce the same maxFieldSize, maxFileSize, maxTotalSize, maxFields, maxFiles, and maxHeaderSize limits. Request abort, parser failure, and file-stream cancellation cancel the active source; MultipartBodyConsumedError rejects buffered/streaming double consumption.

package responsibilities

core

  • @fluojs/core: Metadata helpers and TC39-standard decorator support, including the @fluojs/core/request-pipeline package-integration seam for first-party request-pipeline DTO validation, binding, and standard metadata-bag access.
  • @fluojs/di: Provider resolution, lifecycle scopes, and dependency graph analysis.
  • @fluojs/config: Portable in-memory configuration merging, validation, cloning, and typed access with no package-wide Node engine. ConfigService and explicit in-memory ConfigModule.load({ envFilePaths: [], ... }) inputs never resolve process.cwd(), default .env, or Node builtins. One ConfigModule.forRoot(...) registration exports both ConfigService and CONFIG_RELOADER; use ConfigReloadManager.create(...) for standalone reloads. Ordered envFilePaths apply from lowest to highest precedence; omission loads .env only with cwd or watch, or without explicit defaults/processEnv/runtimeOverrides; [] disables file loading. Env-file, default .env, and watch paths are Node-only features guarded at execution by CONFIG_RUNTIME_UNAVAILABLE; they lazily require process.getBuiltinModule(...) and provide remediation guidance when unavailable.
  • @fluojs/i18n: Framework-agnostic internationalization package boundary whose root import depends only on @fluojs/core and does not declare a Node.js engine floor, with I18nModule.forRoot(...) module registration that exposes I18nService globally by default and supports global: false for module-local visibility, a standalone service factory, reserved core option/error types, ICU MessageFormat support through @fluojs/i18n/icu, HTTP locale helpers and opt-in Accept-Language policy helpers through @fluojs/i18n/http, opt-in non-HTTP locale adapters and header policy helpers through @fluojs/i18n/adapters, validation localization through @fluojs/i18n/validation, Node filesystem and provider-backed catalog loaders with opt-in remote cache wrappers through @fluojs/i18n/loaders/fs and @fluojs/i18n/loaders/remote, and catalog key plus typed translation helper declaration generation through @fluojs/i18n/typegen. Ecosystem parity with NestJS i18n, i18next, next-intl, and request/validation convenience glue is governed by the i18n ecosystem bridge decision record and remains documentation-first unless a future opt-in subpath satisfies the bridge acceptance criteria.
  • @fluojs/runtime: Portable application bootstrap, module orchestration, platform shell registration, and platform snapshot production with no package-wide Node engine or eager Node builtin imports. Node listener, filesystem, logger, compression, and process-signal responsibilities moved from the removed @fluojs/runtime/node and @fluojs/runtime/internal-node entrypoints to @fluojs/platform-nodejs and @fluojs/platform-nodejs/internal; fetch-style hosts continue through @fluojs/runtime/web. Its HTTP request pipeline writes framework-managed handler results only while the response remains uncommitted. After a handler or response helper commits RequestContext.response, the dispatcher skips a second success-response write instead of writing the final interceptor-chain result. HealthModule.forRoot(...) returns the public RuntimeHealthModule readiness-registration seam for first-party runtime-aware packages that must contribute /ready checks without importing internal runtime subpaths. Published @fluojs/runtime/internal* subpaths are runtime-neutral package-integration seams for first-party adapters and runtime-aware packages; they are not application-level helper contracts.

adapters

  • platform-*: Implement the repository policy seam named PlatformAdapter; HTTP runtime packages do so through HttpApplicationAdapter from @fluojs/http. They bridge abstract HTTP calls to runtime-specific listeners.
  • @fluojs/socket.io: A dedicated transport-brand adapter that integrates Socket.IO v4 with fluo gateways while preserving package-level runtime limits: Node.js >=24.0.0 <27 server-backed adapters and the official Bun engine path are supported, Deno and Workers are not supported, Bun requires static CORS shapes, every runtime rejects @WebSocketGateway({ serverBacked }), adapter-owned/shared HTTP listeners remain owned by the platform adapter during Socket.IO shutdown, accepted gateway work drains before managed state is cleared within the shutdown bound, and explicit numeric payload/buffer/shutdown options fail fast when invalid.

features

  • @fluojs/http: Routing, guards, interceptors, exception handling, immutable structured access-log lifecycle records, the portable FrameworkRequestConnection transport seam and immutable trusted connection API (resolveHttpConnection, HttpConnection, ResolveHttpConnectionOptions, TrustProxyPolicy, and TrustProxyPredicate), and the portable byte-range public API (createByteRangeResponse(...), ByteRangeResponseSource, and ByteRangeResponseOptions). Access-log clientIdentity explicitly opts into the direct transport peer; forwarded identity requires an explicit trustProxy policy. It owns single-byte-range request policy, including the GET/HEAD gate and identity partial-response metadata, but applications retain byte sources, exact representation sizes, filesystem open/stat/seek/close ownership, and multi-range construction.

  • @fluojs/graphql: GraphQL schema exposure, root and code-first object field resolver execution, and subscriptions on top of the HTTP abstraction, with a package-owned engines.node >=24.0.0 <27 support contract; portable @fluojs/runtime has no package-wide Node engine.

  • @fluojs/openapi: Decorator-based OpenAPI 3.1.0 document generation for HTTP controllers, registered through OpenApiModule.forRoot(...) or forRootAsync(...) with explicit sources, prebuilt descriptors, or both. It owns configurable documentPath/uiPath routes with preserved /openapi.json and /docs defaults, deterministic HTTP route-collision failures for multi-document registrations, deterministic Swagger UI serving, descriptor/source collision precedence for duplicate operations, request DTO schema extraction, explicit ApiParam / ApiQuery / ApiHeader / ApiCookie / ApiBody override behavior, rejection of legacy nullable and boolean exclusive bounds in favor of OpenAPI 3.1 null unions and finite numeric bounds, default error response policy injection or omission, and documentTransform hooks before served documents are snapshotted and normalized. The package declares its own engines.node >=24.0.0 <27 support contract, excluding Node versions below 24 and Node 27+; portable @fluojs/runtime has no package-wide Node engine.

  • @fluojs/react: Runtime-neutral React integration whose stable root exposes HTTP-owned React routing facades, ReactServerEntry, createReactServerEntry(...), and renderReactResponse(...). Entries stream through lazy react-dom/server renderToReadableStream(...) and accept explicit bootstrapScripts, bootstrapModules, trusted bootstrapScriptContent, and assetMap hydration metadata. @fluojs/react/vite parses already-loaded Vite manifests, and @fluojs/react/client provides real-anchor, full-document navigation plus hydration-safe route state without taking over matching or DTO validation. The explicitly unstable @fluojs/react/experimental/rsc subpath pins React, React DOM, and the application-selected Flight renderer to 19.2.6; validates Web Streams and build-adapter capabilities; snapshots client-reference and server-to-client module maps with createReactRscManifest(...); returns application-encoded Flight payloads with createReactFlightResponse(...); and prototypes signed Server Function references plus bounded JSON transport with createReactServerFunctionRegistry(...) and createReactServerFunctionClient(...). Flight payloads and action calls both use ordinary fluo HTTP dispatch. The root and stable client subpath do not re-export RSC or Server Function code. No React subpath provides automatic Vite manifest discovery, arbitrary inline serialization, a built-in RSC renderer or build plugin, automatic "use server" transforms/export discovery, client bundle generation, file routes, SPA document swapping, client data caches, prefetch, Node pipeable root APIs, or a separate React matcher/router table.

    Stable phase boundaries keep root @fluojs/react focused on runtime-neutral SSR contracts. @fluojs/react/vite owns Vite asset manifest parsing, @fluojs/react/client owns HTTP-first browser navigation and hydration-safe route-state hooks, and @fluojs/react/experimental/rsc owns the unstable exact-React-version RSC compatibility, client-reference/server-module manifest, HTTP-dispatched Flight response, and signed Server Function transport seams. Server Functions require an application-owned explicit POST route, Web Crypto provider, 32-byte-or-longer secret, exact origin allowlist, non-simple request marker, JSON/body/depth/result limits, and action-level or guard authorization; they do not create a renderer, build plugin, loader/cache system, or separate router. The stable root and client subpath should not be presented as a Next.js App Router clone, React Server Components framework, TanStack route tree, Angular Routes[], file-route scanner, or primary React-owned routes: [] table.

    The stable root also exports createReactPageCatalog(...) and ReactPageCatalogEntry. This read-only catalog filters authoritative compiled HTTP descriptors by React router/path markers and projects effective method, path, version, parameter names, module, router, and handler identity. It is observational only: matching, conflict detection, dispatch, and non-React route behavior remain HTTP-owned. Runtime/CLI inspection serializes the same route-kind marker as react-page without retaining request values or creating a React route table.

    The stable @fluojs/react/typegen tooling subpath generates deterministic, path-only TypeScript declarations, absolute href builders, route-bound real-anchor props, and typed push/replace methods from that catalog without widening the runtime-neutral package root. Generated navigation resolves ordinary absolute href strings into the existing HTTP-first client APIs instead of adding a runtime route table or matcher. It rejects versioned routes because the catalog cannot distinguish URI versioning from header, media-type, or custom version strategies.

    Stable root render policies are limited to @PageLayout(...) and @SuspenseFallback(...) component references on @Router(...) classes and @Path(...) methods. The application ReactPageRenderer alone receives resolved base-to-derived class/method policies after HTTP matching, with the active request-scope container available through ReactRenderContext. Same-site duplicates, invalid targets/references, and policies without renderPage fail during bootstrap. Suspense fallback metadata covers only descendants that suspend during SSR; it does not add handler-await, client-navigation pending UI, error/not-found ownership, or URL-prefix ancestry. The render policy decision is canonical.

    The React RSC graduation policy keeps @fluojs/react/rsc blocked until maintainer-approved evidence covers React/renderer versions, manifest and Server Function transport compatibility, browser/server separation, SSR/CSR/prerendering, hydration mismatch recovery, safe transfer rules, HTTP route and #2506 navigation ownership, dual-import tests, bilingual docs, and Changesets intent. Graduation never adds a stable root RSC export. When approved, the experimental path remains a tested re-export for the policy's deprecation window.

  • @fluojs/jwt: HTTP-agnostic JWT signing, verification, principal normalization, refresh-token rotation contracts with optional family-scoped reuse revocation and a compatible subject-revocation fallback, and platform status/diagnostic helpers with exported status snapshot and adapter input types.

  • @fluojs/passport: Strategy-agnostic authentication guards, optional auth and scope decorators, PassportModule strategy registry wiring, Passport.js manual bridge provider bundles, bearer JWT, cookie-auth and refresh-token presets, account-linking policy helpers, public auth metadata helpers, and platform status/diagnostic helpers for auth readiness.

  • @fluojs/microservices: Pattern-matching transport abstraction for TCP, Redis Pub/Sub, Redis Streams, NATS, Kafka, RabbitMQ, MQTT, and gRPC. Application registration stays on MicroservicesModule.forRoot(...), and MICROSERVICE resolves the programmatic Microservice lifecycle facade rather than a raw transport. send() settles on a correlated response, emit() settles at outbound transport publication rather than remote-handler completion, and close() tears down transport-owned listeners/subscriptions without closing caller-owned NATS, Kafka, or RabbitMQ collaborators. Status snapshots preserve mixed ownership: supplied gRPC servers remain externally managed while cached outbound clients remain framework-owned and are reported separately through details.transportResourceOwnership. Transport-specific subpaths expose TCP, Redis Pub/Sub, Redis Streams, NATS, Kafka, RabbitMQ, MQTT, and gRPC adapters; import each class and its options only from its subpath and use the class create(...) factory as the canonical construction path. The root barrel exports registration, lifecycle, decorators, status, and shared types only. Safety defaults keep incomplete newline-delimited TCP frames byte-based until a complete frame can be decoded once as UTF-8, bound each frame to 1 MiB, route port: 0 outbound send()/emit() through the OS-assigned listener, and establish a terminal facade ingress gate when close() starts so new send(), emit(), serverStream(), clientStream(), and bidiStream() calls reject before transport handoff even while listen() is pending; the runtime shell applies the same terminal gate to send() and emit(). Transport adapters retain their own shutdown guards. NATS close attempts every owned subscription cleanup, aggregates multiple failures, and retains only failed subscriptions for a later close retry before listen can resume. The NATS request callback boundary contains malformed frames plus response encoding or respond() failures, reports them through the configured transport logger without closing the caller-owned NATS client, and adds no raw console fallback when a logger is absent. Concurrent or repeated TCP close() calls share the first shutdown promise so listener and socket teardown runs once. gRPC removes each AbortSignal abort listener after a unary call settles, when a streaming call ends or errors even before reader iteration starts, or when its reader returns early; overlapping terminal paths clean up only once.

  • @fluojs/cqrs: CQRS command/query buses with bootstrap-time singleton handler discovery, explicit command/query/event handler and saga decorators, in-process saga orchestration with opaque runtime-agnostic CqrsDispatchContext topology guardrails and one active execution owner per singleton saga provider token, and delegated domain event publishing through @fluojs/event-bus after CQRS handlers and sagas settle. Nested publication to a different route owned by the active saga provider token becomes a deadlock-safe serialized continuation; an active provider-token/event-route cycle still fails with SagaTopologyError. The package declares Node.js >=24.0.0 <27 as its package-owned support contract, excluding Node.js versions below 24 and Node.js 27+. Application registration goes through CqrsModule.forRoot(...); low-level provider assembly is not a documented public facade. Its public status snapshot helpers report command/query/event-handler/saga discovery and lifecycle details, current saga execution and shutdown-drain diagnostics, and the fixed delegated dependency; readiness and health continue to be defined by the event/saga runtime. See the CQRS architecture contract for the complete field and lifecycle meanings.

  • @fluojs/event-bus: In-process domain event fan-out registered through EventBusModule.forRoot({ global?, publish?, shutdown?, transport? }), with global provider visibility enabled by default, normalized effective singleton handler discovery, inherited event channel fan-out, bounded publish cancellation/timeouts, and shutdown drain semantics that recheck local publishes and inbound transport callbacks to live-set quiescence under one absolute deadline before transport close. It requires Node.js >=24.0.0 <27 under its package-owned support contract; handler and transport failures are logged and isolated from the caller-facing publish() promise. The only public publication path is EventBusService.publish(event, options?): Promise<EventPublishResult>. It returns delivery outcomes without exposing a second result-aware facade or legacy bus token. The root provides type-only exports of EventDeliveryTarget, EventDeliveryStatus, EventDeliveryOutcome, EventPublishSettlement, and EventPublishResult. The narrow @fluojs/event-bus/integration subpath exposes only the shutdown coordinator seam for first-party integration; it is not an application-level publication API. The event-bus README owns the full contract.

    settled does not mean every reaction succeeded. Callers check all outcomes and a nonempty result; with neither matching local handlers nor a configured transport, the result is no-recipients. Lifecycle refusal returns rejected with a stopping/stopped/failed reason, while discovery/preparation errors still reject. Results list effective local handlers in discovery order, then outbound transport in channel order, observing succeeded, failed, timed-out, or cancelled. They report neither remote handlers/subscribers nor durability; an adapter success with zero subscribers is still transport success.

    Results and outbound-publication bus logs contain no raw handler/transport errors or handler return values, retaining only existing safe target/status messages. Inbound handler processing and transport subscription/close diagnostics retain their raw errors; app-owned logs remain the application's responsibility. Awaited timeout/cancellation does not terminate started, shutdown-tracked work. background.completion: Promise<EventPublishSettlement> ignores timeout and post-start cancellation to await actual work.

  • @fluojs/event-bus/redis: Optional Redis Pub/Sub transport subpath for cross-process event-bus fan-out. It exports RedisEventBusTransport and RedisEventBusTransportOptions, requires the application to install the optional ioredis peer, and accepts dedicated, separate caller-owned publishClient and subscribeClient instances. Transport close removes its subscriptions and listener without disconnecting either client, and durable queue/retry semantics remain with @fluojs/queue.

  • @fluojs/cron: Decorator and registry scheduling for cron expressions, fixed intervals, and one-shot timeouts; immutable registry descriptor snapshots; rollback-safe dynamic cron expression and interval cadence updates whose provisional and retired handles stay tick-gated across replacement; stop-failure handle retention for retryable disable/remove operations and subsequent shutdown-hook cleanup; optional Redis distributed locking with named-client selection, lock TTL/owner controls validated before Redis I/O when enabled, per-acquisition lease tokens, release/renewal status accounting, and Redis peer loading only when distributed.enabled is true; bootstrap-aware dynamic task startup; bounded scheduler shutdown that rejects late queued ticks and reuses a shutdown-start shutdown.timeoutMs deadline for post-task Redis release and immediate retry; and health/readiness status snapshots for lifecycle, task, and unresolved lock ownership visibility. Version 3 requires Node.js >=24.0.0 <27 as its package-owned support contract. Consumers upgrading from version 2 must move Node.js versions below 24 and Node.js 27+ hosts to that supported range.

  • @fluojs/notifications: Shared channel contract and orchestration layer for provider-specific notification packages. Application registration stays on NotificationsModule.forRoot(...) or NotificationsModule.forRootAsync({ inject, useFactory, global? }); exported notifications providers are global by default unless global: false opts into module-local visibility. It exposes NotificationsService.dispatch(...) and dispatchMany(...), optional queue-backed delivery seams for single opt-in, threshold-driven batch dispatch, and explicit dispatchMany(..., { queue: true }) queue forcing below bulkThreshold; lifecycle event publication through caller-supplied immutable observation snapshots that cannot alter the separately snapshotted channel resolution, queue jobs, generated identity, or provider delivery; deterministic generated fallback delivery/job keys for current envelope shapes while recommending caller-supplied notification.id for durable cross-release identity; and platform status snapshots/diagnostics through NotificationsService.createPlatformStatusSnapshot() and createNotificationsPlatformStatusSnapshot(...), where operationMode, dependencies, bulkQueueThreshold, queueConfigured, eventPublisherConfigured, and eventPublicationEnabled live under typed details. Publisher configuration remains separately visible from lifecycle enablement, so publishLifecycleEvents: false does not report an active event dependency or external ownership. Concrete queue and event-bus implementations remain outside the foundation package so applications can wire @fluojs/queue, @fluojs/event-bus, or another runtime explicitly. Queue adapter handoffs receive an optional NotificationsQueueContext with the caller's live AbortSignal; the service prevents pre-aborted queue I/O, while adapters own listener cleanup and queue-specific mid-flight cancellation policy.

  • @fluojs/email: Transport-agnostic email delivery core. Application registration stays on EmailModule.forRoot(...) or EmailModule.forRootAsync({ inject, useFactory, global? }); async registration does not support NestJS imports, useClass, or useExisting, and exported email providers are global by default unless global: false opts into module-local visibility. It provides direct EmailService delivery, a first-party notifications channel, unconditional EmailLifecycleError rejection in stopping, stopped, or failed states, and opt-in verifyOnModuleInit startup gating that waits for successful bootstrap verification before transport handoff. Without that option, delivery may proceed while the lifecycle is created or starting. The package also provides transport lifecycle/readiness/ownership status snapshots that surface queue metadata only when callers provide queue details explicitly and optional queue worker integration through @fluojs/email/queue.

  • @fluojs/email/node: Node.js specific subpath for @fluojs/email that provides first-party Nodemailer/SMTP transport.

  • @fluojs/slack: Webhook-first Slack delivery core that can run standalone or register a first-party notifications channel. It documents SlackModule.forRoot(...) / forRootAsync(...) with default global provider visibility and global: false local-visibility opt-out, direct SlackService delivery, SLACK_CHANNEL notifications integration, app-owned composition for multiple Slack clients, verifyOnModuleInit bootstrap verification when a transport exposes optional verify(), shared bootstrap/shutdown ordering that keeps factory-owned transports open until verification settles, serialized factory-owned transport cleanup across bootstrap failure and shutdown, SlackTemplateRenderer notification template rendering, payload-over-rendered merge precedence for Slack content, lifecycle/readiness status snapshots, and a transport-agnostic boundary without direct process.env reads.

  • @fluojs/discord: Webhook-first Discord delivery core that can run standalone or register a first-party notifications channel. It documents DiscordModule.forRoot(...) / forRootAsync({ inject, useFactory, global? }), default global provider visibility with global: false local-visibility opt-out, intentionally private internal provider helpers/tokens, direct DiscordService delivery, DISCORD_CHANNEL notifications integration, lifecycle-gated sends, transport kind and resource ownership diagnostics, optional verifyOnModuleInit bootstrap verification, DiscordTemplateRenderer notification template rendering, DiscordService.createPlatformStatusSnapshot() and createDiscordPlatformStatusSnapshot(...) status snapshots, and a transport-agnostic boundary without direct process.env reads.

  • @fluojs/websockets: Runtime-neutral WebSocket gateway authoring from the root and runtime-specific registration subpaths @fluojs/websockets/node, @fluojs/websockets/bun, @fluojs/websockets/deno, and @fluojs/websockets/cloudflare-workers that expose registration and runtime projections, while the root alone owns shared decorator and metadata authoring primitives. It owns WebSocketRoomService room membership/broadcast semantics as a type-only contract implemented by the runtime lifecycle service and injected via @Inject(...) with NodeWebSocketGatewayLifecycleService from @fluojs/websockets/node, or the matching *WebSocketGatewayLifecycleService token on another runtime subpath, room broadcast backpressure applied only by the Node.js-backed adapter while fetch-style runtimes do not apply a backpressure policy to room broadcasts, normalized text and binary @OnMessage() payload dispatch with positional (payload, socket, request, socketId) inputs, ignored raw handler return values after awaited completion by default with opt-in { event, data? } replies through replies: { mode: 'event-envelope' }, Node IncomingMessage guard typing for the Node subpath, fetch-style Request guard typing for Bun/Deno/Workers subpaths, pre-upgrade rejection through booleans, structured WebSocketUpgradeRejection objects, or thrown HTTP exceptions, token-only NodeWebSocketGatewayLifecycleService resolution from @fluojs/websockets/node for the Node implementation, typed runtime seams such as upgrade context/guard/rejection and gateway descriptors, native host/socket/binding types owned by platform packages, a terminal Node shutdown admission gate rechecked immediately before upgrade acceptance, and retained per-connection lifecycle state that keeps disconnect cleanup queued before or during shutdown inside the bounded drain.

  • @fluojs/validation: Standard-decorator input validation, DTO materialization, and request-boundary safety. PickType, OmitType, PartialType, and IntersectionType are available only from @fluojs/validation/mapped-types; the root has no mapped-helper aliases.

  • @fluojs/serialization: Decorator-aware response serialization and output DTO shaping with Expose, ExposeClassOptions, Exclude, Transform, TransformFunction, serialize(value), and SerializerInterceptor for HTTP response-boundary integration. SerializerInterceptor shapes framework-managed handler values while RequestContext.response is uncommitted. After the response is committed, it bypasses serialization and returns the value it received from next.handle() unchanged; other interceptors may still transform the chain result.

  • @fluojs/prisma: Node.js >=24.0.0 <27 Prisma ORM lifecycle and ALS-backed transaction context using host AsyncLocalStorage; the package manifest declares engines.node >=24.0.0 <27. It exports PrismaModule.forRoot(...) / forRootAsync(...), PrismaService and PrismaServiceFacade<TClient> for current-client access, manual transaction(...), abort-aware requestTransaction(...), platform status snapshots, named client/service/options token helpers, and a @Transaction() decorator for Service-layer transaction boundaries. A default registration aliases the PrismaService class token and getPrismaServiceToken() to the same module-owned facade; named registrations are isolated, resolve through getPrismaServiceToken(name), and do not expose the class token. PrismaTransactionInterceptor is removed; application-owned request boundaries call requestTransaction(...). Shutdown drains active request and service/manual transaction boundaries before disconnecting the registered client.
  • @fluojs/drizzle: Node.js >=24.0.0 <27 Drizzle ORM lifecycle and ALS-backed transaction context using Node's node:async_hooks, under its package-owned support contract; Node versions below 24 and Node 27+ are unsupported. It exports @Transaction(), DrizzleDatabase<TDatabase>, DrizzleDatabaseFacade<TDatabase>, and status snapshot helpers. Application registration stays on DrizzleModule.forRoot(...) / forRootAsync(...); request-wide work uses explicit DrizzleDatabase.requestTransaction(...). Drizzle's Bun SQL, Cloudflare D1, and other non-Node drivers remain outside this wrapper until a non-Node transaction-context adapter is documented; those runtimes should expose raw Drizzle handles through application-owned fluo providers instead of importing the root wrapper.
  • @fluojs/redis: App-scoped Redis client registration, raw-client injection, JSON-aware RedisService facade, named-client tokens, lifecycle-owned connect/quit timeout guardrails, bootstrap rejection of duplicate default or trimmed named registration ownership identities before a client is created, platform health/readiness status snapshot helpers, and documentation guidance that Pub/Sub subscribers use dedicated Redis connections rather than sharing the ordinary command client.
  • @fluojs/cache-manager: Application cache module registration through synchronous CacheModule.forRoot(options) or injected-factory CacheModule.forRootAsync({ inject, useFactory, global? }), memory/Redis/custom store selection, decorator-driven GET response caching, post-write cache eviction, injected CacheService operations, low-level cache metadata helper exports, and platform status/diagnostic helpers for cache readiness. Normalized configuration shapes are private provider-assembly details. The package declares engines.node >=24.0.0 <27 as its package-owned support contract, excluding Node versions below 24 and Node 27+.
  • @fluojs/throttler: Node.js >=24.0.0 <27 decorator-driven request rate limiting with ThrottlerModule.forRoot(options), explicit ThrottlerGuard activation, route/class override decorators, in-memory and Redis/custom store contracts, proxy-aware client identity controls, shared route/client bucket semantics, custom-store retryAfterMs support for authoritative backing-store clocks, and platform status/diagnostic helpers for backing-store readiness, ownership, and local or distributed operation visibility.
  • @fluojs/queue: Redis-backed BullMQ job processing whose package manifest declares engines.node >=24.0.0 <27 as its package-owned support contract. Consumers upgrading from Node.js versions below 24 and Node.js 27+ must move to a supported release. Queue provides decorator-discovered singleton workers, application-supplied ownershipNamespace identities independent of DI clientName, pre-resource (ownershipNamespace, jobName) collision validation, 2.x compatibility diagnostics by default, opt-in ownershipEnforcement: 'reject' bootstrap rejection, queue-owned duplicate Redis connections, JSON-object payload serialization, dead-letter retention, bounded read-only inspectDeadLetters(...) access with newest-first typed results and malformed-record counts, bootstrap-ready worker startup, bounded worker shutdown through workerShutdownTimeoutMs, and lifecycle/readiness status snapshots.
  • @fluojs/mongoose: Node.js >=24.0.0 <27 Mongoose lifecycle integration with application-owned concrete connections and ALS/session-aware transaction boundaries via Node's node:async_hooks, under its package-owned support contract; Node versions below 24 and Node 27+ are unsupported. It exports @Transaction(), explicit requestTransaction(...) request boundaries, a MongooseConnection.model(...) facade that auto-binds sessions for create, find, findOne, aggregate, and bulkWrite, explicit currentSession() access for unsupported model operations, ambient-session delegation through connection.transaction(...) when available, fail-open direct execution when transaction APIs are unavailable unless strictTransactions is enabled, and MongooseConnection.createPlatformStatusSnapshot() shutdown snapshots that report active request/session drain state. It does not export MongooseTransactionInterceptor or createMongooseProviders(...).
  • @fluojs/metrics: Node.js >=24.0.0 <27 Prometheus scrape endpoint registration through MetricsModule.forRoot(...), optional HTTP request collectors, endpoint-scoped endpointMiddleware, distinct module-level middleware, runtime platform telemetry gauges, isolated-by-default or explicitly shared prom-client registry ownership, MetricsService for custom application counters/gauges/histograms plus advanced getRegistry() access, the Registry re-export for shared-registry setups, and the low-level METER_PROVIDER / PrometheusMeterProvider bridge plus meter abstraction types for package integrations; the package manifest declares engines.node >=24.0.0 <27.
  • @fluojs/terminus: Aggregated health/readiness diagnostics with TerminusModule.forRoot(...), indicator/provider registration, exported indicator/provider DI tokens, execution timeouts for slow indicators, service-scoped in-flight indicator serialization, runtime platform health/readiness contributors, Prisma/Drizzle lifecycle-aware provider diagnostics, Node memory/disk indicators through @fluojs/terminus/node, and Redis lifecycle-aware PING diagnostics through @fluojs/terminus/redis without making the root package depend on optional Redis or Prisma peers, or the optional Drizzle peer.

tooling

  • @fluojs/cli: Node.js >=24.0.0 <27 project scaffolding, generation, codemods, and inspection export/delegation for runtime-produced snapshots. This is its package-owned support contract: upgrade Node 20 and Node 22 hosts to Node.js >=24.0.0 <27; Node versions below 24 and Node 27+ are unsupported. fluo inspect owns CLI argument validation, application bootstrap/close, default JSON snapshot serialization, snapshot-plus-timing envelopes for --timing, support report artifact writing for --report, --output <path> file emission, and the handoff to Studio for Mermaid rendering. For fluo dev --studio, the CLI-owned sidecar shares repeated or concurrent StudioSidecar.close() calls, ends its tracked SSE responses, and closes only active authenticated ingestion sockets so a client-held incomplete body cannot keep CLI teardown pending; completed ordinary requests remain outside that ingestion ownership set.
  • @fluojs/studio: Node.js >=24.0.0 <27 CLI sidecar/viewer package for the Node dev-runner fluo dev --studio live MVP plus static snapshot/report/timing compatibility for CI, support, architecture review, and non-Node runtime fallback workflows. Studio owns the responsibility boundary for consuming sidecar live events (snapshot, request, timing, diagnostic, restart, disconnect, and heartbeat), validating live event envelopes through parseStudioLiveEvent(...), validateStudioLiveEvent(...), and isStudioLiveEvent(...), rejecting body-like fields (body, headers, payload, rawBody, requestBody, and responseBody) on live request events, reading fluo inspect --json snapshots, legacy standalone timing diagnostics, --timing and --json --timing snapshot-plus-timing envelopes, --report artifacts through parseStudioPayload(...), filtering with applyFilters(...), Mermaid graph rendering through renderMermaid(snapshot), resolving @fluojs/studio/viewer as a Node package-resolution HTML asset subpath, and exporting root Studio contract types such as StudioLiveEvent, StudioLiveSnapshot, and StudioRequestTrace for tooling.
  • @fluojs/testing: Node.js >=24.0.0 <27 conformance and integration helpers for verifying application and platform contracts, including Test.createTestingModule({ rootModule }), request-level Test.createApp(...), the @fluojs/testing/http request helper subpath, Vitest decorator tooling, request-scoped DI isolation regression helpers, body-bearing RFC QUERY and single-byte-range listener portability assertions, and portability harness cleanup guarantees.
  • @fluojs/vite: Node.js >=24.0.0 <27 Vite-facing build utilities for fluo projects; the package manifest declares engines.node >=24.0.0 <27. It includes the maintained fluoDecoratorsPlugin() used by generated starter vite.config.ts files, requires Vite >=6.2.0, and lazily resolves Babel peers from eligible application .ts transforms rather than package import or plugin creation. The workspace suite runs Vite 8.2.2/Rolldown and executes field-decorator metadata while locking the plugin's pre-transform ordering.

Studio's Node.js >=24.0.0 <27 engine floor remains independently installable because its consumer-side snapshot, diagnostic, and timing declarations are runtime-neutral; @fluojs/runtime is a development-time drift check rather than a published Studio dependency. The runtime-neutral PlatformCheckResult, PlatformReadinessReport, and PlatformHealthReport declarations preserve check outcomes across inspect artifacts, and readiness and health reports may each include optional checks.

Studio inspect artifact ownership

Runtime packages remain the source of inspection snapshots, timing diagnostics, request traces, route descriptors, live diagnostics, and sidecar events. The CLI either streams those values to Studio through the Node dev-runner fluo dev --studio sidecar or turns them into transportable artifacts: raw JSON, standalone timing diagnostics, a snapshot-plus-timing envelope, a report artifact, or Mermaid text when Studio is installed. @fluojs/runtime/devtools is the documented transport-neutral package-integration subpath for a host-owned StudioDevtoolsRuntime; applications pass it through the studioDevtools bootstrap option instead of mutating a process-global. Bun, Deno, and Cloudflare Workers projects use the inspect/static artifact path unless their owner supplies both a bridge and executable host integration evidence. Studio is responsible for reading, validating, filtering, viewing, and rendering both live sidecar state and inspect artifacts for humans and automation callers.

This boundary keeps graph semantics out of @fluojs/cli: the CLI may locate @fluojs/studio and call renderMermaid(snapshot), but Studio defines how internal dependency edges and external dependency nodes become Mermaid output. Consumers should use the versioned report recipe fluo inspect --report --output <path> for newly stored artifacts. Raw snapshots via fluo inspect --json --output <path>, snapshot-plus-timing envelopes via fluo inspect --timing --output <path> or fluo inspect --json --timing --output <path>, and legacy timing readers remain supported for compatibility.

naming conventions

  • platform-*: Reserved for runtime/protocol adapters that implement the documented adapter seam (HttpApplicationAdapter for HTTP transports).
  • *service: Concrete implementation of business logic.
  • *module: Entry point for a package's runtime initialization.

public API naming policy

Module and provider registration APIs use namespace facades rather than public create* factories. Application-facing module entrypoints should be exposed as XModule.forRoot(...), XModule.forRootAsync(...), XModule.register(...), or XModule.forFeature(...), with provider assembly kept internal to the package. For example, application code should import HealthModule.forRoot(...), QueueModule.forRoot(...), or TerminusModule.forRoot(...) instead of calling low-level provider/module factory helpers directly.

Raw Node adapter creation uses one path: NodeHttpApplicationAdapter.create(options) from @fluojs/platform-nodejs. Compression and multipart belong in the same options object. The existing class token and constructor remain; follow the creation migration for removed duplicate factories and type aliases.

create* remains valid for intentionally documented helper and builder APIs that do not register package modules or provider sets by themselves. Testing construction is not a free-factory exception: Test is the only public entrypoint, with Test.createApp(...) and Test.createTestingModule(...); conformance and portability harnesses use XHarness.create(...). Standalone Terminus values use XHealthIndicator.create(...). @fluojs/terminus documents only createPrismaHealthIndicatorProvider(...), createDrizzleHealthIndicatorProvider(...), and createRedisHealthIndicatorProvider(...) as indicator-level DI composition entries for TerminusModule.forRoot({ indicatorProviders }); they are not module registration facades. When a create* symbol is retained for compatibility with a module registration surface, docs and generated code should prefer the namespace facade and describe the helper as compatibility-only; for example, @fluojs/drizzle consumers should use DrizzleModule.forRoot(...) / forRootAsync(...) for application registration and receive the module-owned facade through injection, @fluojs/mongoose consumers should use MongooseModule.forRoot(...) / forRootAsync(...) for application registration because it does not expose createMongooseProviders(...), and @fluojs/microservices consumers should use MicroservicesModule.forRoot(...) with transport classes imported from their dedicated subpaths and created with create(...). @fluojs/passport documents createPassportJsStrategyBridge(...) as an official manual-composition exception because third-party Passport.js strategy instances must be bound through a provider bundle before PassportModule.forRoot(...) can register the matching strategy; cookie authentication uses the complete CookieAuthModule.forRoot(...) recipe, while separately owned cookie writers use CookieManager.create(...).

Refer to glossary-and-mental-model.md for architectural definitions.

Mongoose registration consolidation

@fluojs/mongoose exports neither createMongooseProviders(...) nor MongooseTransactionInterceptor. Register application-owned connections only through MongooseModule.forRoot(...) or MongooseModule.forRootAsync(...), inject MongooseConnection, and keep an explicit request boundary application-owned by forwarding the request signal to requestTransaction(...).

Microservices transport learning paths link back to the package contract source: TCP, RabbitMQ, and gRPC complement packages/microservices/README.md without replacing its facade, shutdown, and transport ownership contracts.

Notifications status contract

@fluojs/notifications reports configured publisher infrastructure independently from enabled lifecycle publication.

Notifications lifecycle built-in representations

Interface Immutable fields
NotificationSnapshotArrayBuffer kind: 'ArrayBuffer', byteLength, bytes
NotificationSnapshotArrayBufferView kind: 'ArrayBufferView', byteOffset, byteLength, bytes, view
NotificationSnapshotDate kind: 'Date', epochMilliseconds: number | null
NotificationSnapshotMap<TKey, TValue> kind: 'Map', entries
NotificationSnapshotRegExp kind: 'RegExp', source, flags, lastIndex
NotificationSnapshotSet<TValue> kind: 'Set', values
NotificationSnapshotUrl kind: 'URL', href
NotificationSnapshotUrlSearchParams kind: 'URLSearchParams', query

Drizzle named-client ownership

@fluojs/drizzle supports one default registration plus non-global named registrations. Consumers import a module that exports the matching package-owned raw database, normalized options, disposal-hook, and lifecycle-handle tokens. Each named registration owns an independent ALS transaction context, shutdown drain, disposal, and status snapshot. Names do not create isolated runtime containers. The default DrizzleDatabase class token remains default-only. Multi-client services inject a named handle token and select it explicitly with @Transaction(...).