API

Types

Behavior

A behavior is a block of code together with its dependency relationships (links). They are one of the two node types in a behavior graph. You define behaviors using the behavior() factory method of an Extent.

Behaviors have both static and dynamic links. You provide static links when you create the behavior. Behavior Graph will update dynamic links per special methods on BehaviorBuilder or you can update them directly on a behavior.

demands

  • returns: Set<Resource> | null
  • read only property

The current set of all Resources which the behavior demands.

extent

  • returns: Extent
  • read only property

A behavior always has an Extent with which it is created.

setDynamicDemands()

  • param: newDemands: (Demandable | undefined)[] | null)

Provide an array of Demandables. undefined is also an element type to make for easier use of optional chaining. Providing null is equivalent to saying there are no dynamic demands.

setDynamicSupplies()

  • param: newSupplies: (Resource | undefined)[] | null)

Provide an array of Resources to supply. undefined is also an element type to make for easier use of optional chaining. Providing null is equivalent to saying there are no dynamic supplies.

supplies

  • returns: Set<Resource> | null
  • read only property

The current set of all Resources which the behavior supplies.

BehaviorBuilder

BehaviorBuilder provides fluent API for constructing instances of a Behavior. You create an instance of a BehaviorBuilder using the behavior() method on Extent. All methods except runs() return the same instance of BehaviorBuilder so you can chain multiple optional clauses.

Generic type T is the Extent subtype BehaviorBuilder is created with.

// Create a single behavior with one demand and one supply.
this.moment1 = this.moment();
this.moment2 = this.moment();
this.moment3 = this.moment();
this.behavior()
    .demands(this.moment1, this.moment2)
    .supplies(this.moment3)
    .runs(ext => {
        if (ext.moment1.justUpdated || ext.moment2.justUpdatedTo(false)) {
            ext.moment3.update();
        }
    });

demands()

  • params: ...demands: Demandable[]
  • returns: BehaviorBuilder<T>

Provide a list of static demands this behavior will link to.

dynamicDemands()

  • param: switches: Demandable[]
  • param: links: ((ext: T) => (Demandable | undefined)[] | null)
  • param: relinkingOrder?: RelinkingOrder
  • returns: BehaviorBuilder<T>

This clause updates the dynamicDemands of this behavior based on the updating of other resources, the switches. When the switches update, the links parameter will be called which should return the list of new resources.

We permit undefined in the list to make for easier optional chaining. Returning null is equivalent to setting no dynamicDemands.

relinkingOrder parameter can optionally be set to Extent.relinkingOrderSubsequent which will update the demands after the runs block is run.

// This behavior will automatically demand the deleteButton resource of
// the currentChild extent whenever it changes.
this.currentChild = this.state(null);
this.behavior()
    .dynamicDemands([this.currentChild], ext => {
        return [ext.currentChild.value?.deleteButton];
    })
    .runs(ext => {
        if (ext.currentChild.value?.deleteButton.justUpdated) {
            // do something in response
        }
    });

dynamicSupplies()

  • param: switches: Demandable[]
  • param: links: ((ext: T) => (Resource | undefined)[] | null)
  • returns: BehaviorBuilder<T>

This clause updates the dynamicSupplies similarly to the dynamicDemands clause.

runs()

  • param: block: (ext: T) => void
  • returns: Behavior

This clause sets the block of code which will run when the behavior is activated. The parameter ext will be the instance of the Extent this behavior was created on.

runs() will return the created behavior which will typically be ignored.

supplies()

  • params: ...supplies: Resource[]
  • returns: BehaviorBuilder<T>

Provide a list of static supplies this behavior will link to.

Extent

Extents are collections of resources and behaviors. You will create your own Extent subclasses to define your Behavior Graph functionality.

// Define extent that toggles state on a switch
class MyExtent extends Extent {
    constructor(graph) {
        super(graph);

        this.toggleSwitch = this.moment();
        this.currentState = this.state(false);

        this.behavior()
            .demands(this.toggleSwitch)
            .supplies(this.currentState)
            .runs(ext => {
                this.currentState.update(!this.currentState.value);
            });
    }
}

// Create instance of MyExtent and add it to the graph
let myGraph = new Graph();
let main = new MyExtent(myGraph);
main.addToGraphWithAction();

action()

  • param action: (ext: this) => void
  • param debugName?: string

Calls the action() method on the underlying Graph object. Contains an additional ext parameter which will be this Extent instance.

async actionAsync()

  • param action: (ext: this) => void
  • param debugName?: string
  • returns: Promise

Calls the actionAsync() method on the underlying Graph object. Contains an additional ext parameter which will be this Extent instance.

addChildLifetime()

  • param: extent: Extent

Adds the parameter to list of child lifetimes. An extent with child lifetimes is guaranteed to be part of the graph while the child is. Behaviors in child extents are permitted to have static links to resources in the parent.

addedToGraph

  • returns State<boolean>
  • read only property

Every extent comes with this state resource. It updates to true when the extent is added to the graph.

addedToGraphWhen

  • returns number | null
  • read only property

The sequence number of the event the Extent was added to the Graph. It is null if it has not been added or once it is removed.

addToGraph()

Adds this extent to the graph. Behavior Graph will only interact with resources and behaviors after their extent has been added.

addToGraphWithAction()

  • param: debugName?: string

Syntactic sugar for creating a new action and calling addToGraph(). debugName is passed to the action.

behavior()

Creates a BehaviorBuilder instance.

debugName

  • returns string | null
  • read write property

You can define a runtime debugName for your instances to aid in debugging. It defaults to the name of your Extent subclass.

new Extent()

  • param: graph: Graph
  • constructor

An Extent must be initialized with a Graph. You must call super() with the graph in your overridden constructor.

graph

  • returns Graph
  • read only property

The graph on which this extent was created.

moment<T>()

  • param debugName?: string

Factory method to create a moment resource. By default the debugName will be the name of the property that points to this resource.

removeFromGraph()

  • param strategy?: ExtentRemoveStrategy

After an extent is removed from the graph its resource and behaviors will no longer interact with other extents in the graph.

Extents must be removed in a manner that is consistent with their lifetimes.

  • All extents with a unified lifetime must be removed during the same event.
  • All extents that have a parent lifetime must not remain in the graph longer than their parent.

Providing Extent.removeContainedLifetimes as the strategy parameter will automatically remove all extents with the unified or child lifetimes.

removeFromGraphWithAction()

  • param: strategy?: ExtentRemoveStrategy
  • param: debugName?: string

Syntactic sugar for creating a new action and calling removeFromGraph(). debugName is passed to the action.

resource()

  • param debugName?: string

Factory method to create a resource. By default the debugName will be the name of the property that points to this resource.

sideEffect()

  • param block: (ext: this) => void
  • param debugName?: string

Calls the sideEffect() method on the underlying Graph object. Contains an additional ext parameter

state<T>()

  • param initialState: T
  • param debugName?: string

Factory method to create a state resource. By default the debugName will be the name of the property that points to this resource.

unifyLifetime()

  • param: extent: Extent

Combines the lifetime of this extent with that of the parameter. Extents with unified lifetimes are guaranteed to be part of the graph for the same period of time. They are permitted to have static links between them.

Graph

action()

  • param block: () => void
  • param debugName?: string

Creates a synchronous action on the graph. By default the debugName will be derived from the set of resources that are updated inside the action block.

async actionAsync()

  • param block: () => void
  • param debugName?: string
  • returns: Promise

Creates an action that will run asynchronously.

currentBehavior

  • returns Behavior | null
  • read only property

Returns the currently running behavior or null if otherwise.

currentEvent

  • returns GraphEvent | null
  • read only property

Returns the current GraphEvent if the graph is running an event or null otherwise.

dateProvider

  • returns: GraphDateProvider
  • read write property

The default dateProvider returns Date.now() which is the source of timestamp on GraphEvent instances. Overriding is primarily useful for controlling values during testing.

debugCycleForBehavior()

  • param behavior: Behavior
  • returns: Resource[]

Used during debugging as an aid when there are dependency cycles. The returned array contains the sequence of Resource objects which will result in a dependency cycle including this behavior. The array will be empty if there is not a cycle.

lastEvent

  • returns GraphEvent
  • read only property

Returns the last GraphEvent that completed. It starts as GraphEvent.initialEvent.

sideEffect()

  • param: block: () => void
  • param: debugName?: string

Creates a block of code that will run during the side effect phase of the event.

GraphEvent

sequence

  • returns: number
  • read only property

Each GraphEvent is assigned a monotonically increasing number for each event run on the graph. You can use this information to quickly determine the order resources update.

timestamp

  • returns: Date
  • read only property

Each GraphEvent is given a timestamp according to the registered DateProvider given to a graph instance. It defaults to Date.now().

Moment

extends Resource

A Moment is a type of Resource for tracking information that exists at a moment in time. Button presses or network call returns are examples of Moments.

Moments optionally have values associated with them. The payload of a network call return is a possible value for a moment. Those values are reset to undefined at the end of the event.

event

  • returns GraphEvent | null
  • read only property

Returns the GraphEvent of the most recent time the moment was updated. It is null if it has never been updated.

justUpdated

  • returns: boolean
  • read only property

Returns true if the moment updated during this event.

justUpdatedTo()

  • param value: T
  • returns boolean

Returns true if the moment was justUpdated and the value == the parameter. If you wish to use something different than == you can implement your own check as this method is syntactic sugar.

update()

  • param value: T | undefined

Marks the moment as justUpdated. If a value is provided, it will be set on the moment for reading.

updateWithAction()

  • param value: T | undefined
  • param debugName? : string

Syntactic sugar for calling action() on the underlying graph and calling update() on the moment.

value

  • returns T
  • read only property

Returns the value stored in the moment if it was updated during this event. It is undefined if it was not updated or outside of an event.

Moments do not necessarily have a value. They will not if they were not given one in their update() method.

Resource

The base class for State and Moment. Prefer those types in almost all circumstances. Wherever you see “resource” in this document, assume we are referring to instances of State and Moment.

Resource has minimal functionality. Using instances of this base class directly is useful when forcing a certain ordering relationship between behaviors.

debugName

  • returns string | null
  • read write property

Assignable name for use during debugging.

extent

  • returns Extent
  • read only property

All resources belong to an Extent.

graph

  • returns Graph
  • read only property

All resources belong to a Graph.

order

  • returns Demandable
  • read only property

A behavior can also demand resource.order which tells the behavior not to activate when the resource updates.

suppliedBy

  • returns Behavior | null
  • read only property

If the resource is supplied by a behavior it will be returned, null otherwise.

State

extends Resource

A State is a type of resource for storing information over a period of time. Its value will persist into the future until it is updated.

All States must be given an initial value when created.

event

  • returns GraphEvent
  • read only property

The last time the State was updated. Will return GraphEvent.initialEvent for its initial value before it has been updated.

justUpdated

  • returns boolean
  • read only property

Returns true if the state was updated during this event.

justUpdatedTo()

  • param: toState: T
  • returns: boolean

Returns true if the state was updated during this event and toState parameter == value.

justUpdatedFrom()

  • param: fromState: T
  • returns: boolean

Returns true if the state was updated during this event and the fromState parameter == the value it had before updating.

justUpdatedToFrom()

  • param: toState: T
  • param: fromState: T
  • returns: boolean

A combination of justUpdatedTo() and justUpdatedFrom()

traceEvent

  • returns: GraphEvent
  • read only property

What was the value of the event property at the beginning of the current event.

traceValue

  • returns: T
  • read only property

What was the value of the value property at the beginning of the current event.

updateWithAction()

  • param: newValue: T
  • param: debugName?: string

Equivalent to calling action() on the underlying Graph instance and update() on the State object.

update()

  • param: newValue: T

Checks to see if the newValue parameter !== the current value, and if so updates it to that new value.

updateForce()

  • param: newValue: T

Updates value to the newValue even if they are the same.

value

  • returns: T
  • read only property

The current underlying value.

Interfaces

Demandable

What a behavior can demand. A sealed opaque type which includes:

  • Instances of Resource and its subclasses State and Moment
  • The object returned by .order on an instance of Resource

There are no other Demandable types and it is not open for extension.

DateProvider

Optional override for default dateProvier on Graph instance.

Implement a type with a single method:

now(): Date