Skip to content

Syntax Reference

Complete syntax patterns for EventFlow.

File Structure

flow
machine: @machine_name

scenario: scenario name

  given:
    // setup

  on> :event from @actor
    // actions
    // guards
    // emit
    expect:
      // assertions

  on :event from @actor
    // ...

  expect:
    // scenario assertions

Machine Definition

flow
machine: @name

Examples:

flow
machine: @order
machine: @payment
machine: @customer

System Definition

flow
system: system name

uses:
  @machine1 from "./path/to/machine1.flow"
  @machine2 from "./path/to/machine2.flow"

machine: @machine3
  // Can also define inline

Scenario Definition

flow
scenario: descriptive name

  // optional given
  given:
    // setup

  // event handlers
  on> :event from @actor
    // ...

  // optional assertions
  expect:
    // ...

Event Handlers

API Event (Public)

flow
on> :event_name from @actor
  // actions

Internal Event

flow
on :event_name from @actor
  // actions

Without Source Filter

flow
on :event_name
  // accepts from any sender

Actions

Actions are lines without prefix.

State Transitions

flow
order moves to #state
order transitions to #state
order enters #state
order becomes #state

Context Updates

flow
$variable becomes value
$variable: type becomes value
$variable increases by amount
$variable decreases by amount
$variable adds item
$variable removes item
$variable clears

Event Emission

flow
emit :event to @actor

emit :event to @actor
  with $var1, $var2

emit :event to @actor
  with:
    | field | value |
    | key1  | val1  |
    | key2  | val2  |

emit :event to @actor
  with:
    | field | type   | value |
    | key1  | string | val1  |
    | key2  | number | val2  |

Custom Actions

flow
send confirmation email
process the payment
validate cart contents
notify warehouse team

Guards

Single Guard

flow
? condition
  // runs if true

Multiple Guards (AND)

flow
? condition1
? condition2
  // runs if BOTH true

OR Guards

flow
? condition1
?? condition2
  // runs if EITHER true

Combined Logic

flow
? conditionA
? conditionB
?? conditionC
  // runs if (A AND B) OR C

Default Case

flow
? condition1
  handle 1
? condition2
  handle 2
otherwise
  handle default

Or:

flow
? condition
  handle condition
?
  handle else

Nested Guards

flow
? outer condition
  ? inner condition
    // nested action
  otherwise
    // inner else
otherwise
  // outer else

Given Blocks

Scenario-Level

flow
scenario: name

  given:
    @actor is in state
    $variable: type is value
    collection contains items

Event-Level

flow
on> :event from @actor

  given:
    additional setup

Table Data

flow
given:
  cart contains:
    | product | price | quantity |
    | Laptop  | 1200  | 1        |
    | Mouse   | 25    | 2        |

Expect Blocks

Event-Level Assertions

flow
on> :event from @actor
  // actions

  expect:
    = assertion1
    = assertion2

Scenario-Level Assertions

flow
scenario: name

  on> :event1 ...
  on :event2 ...

  expect:
    = final assertions

Assertion Patterns

flow
expect:
  // State assertions
  = order is in #paid
  = order is not in #cancelled

  // Context assertions
  = $total equals 1200
  = $items contains "Laptop"
  = $count is greater than 0

  // Event assertions
  = @customer received :confirmation
  = :payment_request was emitted to @payment

Scheduled Events

flow
on :event_name
  triggered: schedule expression

  for each item in #state
    ? guard condition
      // actions

Schedule Expressions

flow
triggered: every day at 00:00
triggered: every hour
triggered: every 30 minutes
triggered: every monday at 09:00
triggered: every month on 1st at 00:00

Comments

flow
// Single line comment

/*
  Multi-line
  comment
*/

action // Inline comment

Data Tables

flow
table_name:
  | column1 | column2 | column3 |
  | value1  | value2  | value3  |
  | value4  | value5  | value6  |

With types:

flow
with:
  | field | type   | value |
  | id    | string | $id   |
  | count | number | $cnt  |

Context Variable Patterns

Declaration

flow
$name becomes value
$name: type becomes value

In Guards

flow
? $total equals 100
? $total is 100
? $total is greater than 0
? $total is less than 1000
? $items contains "Laptop"
? $items is empty
? $items is not empty

In Assertions

flow
= $total equals 100
= $items contains "Laptop"
= $count is greater than 0

Indentation

EventFlow uses indentation for nesting. Use consistent spaces (2 or 4):

flow
machine: @order                    // Level 0

scenario: checkout                 // Level 1

  given:                           // Level 2
    @customer is logged in         // Level 3

  on> :checkout from @customer     // Level 2
    ? cart is valid                // Level 3
      order moves to #processing   // Level 4
      emit :request to @payment    // Level 4

    otherwise                      // Level 3
      emit :error to @customer     // Level 4

  expect:                          // Level 2
    = order is in #processing      // Level 3

Complete Example

flow
machine: @order

scenario: complete checkout

  given:
    @customer is logged in as "[email protected]"
    cart contains:
      | product | price |
      | Laptop  | 1200  |
    $total: number is 1200

  on> :checkout from @customer
    ? cart is not empty
      ? $total > 0
        order moves to #awaiting_payment
        emit :payment_request to @payment
          with:
            | order_id | string | $order_id |
            | amount   | number | $total    |
      otherwise
        emit :zero_total_error to @customer
    otherwise
      emit :empty_cart_error to @customer

    expect:
      = order is in #awaiting_payment

  on :payment_success from @payment
    order moves to #paid
    $paid_at becomes now
    emit :order_confirmed to @customer

  on :payment_failed from @payment
    $retry_count increases by 1
    ? $retry_count < 3
      emit :payment_request to @payment
    otherwise
      order moves to #failed
      emit :order_failed to @customer

  expect:
    = order is in #paid
    = @customer received :order_confirmed

Test File Syntax (.test.flow)

Test files contain variations of the happy path defined in .flow files.

Test File Structure

flow
test: @machine_name

  for scenario: scenario name

    for :event:
      test name:
        // variations + assertions

    test name:
      // path divergence + assertions

Transition Tests

Target specific event handlers with for :event::

flow
test: @order

  for scenario: checkout

    for :checkout:
      empty cart rejected:
        with scenario:
          cart is empty
        = @customer received :error

      payment declined:
        assume:
          ? payment successful = false
        = order is in #payment_failed

Scenario Tests (Path Divergence)

Test full flow variations with after/receive/then:

flow
test: @order

  for scenario: checkout

    payment fails:
      after :checkout
      receive :payment_failed from @payment
      = order is in #payment_failed

    retry then succeed:
      after :checkout
      receive :payment_failed from @payment
      then :payment_success from @payment
      = order is in #paid

Variation Sources

flow
// Override scenario-level given
with scenario:
  cart is empty

// Override event data
with event:
  payment_method is "bank_transfer"

// Override event-level given
with given:
  shipping_address is invalid

// Override context variables
with context:
  $total is 5000

assume: System

Control guard and action behavior in tests:

flow
assume:
  // Force guard result
  ? cart is valid = false

  // Action returns value
  process payment returns { transaction_id: "TXN-123" }

  // Action throws error
  send email throws "SMTP error"

observe: System

Watch actions without changing behavior:

flow
observe:
  send confirmation email
  update inventory

= send confirmation email was called
= send confirmation email was called with:
    to: @customer.email
= update inventory was not called

System Test Syntax

For machine systems, target specific machines:

flow
test: system checkout

  for scenario: complete purchase

    for :checkout to @order:
      guest rejected:
        with scenario:
          @customer is not logged in
        = @customer received :login_required

    for :payment_request to @payment:
      gateway down:
        assume:
          @payment.process throws "Unavailable"
        = @order is in #payment_failed

Released under the MIT License.