Menu

Component System

Relevant source files

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.

Overview

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:

  • Provide standardized input/output port structure
  • Define execution interface for both standalone and pipeline modes
  • Integrate with the underlying Hlang FBP (Flow-Based Programming) runtime
  • Enable composability through consistent port attachment

Sources: packages/core/src/component/index.ts1-40

Component Class Structure

Class Hierarchy and Dependencies

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

Class Properties and Methods

MemberTypeVisibilityPurpose
Portstatic typeof PortpublicStatic reference to Port class for creating custom ports
inPortReturnType<typeof Port.I>protectedDefault input port with name 'in'
outPortReturnType<typeof Port.O>protectedDefault output port with name 'out'
constructor(opts)methodpublicInitializes component and attaches default ports
run(data)methodpublicStandalone execution method, returns transformed data
_transform($i, $o)methodprotectedPipeline execution method, handles port-to-port data flow

Sources: packages/core/src/component/index.ts3-34

Component Initialization

Constructor Lifecycle

Diagram: Component Construction Sequence

The constructor performs the following operations in order:

  1. Invoke parent constructor: Calls super(opts) to initialize the TransformNode base class
  2. Create default input port: Instantiates Port.I('in') and assigns to this.inPort
  3. Create default output port: Instantiates Port.O('out') and assigns to this.outPort
  4. Attach ports: Calls attach(this) on both ports to bind them to the component instance

Sources: packages/core/src/component/index.ts8-17

Execution Modes

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.

Standalone Mode

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

Pipeline Mode

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 components

Data Flow Pattern:

  1. Call $i('in').receive(callback) to register a handler for incoming data on the 'in' port
  2. Inside the callback, invoke this.run(input) to transform the data
  3. Call $o('out').send(output) to emit the result to the 'out' port

Sources: packages/core/src/component/index.ts23-34

Execution Mode Comparison

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

Component-Pipeline Integration

Component Registration

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

Port Resolution in Pipeline

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

Pipeline Execution Trigger

When pipeline.run() is invoked with a trigger name, the pipeline automatically:

  1. Creates hidden BaseProducer and BaseConsumer components if they don't exist
  2. Connects the producer to the specified entry point
  3. Identifies the final output component
  4. Connects the final output to the consumer
  5. Initializes the Hlang Flow execution engine
  6. Injects input parameters to start data flow

Execution 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

Component Implementation Patterns

Basic Component Pattern

The most common pattern for creating a custom component:

  1. Extend the Component base class
  2. Define constructor to accept configuration options
  3. Override the run() method with transformation logic
  4. Optionally define custom input/output ports beyond the defaults

Structure 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

Multi-Port Component Pattern

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

Relationship to Hlang Runtime

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 FeatureComponent Benefit
TransformNode base classProvides FBP node lifecycle and port management
Port systemEnables type-safe, backpressure-aware data connections
Flow execution engineOrchestrates asynchronous, event-driven component execution
Graph-based executionSupports complex branching, merging, and feedback loops

The Component class acts as an abstraction layer that:

  • Simplifies the Hlang API for common use cases (default ports, standard execution pattern)
  • Provides a consistent interface for both standalone and pipeline execution
  • Enables the "everything is a component" philosophy while leveraging Hlang's robust runtime

Sources: packages/core/src/component/index.ts1 packages/core/src/pipeline/index.ts1

Component Lifecycle Summary

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