Framework Knowledge
This guide explains how HeronJS applications are composed, how components execute, and which resources the framework owns. It applies to version 4.0.0.
Use the Knowledge Catalog to find a package or source submodule. Use the Knowledge Graph to inspect package dependencies and submodule ownership. For a runnable application, start with Getting Started.
Core concepts
| Concept | Meaning |
|---|---|
| Framework package | A published @heronjs/* package with a defined responsibility and public entrypoint. |
| Source submodule | A feature directory within a package, such as common/di or core/lifecycle. It is not automatically a public import path. |
| Application module | A class decorated with @Module that declares components and imports other application modules. |
| Provider | An injectable component containing application logic or access to a resource. |
| Container | The application’s dependency-injection container, responsible for provider resolution and owned instance disposal. |
| Registry | Application-local component registrations used during composition and runtime initialization. |
| Protocol extension | An adapter that connects declared handlers to a protocol while participating in application lifecycle management. |
| Execution result | A value, Promise, Observable, or Promise resolving to an Observable, consumed according to the protocol’s execution contract. |
| Worker | A separate execution context with its own module instances and DI container. |
Application composition
Decorators record metadata. A module declaration identifies the providers, controllers, services, and protocol handlers that belong to an application. Bootstrap reads that metadata and builds a registration plan; importing a decorated class alone does not create a global application instance.
Use module imports to compose features. Module traversal uses constructor identity, handles repeated imports and cycles, and respects explicit export declarations. Import framework APIs through their public package entrypoints rather than internal source paths.
| Module field | Registered components |
|---|---|
imports | Other application modules |
providers | Injectable application components |
controllers | HTTP controllers |
services | Initialization, interval, and daemon services |
graphqls | GraphQL schema and resolver classes |
websockets | Native WebSocket handlers |
rpcs | tRPC procedure groups |
mcps | MCP catalog controllers |
acps | ACP agents |
transporters | Messaging components |
Use @Stores and @DataSources to declare cache and database resources. See Dependency Injection, Stores, and Data Sources for configuration and ownership rules.
Application lifecycle
| Stage | Framework behavior | Application responsibility |
|---|---|---|
| Declaration | Decorators record configuration and component metadata. | Declare components and their module relationships. |
| Creation | HeronJS.create({module}) creates application-local state and bootstraps owned resources. | Provide validated configuration and handle initialization failures. |
| Startup | app.listen(...) starts the shared HTTP listener after extension registration. app.start() also supports explicitly selected stdio execution. | Select the transport and configure protocol endpoints before startup. |
| Execution | Adapters resolve handlers, evaluate guards, bind inputs, and consume results. | Validate untrusted input and observe cancellation during long-running work. |
| Shutdown | await app.close() stops owned execution and disposes resources. | Await cleanup in tests and scripts; dispose resources that remain externally owned. |
Each application has its own container, registry, protocol extensions, services, and resources. Repeated or concurrent close calls share completion. Cleanup attempts remaining disposers even when one fails, and aggregated failures remain available to the caller.
Dependency injection and ownership
Use constructors or Token<T> to associate a registration with its value type. String and symbol lookups return unknown and require validation before use. Typed metadata keys enforce the same relationship between stored configuration and its reader.
An instance registered with registerInstance remains owned by its caller. Use registerOwnedInstance or onDispose when the container should release the resource. Register cleanup for factory-created resources explicitly.
Worker containers are isolated from the parent application. Pass cloneable data across the worker boundary; use declared parent-service RPC interfaces when a worker needs access to a parent-owned service. See Thread for job scopes, service lifetimes, and shutdown behavior.
Execution and cancellation
Unary protocol operations consume the first emission of an Observable and then unsubscribe. Streaming APIs consume emissions until completion or cancellation. Finite lifecycle hooks and message-consumer operations wait for completion.
Use Reactive.call to defer invocation until subscription and preserve the receiver. Use Reactive.first for a unary result or Reactive.complete when completion is the relevant outcome. An empty unary result is an error; an emitted undefined remains a value, subject to protocol validation.
Disconnects, protocol cancellation, and application shutdown cancel framework-owned Observable execution. Native Promises require cooperative cancellation in application code. Observe the supplied abort signal and release application-owned work when it is aborted. The Reactive Execution guide defines behavior for each protocol.
Protocol boundaries
Core owns the shared HTTP listener. HTTP, GraphQL, tRPC, WebSocket, MCP, and ACP extensions register their endpoints through the application lifecycle. Authentication, validation, error mapping, and transport semantics remain specific to each protocol.
HTTP is the default transport. MCP and ACP can use stdio when explicitly configured, but only one endpoint can own process stdio. Messaging adapters retain broker-specific acknowledgement and shutdown behavior.
Selecting an execution model
| Workload | Component |
|---|---|
| Request/response logic | HTTP controller, GraphQL resolver, or tRPC procedure |
| Connection-oriented events | WebSocket handler |
| Tool, resource, or prompt catalog | MCP controller |
| Agent sessions and prompts | ACP agent |
| Startup or recurring background work | Service on the main thread |
| CPU-bound jobs or isolated long-running work | Worker pool or dedicated worker service |
| Broker-driven processing | EBS consumer or publisher |
Review the Migration Guide before moving an existing 3.7.11 application to 4.0.0. Both versions remain LTS; their documentation and examples are versioned separately.
This guide is based on the 4.0.0 module and lifecycle implementation .