Activity Modes
Every activity you add with .do() runs in one of several modes. The mode
controls how the operation executes — once, in a loop, conditionally, or in
parallel. Modes are selected with the ACTIVITY enum:
import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"Execute once (default)
Section titled “Execute once (default)”The simplest mode runs an activity exactly once per context. Just pass a name and an operation:
class MyAction { setup(builder) { builder.do("processItem", ctx => { ctx.result = ctx.input * 2
return ctx }) }}Loops while a predicate returns true. The predicate is evaluated
before each iteration, so the body may run zero times.
import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"
class CounterAction { #shouldContinue = ctx => ctx.count < 10 #increment = ctx => { ctx.count += 1; return ctx }
setup(builder) { builder .do("initialize", ctx => { ctx.count = 0; return ctx }) .do("countUp", ACTIVITY.WHILE, this.#shouldContinue, this.#increment) .do("finish", ctx => { return ctx.count }) }}Signature: .do(name, ACTIVITY.WHILE, predicate, operation)
Loops until a predicate returns true. The predicate is evaluated
after each iteration, so the body always runs at least once.
import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"
class ProcessorAction { #queueIsEmpty = ctx => ctx.queue.length === 0
#processItem = ctx => { const item = ctx.queue.shift() ctx.processed.push(item)
return ctx }
setup(builder) { builder .do("initialize", ctx => { ctx.queue = [1, 2, 3, 4, 5] ctx.processed = []
return ctx }) .do("process", ACTIVITY.UNTIL, this.#queueIsEmpty, this.#processItem) .do("finish", ctx => { return ctx.processed }) }}Signature: .do(name, ACTIVITY.UNTIL, predicate, operation)
Conditionally runs an activity at most once. If the predicate is true, the
operation runs once; if false, the activity is skipped entirely.
import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"
class ConditionalAction { #shouldProcess = ctx => ctx.value > 10 #processLargeValue = ctx => { ctx.processed = ctx.value * 2; return ctx }
setup(builder) { builder .do("initialize", ctx => { ctx.value = 15; return ctx }) .do("maybeProcess", ACTIVITY.IF, this.#shouldProcess, this.#processLargeValue) .do("finish", ctx => { return ctx }) }}Signature: .do(name, ACTIVITY.IF, predicate, operation)
BREAK and CONTINUE
Section titled “BREAK and CONTINUE”BREAK and CONTINUE control loops from the inside, mirroring their JavaScript
namesakes. Both must live inside a nested builder
that is the body of a WHILE or UNTIL loop.
BREAKexits the enclosing loop when its predicate istrue.CONTINUEskips the rest of the current iteration when its predicate istrue.
.do("loop", ACTIVITY.WHILE, ctx => ctx.count < 100, new ActionBuilder() .do("increment", ctx => { ctx.count++; return ctx }) .do("earlyExit", ACTIVITY.BREAK, ctx => ctx.count >= 5))Signatures: .do(name, ACTIVITY.BREAK, predicate) and
.do(name, ACTIVITY.CONTINUE, predicate)
These get their own deep dive in Control Flow.
Fans a context out into multiple parallel sub-runs, then rejoins the results. SPLIT needs a splitter (divides the context), a rejoiner (recombines results), and an operation (runs per split).
.do("parallel", ACTIVITY.SPLIT, splitter, rejoiner, operation)Because parallel work and settled results have a few sharp edges, SPLIT has a dedicated guide: Parallelism with SPLIT.
Mode summary
Section titled “Mode summary”| Mode | Signature | Predicate timing | Use case |
|---|---|---|---|
| Default | .do(name, operation) | — | Execute once per context |
| WHILE | .do(name, ACTIVITY.WHILE, predicate, operation) | Before iteration | Loop while condition is true |
| UNTIL | .do(name, ACTIVITY.UNTIL, predicate, operation) | After iteration | Loop until condition is true |
| IF | .do(name, ACTIVITY.IF, predicate, operation) | Before execution | Run once or skip |
| BREAK | .do(name, ACTIVITY.BREAK, predicate) | When reached | Exit enclosing loop |
| CONTINUE | .do(name, ACTIVITY.CONTINUE, predicate) | When reached | Skip to next iteration |
| SPLIT | .do(name, ACTIVITY.SPLIT, splitter, rejoiner, operation) | — | Parallel split/rejoin |
Constraints
Section titled “Constraints”- One mode per activity. Combining modes throws.
- SPLIT requires both functions. The splitter and rejoiner are mandatory.
- Predicates return booleans. All predicates (
WHILE,UNTIL,IF,BREAK,CONTINUE) should returntrueorfalse. - BREAK/CONTINUE need a loop. They only work inside a nested builder that is
the body of a
WHILEorUNTILactivity.