@mantaq/sugar
matches(actor, pattern)
Section titled “matches(actor, pattern)”import { checkout } from "../checkout.ts";
function matches(actor: { snapshot(): Snapshot }, pattern: string): boolean;Dot-separated pattern match against snapshot tree.
matches(checkout, "payment"); // flatmatches(checkout, "basicInfo.fraud.scanning"); // hierarchicalmatches(checkout, "basicInfo.fraud.cleared"); // parallel regionPrefix match. Checks top-level state name first, descends through region names in order, ends at state boundary. matches(actor, "connected") is true only if top-level state is connected, not anywhere inside. That is isIn.
states(...entries)
Section titled “states(...entries)”function states( ...entries: Array<string | { name: string; final?: boolean }>): Record<string, StateRef>;Batch state creation. Entries are strings, or objects with final: true for a terminal state.
const s = states("basicInfo", "payment", { name: "success", final: true }, "error");s.basicInfo.name; // "basicInfo"s.success.isFinal; // trueevents(...names)
Section titled “events(...names)”function events<T extends string>(...names: T[]): { [K in T]: EventRef<K> };Batch event creation.
const e = events("submitBasicInfo", "submitPayment", "back");e.submitBasicInfo.create(); // { type: "submitBasicInfo" }tag(...stateRefs)
Section titled “tag(...stateRefs)”function tag(...stateRefs: StateRef<string, unknown>[]): Tag;Group states. tag.has(snapshot) returns true if any active.
const busy = tag(s.payment, s.submitting);busy.has(checkout.snapshot()); // true if paying or submittingActorMap
Section titled “ActorMap”Keyed registry of one actor type.
const map = new ActorMap(createOrderWorker);map.spawn("ord_1");map.send("ord_1", ship.create());map.kill("ord_1");new ActorMap(factory, options?). Factory builds actor, receiving key: (id: string) => Actor. spawn(key) creates fresh instance keyed by id. options: { autoReap? }: true removes child once it reaches final state or dies into __error. Off by default.
| Method | What |
|---|---|
spawn(key) | Fresh instance via factory |
ensure(key) | No-op if exists |
send(key, event) | Send to child |
kill(key) | Abort and remove |
has(key) | Exists? |
keys() | List keys |
snapshot(key) | Child snapshot. undefined if absent |
size | Count |
broadcast(map, event)
Section titled “broadcast(map, event)”function broadcast<const T extends SendableEvent>(map: SendableMap<T>, event: T): void;Send to all children.
isIn(snapshot, stateName)
Section titled “isIn(snapshot, stateName)”function isIn(snapshot: Snapshot, stateRefName: string): boolean;Recursive search of snapshot tree for state name.
activeLeaves(snapshot)
Section titled “activeLeaves(snapshot)”function activeLeaves(snapshot: Snapshot): string[];All leaf paths as dot-separated strings.
actorSpec(config)
Section titled “actorSpec(config)”function actorSpec< const States extends readonly AnyStateRef[], const Inputs extends readonly AnyEventRef[], const Internal extends readonly AnyEventRef[] = readonly [], const Outputs extends readonly AnyEventRef[] = readonly [], ActorContext = Record<string, unknown>, const Initial extends AnyStateRef | { state: AnyStateRef; payload?: unknown } = AnyStateRef,>(config: { states: States; inputs: Inputs; internal?: Internal; outputs?: Outputs; context?: ActorContext; initial: Initial; clock?: Clock; regions?: Record<string, AnyActor>; internalBudget?: number;}): { states: States; inputs: Inputs; internal?: Internal; outputs?: Outputs; context?: ActorContext; initial: Initial; clock?: Clock; regions?: Record<string, AnyActor>; internalBudget?: number;};Builds static actor spec. Narrows state/event arrays to literals (no as const), keeps context mutable. Feed to definePart<typeof x> and withParts.
const checkout = actorSpec({ inputs: [submitBasicInfo, submitShipping], internal: [paymentOk, paymentFail], states: [basicInfo, shippingAddress, payment, submitting], initial: basicInfo, context: {} as CheckoutContext,});typeof checkout is the anchor for definePart. ActorSpec is the type of such a value; BuilderOf<ActorSpec> is its builder.
definePart(fn)
Section titled “definePart(fn)”function definePart<S extends ActorSpec = never>(fn: (m: BuilderOf<S>) => void): Fragment<S>;Wrap a slice of setup (m.on, m.onAny, m.effect) for its own file. Generic required: omit spec anchor and S defaults to never, so registering anything is a compile error.
const basicInfoPart = definePart<typeof checkout>((m) => { m.on(basicInfo, submitBasicInfo, (event, opts) => { const cur = opts.context.get(); cur.basicInfo = event.payload; opts.context.set(cur); return { state: shippingAddress }; });});Anchor with typeof <spec value>. Import spec type-only in part file, refs as value imports.
use(m, part)
Section titled “use(m, part)”function use<S extends ActorSpec>(m: BuilderOf<S>, part: Fragment<S>): void;Register a part inside hand-written setup.
setup: (m) => { use(m, basicInfoPart); use(m, submittingPart);};Not a React hook. Takes Fragments from definePart. Never annotate Part type by hand.
withParts(base, parts)
Section titled “withParts(base, parts)”function withParts< const States extends readonly AnyStateRef[], const Inputs extends readonly AnyEventRef[], const Internal extends readonly AnyEventRef[] = readonly [], const Outputs extends readonly AnyEventRef[] = readonly [], ActorContext = Record<string, unknown>,>( base: Omit<ActorOptions<States, Inputs, Internal, Outputs, ActorContext>, "setup">, parts: | Part<States, Inputs, Internal, Outputs, ActorContext> | readonly Part<States, Inputs, Internal, Outputs, ActorContext>[],): Actor<States, Inputs, Internal, Outputs, ActorContext>;Build actor from spec plus parts. Setup generated. Parts run against builder in order. base takes every option new Actor takes.
Parts array shares actor generics: Part<States, ...>. A part built against one spec cannot wire into another. For same (state, event), last registered handler wins, across parts and inline code. Effects append: every effect on a state runs on entry.
const actor = withParts(checkout, [basicInfoPart, submittingPart, backPart]);type ActorSpec; // shape of the static actor spec (states/inputs/.../initial)type BuilderOf<S>; // ActorBuilder for that spectype Fragment<S>; // (m: BuilderOf<S>) => voidtype Part<S, I, Int, O, C>; // (m: ActorBuilder<S, I, Int, O, C>) => void — withParts array element typeActorSpec is a constraint, not a value annotation: annotating with ActorSpec (or ActorOptions) erases literal types. Build with actorSpec, pass typeof <that value>. Never annotate the object itself.
onOutput(actor, handler)
Section titled “onOutput(actor, handler)”function onOutput(actor: AnyActor, handler: (event: InternalEvent) => void): void;Route actor emitted outputs to a handler. regions auto-wire child outputs to parent; ActorMap children do not. One call connects declared outputs to a receiver.
onOutput(childActor, (event) => { // handle emitted output});withPromise(promise, signal, emit, events)
Section titled “withPromise(promise, signal, emit, events)”function withPromise<T>( promise: Promise<T>, signal: AbortSignal, emit: EmitFn, events: { success: (data: T) => InternalEvent; error: (err: unknown) => InternalEvent; },): void;Promise to actor events. Checks signal.aborted.
withTimeout(ms, input, eventFn)
Section titled “withTimeout(ms, input, eventFn)”function withTimeout<ActorContext>( ms: number, input: EffectInput<ActorContext>, event: () => InternalEvent,): void;Timeout using actor’s clock. Checks abort signal.