Internal handle implementation. Drivers call the _mark* methods
to drive state transitions; consumers see only the readonly
DetachHandle interface.
A driver is the WHEN/HOW of the detach. Maps (child, input, refId)
to a DetachHandle whose lifecycle the driver owns.
Handle returned by chart.detachAndJoinLater(...) and
executor.detachAndJoinLater(...). Exposes status as PROPERTIES so
sync access is property-read (cheap, no allocation). The single
method wait() returns a Promise for opt-in async join.
Snapshot of a detached handle's state. Returned by handle.poll()
(when added in a follow-up; v1 exposes properties directly on
DetachHandle).
Result delivered when the handle's wait() Promise resolves
successfully (status reached 'done'). Rejection delivers the
native Error directly — no wrapping shape.
Capabilities a driver declares. Drives the runtime decision of "is this driver appropriate for the current environment?" Consumers inspect at construction; the framework doesn't enforce.
Function the driver calls to actually execute the child flowchart.
Returns a Promise resolving to the chart's terminal value (whatever
FlowChartExecutor.run() resolves with), or rejects on failure.
Default runner — instantiates a fresh FlowChartExecutor for each
child and awaits its run. Returns the executor's traversal result.
Default singleton — most consumers use this.
Ready-made singleton. Most consumers import this and pass it as the first
argument: executor.detachAndJoinLater(microtaskBatchDriver, child, input).
It is not an implicit default — nothing picks a driver for you.
Default singleton — zero-delay (next macrotask). For configurable
delays, use createSetTimeoutDriver({ delayMs }).
Type-narrowing helper — cast a public DetachHandle to its
implementation. Drivers (only) use this to call internal mutators.
Driver-facing factory. Drivers MUST use this to create handles
(NOT construct HandleImpl directly — keeps the impl type private).
Build an immediate driver wired to a custom child runner. Most
consumers want the default singleton immediateDriver.
Build a microtask-batch driver wired to a custom child runner. Most
consumers want the default singleton microtaskBatchDriver instead;
this factory exists for tests and for advanced consumers who want to
inject their own runner (e.g., a runner that wraps the child in a
tracing context).
Diagnostic — total live handles. Use sparingly; calling this on hot paths defeats the registry's "cheap insert/lookup" goal.
Wait for every in-flight detached handle to reach a terminal state.
Returns counts for diagnostics. PROCESS-WIDE — drains every driver
across every executor. For per-executor scoping, consumers should
collect their own handles from executor.detachAndJoinLater(...)
calls and await Promise.allSettled([...].map(h => h.wait()))
themselves.
Diagnostic — every live refId. Use for "what's still in flight?" dumps during shutdown or oncall debugging.
Look up a handle by refId. Returns undefined for unknown ids — the
caller decides whether that's an error or just a stale reference.
Fire-and-forget child charts and their drivers.
A library of scheduling drivers that lets you detach work from the parent stage's hot path. The parent stage returns immediately; the child runs on whichever driver you pick (microtask, immediate, setImmediate, setTimeout, sendBeacon or a worker thread).
Two shapes:
detachAndJoinLater(driver, child, input)— returns aDetachHandleyou canwait()on (Promise) or read.statusfrom (sync).detachAndForget(driver, child, input)— discards the handle. Use for fire-and-forget telemetry where the caller never needs to know how the child finished.Two entry points:
scope.$detachAndJoinLater(driver, child, input)— from inside a stage. refIds are minted from the calling stage's runtimeStageId for diagnostic correlation.executor.detachAndJoinLater(driver, child, input)— from outside any chart (consumer code). refIds use the synthetic__executor__prefix.Example