The Component System provides the foundational abstraction for all computational units in AStack. This document covers the Component base class, its lifecycle, execution modes, port system integration, and patterns for creating custom components. For information about how components are orchestrated together, see Pipeline System. For details on the port connection mechanism, see Port System and Zero-Adaptation.
The Component class in AStack serves as the universal base class for all computational units in the framework. Every processing element—whether a text splitter, an AI agent, or a custom transformer—extends this base class and inherits its port system, lifecycle management, and execution capabilities.
Core Responsibilities:
Sources: packages/core/src/component/index.ts1-40
Diagram: Component Class Hierarchy
The Component class extends TransformNode from the @hlang-org/runtime package, which provides the underlying FBP execution model. Each component instance maintains two default ports: an input port named 'in' and an output port named 'out'.
Sources: packages/core/src/component/index.ts1-9
| Member | Type | Visibility | Purpose |
|---|---|---|---|
Port | static typeof Port | public | Static reference to Port class for creating custom ports |
inPort | ReturnType<typeof Port.I> | protected | Default input port with name 'in' |
outPort | ReturnType<typeof Port.O> | protected | Default output port with name 'out' |
constructor(opts) | method | public | Initializes component and attaches default ports |
run(data) | method | public | Standalone execution method, returns transformed data |
_transform($i, $o) | method | protected | Pipeline execution method, handles port-to-port data flow |
Sources: packages/core/src/component/index.ts3-34
Diagram: Component Construction Sequence
The constructor performs the following operations in order:
super(opts) to initialize the TransformNode base classPort.I('in') and assigns to this.inPortPort.O('out') and assigns to this.outPortattach(this) on both ports to bind them to the component instanceSources: packages/core/src/component/index.ts8-17
Components in AStack support two distinct execution modes: standalone mode and pipeline mode. The execution mode determines how data flows through the component and how results are returned.
In standalone mode, a component operates independently without being part of a pipeline. The run() method is the entry point for this mode.
Method Signature:
run(data: unknown): unknown Default Implementation: The base Component class provides a pass-through implementation that returns the input data unchanged. Subclasses override this method to implement custom transformation logic.
Example Usage:
Sources: packages/core/src/component/index.ts19-21 examples/text-splitter/index.ts70
In pipeline mode, a component is registered within a Pipeline and executes as part of a data flow graph. The _transform($i, $o) method is invoked by the Hlang runtime to handle port-to-port communication.
Method Signature:
_transform($i: any, $o: any): void Parameters:
$i: Input port accessor function, used to receive data from connected upstream components$o: Output port accessor function, used to send data to connected downstream componentsData Flow Pattern:
$i('in').receive(callback) to register a handler for incoming data on the 'in' portthis.run(input) to transform the data$o('out').send(output) to emit the result to the 'out' portSources: packages/core/src/component/index.ts23-34
Diagram: Execution Mode Comparison
The _transform() method internally delegates to run() for the actual transformation logic, ensuring that the same business logic is executed regardless of mode. This design pattern allows components to work seamlessly in both contexts.
Sources: packages/core/src/component/index.ts29-33
Components are registered in a pipeline using the addComponent() method, which assigns a unique name to each component instance.
Registration Pattern:
The pipeline stores components in a Map<string, Node> where the key is the component name and the value is the component instance.
Sources: packages/core/src/pipeline/index.ts10 packages/core/src/pipeline/index.ts66-72 examples/text-splitter/index.ts62
When connecting or executing components in a pipeline, ports are referenced using dot notation: componentName.portName.
Port Resolution Flow:
Diagram: Port Resolution Mechanism
The pipeline's from() and to() methods parse the dot notation, retrieve the component from the registry, and access the specified port using the component's I() or O() methods inherited from TransformNode.
Sources: packages/core/src/pipeline/index.ts23-58
When pipeline.run() is invoked with a trigger name, the pipeline automatically:
BaseProducer and BaseConsumer components if they don't existFlow execution engineExecution Example:
This triggers execution starting at the 'text' input port of the 'textSplitter' component.
Sources: packages/core/src/pipeline/index.ts99-171 examples/text-splitter/index.ts66
The most common pattern for creating a custom component:
Component base classrun() method with transformation logicStructure Reference:
class CustomComponent extends Component { constructor(opts) { super(opts); // Initialize component-specific state } run(data: InputType): OutputType { // Implement transformation logic return transformedData; } } The TextSplitter component in the examples demonstrates this pattern by accepting configuration options and implementing text chunking logic in its run() method.
Sources: examples/text-splitter/index.ts52-56
Components can define additional ports beyond the default inPort and outPort by creating and attaching new port instances in the constructor:
Port Creation Pattern:
this.customInputPort = Component.Port.I('customInput'); this.customOutputPort = Component.Port.O('customOutput'); this.customInputPort.attach(this); this.customOutputPort.attach(this); These custom ports are then referenced in pipeline connections using the same dot notation: componentName.customInput or componentName.customOutput.
Sources: packages/core/src/component/index.ts4-5 packages/core/src/component/index.ts12-16
The AStack Component System is built on top of the @hlang-org/runtime package, which provides the underlying FBP execution model. This relationship gives components several capabilities:
| Hlang Feature | Component Benefit |
|---|---|
TransformNode base class | Provides FBP node lifecycle and port management |
Port system | Enables type-safe, backpressure-aware data connections |
Flow execution engine | Orchestrates asynchronous, event-driven component execution |
| Graph-based execution | Supports complex branching, merging, and feedback loops |
The Component class acts as an abstraction layer that:
Sources: packages/core/src/component/index.ts1 packages/core/src/pipeline/index.ts1
Diagram: Component State Lifecycle
A component progresses through distinct lifecycle stages from creation to execution. Components can follow two paths: the pipeline path (Created → Registered → Connected → Running) or the standalone path (Created → StandaloneReady → Running). Both paths converge at the execution stage where transformation logic is applied.
Sources: packages/core/src/component/index.ts8-34 packages/core/src/pipeline/index.ts66-185
Refresh this wiki