Skip to content

ChainGraph Decorator Specification Reference

Table of Contents

Introduction

This document provides a comprehensive specification of all decorators available in the ChainGraph framework. It serves as the definitive reference for all decorator properties, types, and configuration options.

ChainGraph decorators are used to:

  • Define nodes and their metadata
  • Configure node properties as ports
  • Specify port data types, validation rules, and UI behavior
  • Create complex object schemas for structured data

How to Use This Reference

Each decorator section includes:

  • Purpose: What the decorator is used for
  • Target: What TypeScript elements it can be applied to
  • Properties: Complete list of supported properties and their types
  • Examples: Code snippets demonstrating proper usage
  • UI Visualization: Where applicable, guidance on how properties affect visual representation

1. Node Decorators

@Node

Creates a node class that can be registered and used in ChainGraph flows.

Target: Class

Properties:

PropertyTypeRequiredDefaultDescription
titlestringYes-Display name of the node
descriptionstringNo-Detailed description of the node's purpose
categorystringNo"other"Category grouping for the node
iconstringNo-Icon identifier for node visualization
tagsstring[]No[]Keywords for node searchability
authorstringNo-Author of the node
versionnumberNo1Node version number
uiNodeUIMetadataNo-UI styling and positioning properties

Example:

typescript
@Node({
  title: 'Message Creator',
  description: 'Creates a new message with the specified content',
  category: 'messaging',
  tags: ['message', 'create', 'content'],
})
export class CreateMessageNode extends BaseNode {
  // Node properties defined here
}

NodeUIMetadata Properties:

PropertyTypeDescription
position{ x: number, y: number }Position in the canvas
dimensions{ width: number, height: number }Node dimensions
style{ backgroundColor?: string, borderColor?: string }Visual styling
state{ isSelected?: boolean, isHighlighted?: boolean, isDisabled?: boolean }UI state

2. Property Decorators

Direction Decorators

@Input

Marks a property as an input port.

Target: Property

Example:

typescript
@Input()
@String()
message: string = '';

@Output

Marks a property as an output port.

Target: Property

Example:

typescript
@Output()
@String()
result: string = '';

Port Metadata Decorators

These decorators add metadata to ports but don't affect their data types.

@Id

Sets a custom identifier for a port.

Target: Property Properties:

ParameterTypeRequiredDescription
portIdstringYesCustom ID for the port

Example:

typescript
@Id('user_status')
@String()
status: string = 'active';

@Name

Provides a custom name for a port.

Target: Property Properties:

ParameterTypeRequiredDescription
portNamestringYesCustom display name for the port

Example:

typescript
@Name('User Message')
@String()
message: string = '';

@Title

Sets a title for a port.

Target: Property Properties:

ParameterTypeRequiredDescription
titlestringYesTitle text for the port

Example:

typescript
@Title('Message Content')
@String()
content: string = '';

@Description

Adds a description to a port.

Target: Property Properties:

ParameterTypeRequiredDescription
portDescriptionstringYesDescription text for the port

Example:

typescript
@Description('Enter the message content to be sent')
@String()
content: string = '';

@DefaultValue

Specifies a default value for a port.

Target: Property Properties:

ParameterTypeRequiredDescription
valueTYesDefault value for the port

Example:

typescript
@DefaultValue('Hello World')
@String()
greeting?: string;

@Metadata

Adds arbitrary metadata key/value pairs to a port.

Target: Property Properties:

ParameterTypeRequiredDescription
keystringYesMetadata key
valueanyYesMetadata value

Example:

typescript
@Metadata('ui:widget', 'status-indicator')
@String()
status: string = 'active';

3. Scalar Port Decorators

@String

Defines a port with string data type.

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
defaultValuestringNo''Default string value
minLengthnumberNo-Minimum string length
maxLengthnumberNo-Maximum string length
patternstringNo-Regular expression pattern for validation
uiStringPortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@String({
  defaultValue: 'Hello World',
  minLength: 1,
  maxLength: 100,
  pattern: '^[A-Za-z ]+$'
})
greeting: string = 'Hello World'

UI Configuration:

PropertyTypeDescription
isTextAreabooleanRender as multi-line text area
isPasswordbooleanRender as password field with masked input
textareaDimensions{ width?: number, height?: number }Custom dimensions for textarea

typescript
@String()
greeting: string = 'Hello World'
![](/images/nodes/string-basic.png)
typescript
@String({
  ui: {
    isTextArea: true,
  }
})
greeting: string = 'Hello World'
![](/images/nodes/string-textarea.png)
typescript
@String({
  ui: {
    isPassword: true,
  }
})
greeting: string = 'Hello World'
![](/images/nodes/string-password.png)
typescript
@String({
    ui: {
        hideEditor: true,
    },
})
greeting: string = 'Hello World'
![](/images/nodes/string-hidden-editor.png)
typescript
 @String({
    ui: {
        hidden: true,
    },
})
greeting: string = 'Hello World'
![](/images/nodes/string-hide.png)
typescript
 @String({
    ui: {
        hidePort: true,
    },
})
greeting: string = 'Hello World'
![](/images/nodes/string-hide-port.png)

@Number

Defines a port with numeric data type.

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
defaultValuenumberNo0Default numeric value
minnumberNo-Minimum allowed value
maxnumberNo-Maximum allowed value
stepnumberNo-Step increment for UI controls
integerbooleanNofalseWhether only integers are allowed
uiNumberPortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@Number({
  defaultValue: 42,
  min: 0,
  max: 100,
  step: 5,
  integer: true
})
quantity: number;

UI Configuration:

PropertyTypeDescription
isSliderbooleanRender as slider control
leftSliderLabelstringLabel for minimum value of slider
rightSliderLabelstringLabel for maximum value of slider
typescript
@Number()
greeting: number = 100
![](/images/nodes/number-basic.png)
typescript
@Number({
  ui: {
    isSlider: true,
  },
})
greeting: number = 100
![](/images/nodes/number-slider.png)
typescript
@Number({
  ui: {
    isSlider: true,
    leftSliderLabel: 'Low',
    rightSliderLabel: 'High',
  },
})
greeting: number = 100
![](/images/nodes/number-slider-label.png)

@Boolean

Defines a port with boolean data type.

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
defaultValuebooleanNofalseDefault boolean value
uiBooleanPortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@Boolean({
    defaultValue: true,
})
isActive: boolean = true
![](/images/nodes/boolean-basic.png)

4. Complex Port Decorators

@PortObject & @ObjectSchema

The Object port system allows defining complex structured data objects.

@ObjectSchema

Marks a class as an object schema.

Target: Class Properties:

PropertyTypeRequiredDefaultDescription
descriptionstringNo-Schema description
categorystringNo-Schema category
idstringNo-Custom identifier for the schema
typestringNo[Class name]Type identifier

Example:

typescript
@ObjectSchema({
  description: 'User profile data structure'
})
class UserProfile {
  @String({ minLength: 2 })
  name: string = 'Anonymous'

  @Number({ min: 18 })
  age: number = 21
}

@PortObject

Defines a port that accepts structured data.

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
schemaClass|IObjectSchemaYes-Object schema definition
defaultValueobjectNo{}Default object value
isSchemaMutablebooleanNofalseWhether properties can be added/removed at runtime
uiObjectPortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@PortObject({
  schema: UserProfile,
  defaultValue: new UserProfile(),
  isSchemaMutable: true
})
profile: UserProfile;

UI Configuration:

PropertyTypeDescription
addKeyFormHiddenbooleanHide the "Add Key" form
collapsedbooleanWhether the object collapsed in UI
typescript
@Input()
@PortObject({
    schema: UserProfile,
    ui: {
      collapsed: true,
    },
})
profile: UserProfile = new UserProfile()
![](/images/nodes/object-user-profile-basic.png)
typescript
@Input()
@PortObject({
    schema: UserProfile,
    ui: {
      collapsed: false,
    },
})
profile: UserProfile = new UserProfile()
![](/images/nodes/object-user-profile-basic-collapsible.png)
typescript
@Input()
@PortObject({
    schema: UserProfile,
    isSchemaMutable: true,
})
profile: UserProfile = new UserProfile()
![](/images/nodes/object-user-profile-basic-mutable.png)
typescript
@Input()
@PortObject({
    schema: UserProfile,
    isSchemaMutable: true,
    ui: {
        keyDeletable: false,
    },
})
profile: UserProfile = new UserProfile()
![](/images/nodes/object-user-profile-basic-mutable-no-deletable.png)

@PortArray & Array Helpers

@PortArray

Defines a port that accepts array data.

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
itemConfigIPortConfigYes-Configuration for array items
defaultValuearrayNo[]Default array value
minLengthnumberNo-Minimum array length
maxLengthnumberNo-Maximum array length
uiArrayPortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@PortArray({
  itemConfig: { type: 'string', minLength: 1 },
  defaultValue: ['item1', 'item2'],
  minLength: 1,
  maxLength: 10
})
tags: string[];

UI Configuration:

PropertyTypeDescription
addItemFormHiddenbooleanHide the "Add Item" form
itemDeletablebooleanAllow deleting array items

[UI SCREENSHOT PLACEHOLDER: Array port with items and add item button]

Array Helper Decorators

ChainGraph provides convenient helper decorators for common array types:

@PortArrayString

For string arrays.

Example:

typescript
@PortArrayString({ defaultValue: ['a', 'b'] })
tags: string[];
@PortArrayNumber

For number arrays.

Example:

typescript
@PortArrayNumber({ defaultValue: [1, 2, 3] })
scores: number[];
@PortArrayBoolean

For boolean arrays.

Example:

typescript
@PortArrayBoolean({ defaultValue: [true, false] })
flags: boolean[];
@PortArrayObject

For arrays of objects.

Example:

typescript
@PortArrayObject(UserProfile, { defaultValue: [new UserProfile()] })
users: UserProfile[];
@PortArrayNested

For multi-dimensional arrays.

Properties:

PropertyTypeRequiredDescription
depthnumberYesNumber of nested array levels
itemConfigIPortConfigYesConfiguration for innermost items
configobjectNoAdditional array configuration

Example:

typescript
@PortArrayNested(2, { type: 'number', defaultValue: 0 })
matrix: number[][];

@PortEnum & Enum Helpers

Enum ports allow selecting from a predefined set of options.

@PortEnum

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
optionsIPortConfig[]Yes-Array of option configurations
defaultValuestringNo-Default selected option ID
uiEnumPortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@PortEnum({
    options: [
        {
            id: 'red',
            type: 'string',
            defaultValue: 'Red',
            title: 'Red'
        },
        {
            id: 'green',
            type: 'string',
            defaultValue: 'Green',
            title: 'Green'
        },
        {
            id: 'blue',
            type: 'string',
            defaultValue: 'Blue',
            title: 'Blue'
        },
    ],
})
color: string = 'red'
![](/images/nodes/enum-basic.png)

Enum Helper Decorators

@StringEnum

For string-based enums.

Example:

typescript
@StringEnum(['Red', 'Green', 'Blue'], { defaultValue: 'Red' })
color: string;
@NumberEnum

For number-based enums.

Example:

typescript
@NumberEnum([1, 2, 3], { defaultValue: '1' })
level: number;
@PortEnumFromObject

Creates an enum from an object mapping.

Example:

typescript
const statusOptions = {
  active: new UserStatus('Active'),
  inactive: new UserStatus('Inactive'),
  pending: new UserStatus('Pending'),
};

@PortEnumFromObject(statusOptions, { defaultValue: 'active' })
status: keyof typeof statusOptions;
@PortEnumFromNative

Creates an enum from a native TypeScript enum.

Example:

typescript
enum Direction {
  Up = 'Up',
  Down = 'Down',
  Left = 'Left',
  Right = 'Right',
}

@PortEnumFromNative(Direction, { defaultValue: Direction.Up })
direction: Direction;

@PortStream

Defines a port that handles continuous data streams using the MultiChannel implementation.

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
itemConfigIPortConfigYes-Configuration for stream items
defaultValueMultiChannelNo-Default stream channel
uiStreamPortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@PortStream({
  itemConfig: { type: 'string', minLength: 1 }
})
inputStream: MultiChannel<string> = new MultiChannel<string>();

@PortAny

Defines a port that can accept any type of value, with optional type validation through an underlying type.

Target: Property Properties:

PropertyTypeRequiredDefaultDescription
underlyingTypeIPortConfigNo-Optional configuration for value validation
defaultValueanyNonullDefault value
uiBasePortConfigUITypeNo-UI configuration options

Example:

typescript
@Input()
@PortAny({
  underlyingType: { type: 'string', minLength: 1 },
  defaultValue: 'default'
})
dynamicInput: any;

5. Common UI Configuration Properties

All port types support a common set of UI configuration properties through the ui parameter:

PropertyTypeApplicable ToDescription
hiddenbooleanAll portsHide the port entirely
disabledbooleanAll portsDisable user interaction with the port
hideEditorbooleanAll portsHide the editor while showing the port connection point
hidePortbooleanAll portsHide the port from the node display
bgColorstringAll portsBackground color for the port handle (hex or CSS color)
borderColorstringAll portsBorder color for the port handle (hex or CSS color)

Example:

typescript
@String({
  ui: {
    bgColor: '#e70d0d',
    borderColor: '#460707',
    disabled: false,
    hideEditor: false
  }
})

6. Port Type-Specific UI Properties

String Port UI

PropertyTypeDescription
isTextAreabooleanRender as multi-line text area instead of single-line input
isPasswordbooleanRender as password field with masked input
textareaDimensions{ width?: number, height?: number }Custom dimensions for textarea

Number Port UI

PropertyTypeDescription
isSliderbooleanRender as slider control instead of number input
leftSliderLabelstringLabel for minimum value of slider
rightSliderLabelstringLabel for maximum value of slider

Array Port UI

PropertyTypeDescription
addItemFormHiddenbooleanHide the "Add Item" form
itemDeletablebooleanWhether array items can be deleted
allowedTypesPortType[]Types allowed when adding new items

Object Port UI

PropertyTypeDescription
keyDeletablebooleanWhether object properties can be deleted
collapsedbooleanWhether the object port collapsed
hidePropertyEditorbooleanWhether object properties can be edited
allowedTypesPortType[]Types allowed when adding new properties

7. Complete Examples

Basic Node with Scalar Ports

typescript
import type { ExecutionContext, NodeExecutionResult } from '@badaitech/chaingraph-types'
import {
  BaseNode,
  Boolean,
  Input,
  Node,
  Number,
  ObjectSchema,
  Output,
  String,
} from '@badaitech/chaingraph-types'
import { NODE_CATEGORIES } from '../categories'

@Node({
  title: 'Message Formatter',
  description: 'Formats a message with parameters',
  category: NODE_CATEGORIES.DATA,
  tags: ['formatting', 'text', 'message'],
})
class MessageFormatterNode extends BaseNode {
  @Input()
  @String({
    title: 'Message Template',
    description: 'Template with {placeholder} markers',
    defaultValue: 'Hello, {name}!',
    minLength: 1,
    ui: {
      isTextArea: true,
      textareaDimensions: { width: 200, height: 100 },
    }
  })
  template: string = 'Hello, {name}!'

  @Input()
  @String({
    title: 'Name',
    description: 'Name to insert in the template',
    defaultValue: 'World',
  })
  name: string = 'World'

  @Input()
  @Number({
    title: 'Repeat Count',
    description: 'Number of times to repeat the message',
    defaultValue: 1,
    min: 1,
    max: 10,
    integer: true,
    ui: {
      isSlider: true,
    }
  })
  repeatCount: number = 1

  @Input()
  @Boolean({
    title: 'Add Timestamp',
    description: 'Whether to add a timestamp to the message',
    defaultValue: false,
  })
  addTimestamp: boolean = false

  @Output()
  @String({
    title: 'Formatted Message',
    description: 'The resulting formatted message',
  })
  output: string = ''

  async execute(context: ExecutionContext): Promise<NodeExecutionResult> {
    let message = this.template.replace('{name}', this.name)

    if (this.addTimestamp) {
      message = `${message} [${new Date().toISOString()}]`
    }

    this.output = message.repeat(this.repeatCount)

    return {}
  }
}

8. Best Practices

Organization of Decorators

When applying multiple decorators to a property, follow this order for maximum readability:

  1. Direction decorators (@Input, @Output)
  2. Metadata decorators (@Title, @Description)
  3. Type decorators (@String, @Number, etc.)
typescript
@Input()
@Title('User Message')
@Description('Enter the message to be processed')
@String({
  minLength: 1,
  maxLength: 1000,
  ui: {
    isTextArea: true
  }
})
message: string = '';

Object Schema Definitions

Define object schemas as separate classes rather than inline objects for better reusability and type safety:

typescript
// Good practice:
@ObjectSchema()
class UserProfile {
  @String()
  name: string = '';

  @Number()
  age: number = 0;
}

@Input()
@PortObject({ schema: UserProfile })
user: UserProfile;

// Avoid:
@Input()
@PortObject({
  schema: {
    properties: {
      name: { type: 'string' },
      age: { type: 'number' }
    }
  }
})
user: any;

Default Values

Always provide sensible default values, either in decorator configurations or as property initializers:

typescript
// In decorator:
@String({ defaultValue: 'Default text' })
message?: string

// Or as property initializer:
@String()
message: string = 'Default text';

This specification document provides a comprehensive reference for all ChainGraph decorators and their configuration options. Use this as your guide when creating nodes, ports, and object schemas in your ChainGraph applications.

Licensed under BUSL-1.1