import { Bindable , isBindable } from '../shared/bindable' ; import { PinLike } from '../pin/pin-like' ; import { Signature } from './signature' ; import { Agent } from './agent' ; import { ChildNotDefined } from './errors/child-not-defined.error' ; import { ChildIsNotPin , ChildIsNotAgent } from './errors/child-type-mismatch.error' ; type _ChildType = PinLike | Agent ; /**
 *
 * Represents [compositions](https://connective.dev/docs/composition). This class
 * is to be used directly if you want to create class-based compositions, otherwise
 * utilize [`composition()`](https://connective.dev/docs/composition) method.
 *
 */ export abstract class Composition extends Agent implements Bindable { private _bindables : Bindable [ ] | undefined ; private _children : { [ name ] : _ChildType ; } | undefined ; param signature constructor ( signature ) { super ( signature ) ; this . init ( ) ; } /**
     *
     * Override this to modify the initialization process of a composition.
     * This function is called by parent's constructor, so if you want to
     * invoke `.build()` and `.wire()` after some child-class properties have been
     * initialized as well, you would need to override this function. This is a typical
     * scenario in case of parametric class-based compositions.
     *
     */ protected init ( ) { this . build ( ) ; this . wire ( ) ; } /**
     *
     * Override this to define the child pins and agents of your composition,
     * using `.add()` method:
     *
     * ```typescript
     * build() {
     *   this.add('myState', state());
     *   this.add('mySink', sink());
     *   this.myOtherState = this.add('myOtherState', state()) as State;
     * }
     * ```
     *
     */ protected abstract build ( ) ; /**
     *
     * Override this to wire the pins and agents you defined in `.build()` to each other:
     *
     * ```typescript
     * build() {
     *   this.agent('myState').out('value').to(this.pin('mySink'));
     *   this.pin('mySink').to(this.myOtherState).to(this.out('myOutput'));
     * }
     * ```
     *
     */ protected abstract wire ( ) ; protected add ( child ) ; protected add ( name , child ) ; param nameOrChild param child protected add ( nameOrChild , child ? ) { if ( ! this . _children ) this . _children = { } ; if ( ! child ) return this . add ( `${ Object . keys ( this . _children ) . length }` , nameOrChild as _ChildType ) ; let _name = nameOrChild as string ; this . _children [ _name ] = child ; if ( isBindable ( child ) ) this . toBind ( child ) ; return child ; } param name returns throws protected child ( name ) { if ( typeof name !== 'string' ) return this . child ( name . toString ( ) ) ; if ( this . _children && name in this . _children ) return this . _children [ name ] ; throw new ChildNotDefined ( name ) ; } param name returns throws protected pin ( name ) { let _child = this . child ( name ) ; if ( _child instanceof Agent ) throw new ChildIsNotPin ( name . toString ( ) ) ; return _child ; } param name returns throws protected agent ( name ) { let _child = this . child ( name ) ; if ( ! ( _child instanceof Agent ) ) throw new ChildIsNotAgent ( name . toString ( ) ) ; return _child ; } param bindable protected toBind ( bindable ) { if ( ! this . _bindables ) this . _bindables = [ ] ; this . _bindables . push ( bindable ) ; return this ; } /**
     *
     * Binds all registered `Bindable`s, including bindable children like
     * [states](https://connective.dev/docs/state) and
     * [sinks](https://connective.dev/docs/sink).
     *
     */ bind ( ) { if ( this . _bindables ) this . _bindables . forEach ( bindable => bindable . bind ( ) ) ; return this ; } note clear ( ) { if ( this . _children ) { Object . values ( this . _children ) . forEach ( child => child . clear ( ) ) ; this . _children = undefined ; } if ( this . _bindables ) this . _bindables = undefined ; return super . clear ( ) ; } }