API reference › @evolu/common › Task
JavaScript-native structured concurrency.
Structured concurrency organizes running tasks into a tree. Every child belongs to a parent, a parent waits for its children before it completes, and abort propagates from parents to descendants. Races and fail-fast control flow abort siblings that are no longer needed.
With plain AbortController code, these guarantees depend on call-site
discipline: someone must remember the finally that aborts started tasks and
the await that waits for cleanup. Evolu makes both structural: run(task)
registers every child before it starts, and the parent Run settles
only after child cleanup finishes.
Evolu implements structured concurrency with:
- A Task is a function passed to Run that returns an Awaitable Result and declares its dependencies.
- A Run starts Tasks and owns their lifetimes.
- A Fiber is the Promise-backed handle returned when a Run starts a Task.
- An AbortableFiber adds explicit abort and async disposal.
Together, these APIs provide abort, cleanup, defect handling, dependency injection, monitoring, and resource management.
Tasks return a Result containing either success or a domain error. Abort is control flow represented by AbortError. If a Task throws or rejects with anything else, that is a defect: the root Run reports it and shuts down its tree so code does not continue in a potentially invalid state.
import {
assertOk,
assertType,
createRun,
err,
ok,
type Result,
type Task,
type Typed,
} from "@evolu/common";
interface User {
readonly id: string;
readonly name: string;
}
interface Db {
readonly usersById: ReadonlyMap<string, User>;
}
interface DbDep {
readonly db: Db;
}
const getUser =
(id: string): Task<User, UserNotFoundError, DbDep> =>
(run) => {
const user = run.deps.db.usersById.get(id);
return user ? ok(user) : err({ type: "UserNotFound", id });
};
// Typed declares the `type` discriminant without repeating the property.
interface UserNotFoundError extends Typed<"UserNotFound"> {
readonly id: string;
}
const user: User = { id: "user-1", name: "Ada" };
// Provide dependencies at the composition root. `await using` disposes the
// Run and waits for its child Tasks before leaving this scope.
await using run = createRun({
db: { usersById: new Map([[user.id, user]]) },
});
const result = await run(getUser(user.id));
assertType<Result<User, UserNotFoundError>, typeof result>();
assertOk(result, user);
In composition roots, prefer the lifecycle API from the matching Evolu platform package:
- Node.js:
@evolu/nodejs - Web:
@evolu/web - React Native:
@evolu/react-native
Composition
| Category | Helper | Description |
|---|---|---|
| Collection | all | Return Ok values or stop on first Err |
| allSettled | Return every Task Result | |
| each | Handle each Task Result | |
| Interop | callback | Wrap callback APIs |
| fetch | Native fetch with bounded Response use | |
| Timing | sleep | Pause execution |
| timeout | Time-bounded execution | |
| Resilience | retry | Retry domain errors with a schedule |
| Repetition | repeat | Repeat successes with a schedule |
| Racing | any | First Ok wins |
| race | First settled Result wins | |
| firstN | First n Ok values win | |
| firstNSettled | First n Results win | |
| Scheduling | prioritized | Assign scheduler priority |
| yieldNow | Yield to the host scheduler | |
| Lifetime | daemon | Run under root ownership |
| acquireUseRelease | Bracket acquire, use, and release | |
| Abortability | unabortable | Mask abort after a Task starts |
| unabortableMask | Mask abort and selectively restore it |
Helpers that process multiple Tasks run sequentially by default. Use a
concurrency option to run more than one Task at a time.
Sequential composition
For ordinary sequential composition, use imperative code:
import {
assertOk,
assertType,
createRun,
err,
ok,
type Result,
type Task,
type Typed,
} from "@evolu/common";
interface User {
readonly id: string;
readonly profileId: string;
}
interface Profile {
readonly id: string;
}
const getUser =
(id: string): Task<User, UserNotFoundError> =>
() =>
id === "user-1"
? ok({ id, profileId: "profile-1" })
: err({ type: "UserNotFound", id });
interface UserNotFoundError extends Typed<"UserNotFound"> {
readonly id: string;
}
const getProfile =
(id: string): Task<Profile, ProfileNotFoundError> =>
() =>
id === "profile-1" ? ok({ id }) : err({ type: "ProfileNotFound", id });
interface ProfileNotFoundError extends Typed<"ProfileNotFound"> {
readonly id: string;
}
const getUserWithProfile =
(
id: string,
): Task<
{ readonly user: User; readonly profile: Profile },
UserNotFoundError | ProfileNotFoundError
> =>
async (run) => {
const user = await run(getUser(id));
if (!user.ok) return user;
const profile = await run(getProfile(user.value.profileId));
if (!profile.ok) return profile;
return ok({ user: user.value, profile: profile.value });
};
await using run = createRun();
const result = await run(getUserWithProfile("user-1"));
assertType<
Result<
{ readonly user: User; readonly profile: Profile },
UserNotFoundError | ProfileNotFoundError
>,
typeof result
>();
assertOk(result, {
user: { id: "user-1", profileId: "profile-1" },
profile: { id: "profile-1" },
});
Evolu intentionally avoids pipe APIs, chainable methods, and generator-based effect DSLs. Plain async/await with early returns is easier to read, review, and debug, and it lets TypeScript narrow Result values through ordinary control flow.
Resilient fetch
fetch with a body mode already returns a plain value, so resilience is ordinary Task composition. Combine timeout and retry to bound each attempt and retry recoverable domain errors:
import {
assertType,
exponential,
fetch,
jitter,
maxDelay,
retry,
take,
timeout,
type FetchError,
type RetryTaskError,
type Task,
type TimeoutError,
} from "@evolu/common";
const fetchWithRetry = (url: string) =>
retry(
timeout(fetch(url, "text"), "30s"),
// A jittered, capped, limited exponential backoff.
jitter("100%")(maxDelay("20s")(take(2)(exponential("100ms")))),
);
assertType<
Task<string, RetryTaskError<FetchError | TimeoutError>>,
ReturnType<typeof fetchWithRetry>
>();
Concurrent composition
Run composed Tasks with a concurrency option and all:
import {
assertEqual,
assertOk,
all,
createRun,
ok,
sleep,
type Task,
} from "@evolu/common";
await using run = createRun();
const urls = [
"https://api.example.com/users",
"https://api.example.com/posts",
"https://api.example.com/comments",
];
let activeRequests = 0;
let maxActiveRequests = 0;
const fetchUrl =
(url: string): Task<string> =>
async (run) => {
activeRequests += 1;
maxActiveRequests = Math.max(maxActiveRequests, activeRequests);
await run.ok(sleep("1ms"));
activeRequests -= 1;
return ok(url);
};
// At most 2 concurrent requests.
const result = await run(all(urls, fetchUrl, { concurrency: 2 }));
assertOk(result, urls);
assertEqual(maxActiveRequests, 2);
Task helpers compose Tasks; concurrency primitives are stateful objects that
coordinate Tasks across call sites. Create them with their createX
factories and share them where coordination is needed.
| Primitive | Description |
|---|---|
| Deferred | One-shot value resolved from outside |
| Gate | Block and release Tasks repeatedly |
| Semaphore | Limit concurrent Tasks with permits |
| Mutex | Run Tasks one at a time |
| SemaphoreByKey | Per-key permits with automatic cleanup |
| MutexByKey | Per-key one-at-a-time execution |
| MutexRef | Ref with serialized Task transitions |
Dependency injection
Task DI is
Evolu Pure DI
applied to Run. A Task declares required capabilities with its
D type parameter and reads them from Run.deps.
createRun supplies dependencies to the root Run and its children. A
Run can also start one child Task with runtime-created dependencies by
calling run(task, deps), where deps is checked as RunCustomDeps.
Use normal Task arguments for per-call values and D for capabilities,
resources, or services shared by all code running inside a Run.
import { assertOk, createRun, ok, type Task } from "@evolu/common";
interface GreetingFormatter {
readonly format: (name: string) => string;
}
interface GreetingFormatterDep {
readonly greetingFormatter: GreetingFormatter;
}
const greet =
(name: string): Task<string, never, GreetingFormatterDep> =>
(run) =>
ok(run.deps.greetingFormatter.format(name));
const formal: GreetingFormatter = {
format: (name) => `Hello, ${name}`,
};
const casual: GreetingFormatter = {
format: (name) => `Hi, ${name}`,
};
await using run = createRun({ greetingFormatter: formal });
// Root dependencies are inherited.
assertOk(await run(greet("Ada")), "Hello, Ada");
// Child-specific dependencies replace the root's custom dependencies.
assertOk(await run(greet("Ada"), { greetingFormatter: casual }), "Hi, Ada");
Default dependencies
createRun provides default RunDefaultDeps available to all
Tasks without declaring D:
- Console — logging with hierarchical context via
child() - LeakDetector — development-time leaked-handle detection
- NativeFetch — WHATWG-compatible native fetch
- Random — random number generation
- RandomBytes — cryptographic random bytes
- ReportDefect — defect reporting
- Time — current time
For testing, use testCreateRun to get deterministic, controllable implementations of all RunDefaultDeps.
Resource management
JavaScript provides standard resource management. Evolu adds DisposableRun.defer for closure-held state owned by a reusable Run.
Choose the ownership primitive by where the resource is reachable:
- Synchronous stack frame:
usingor DisposableStack - Async Task stack frame:
await usingor AsyncDisposableStack - Closure-held state bounded by a reusable DisposableRun: DisposableRun.defer
Returning resources from Tasks
A Task that successfully returns a disposable resource transfers ownership of a live resource to its caller. The resource must remain live after the Task settles. Do not register its disposal with the creating Task's DisposableRun.defer, because that child Run is disposed when the Task settles.
Use AsyncDisposableStack while creating a resource. On a Result error,
abort, or defect, stack unwinding disposes partially created resources. On
success, AsyncDisposableStack.move transfers ownership to the returned
resource. A recoverable creation failure should be a typed Result error;
undefined should represent valid absence, not failure.
import {
assertEqual,
assertFalse,
assertTrue,
createRun,
ok,
type Task,
type Typed,
} from "@evolu/common";
interface Socket extends AsyncDisposable {
readonly send: (message: string) => string;
}
interface Connection extends AsyncDisposable {
readonly send: (message: string) => string;
}
let socketDisposed = false;
const openSocket: Task<Socket, ConnectionFailedError> = () =>
ok({
send: (message) => message,
[Symbol.asyncDispose]: () => {
socketDisposed = true;
return Promise.resolve();
},
});
interface ConnectionFailedError extends Typed<"ConnectionFailed"> {}
const handshake =
(_socket: Socket): Task<void, ConnectionFailedError> =>
() =>
ok();
const createConnection: Task<Connection, ConnectionFailedError> = async (
run,
) => {
await using disposer = new AsyncDisposableStack();
const socketResult = await run(openSocket);
if (!socketResult.ok) return socketResult;
const socket = disposer.use(socketResult.value);
const handshakeResult = await run(handshake(socket));
if (!handshakeResult.ok) return handshakeResult;
const disposables = disposer.move();
return ok({
send: (message) => socket.send(message),
[Symbol.asyncDispose]: () => disposables.disposeAsync(),
});
};
await using run = createRun();
const result = await run(createConnection);
assertTrue(result.ok);
assertFalse(socketDisposed);
assertEqual(result.value.send("hello"), "hello");
await result.value[Symbol.asyncDispose]();
assertTrue(socketDisposed);
Use Run.ok with await using when a Task whose error type is never
returns a disposable value. Use acquireUseRelease when acquisition and
release are separate steps rather than a disposable value.
Awaitable
A Task returns Awaitable, so its body may produce a Result immediately or asynchronously. Run is always async and returns a Fiber; callers use the same ownership model either way.
- Sync → Result, native
using/DisposableStack - Async → Task, Run, Fiber,
await using/AsyncDisposableStack
A Task is an async ownership boundary, not a general unit of program
decomposition. Calling run(task) always creates a child Run by design. Use
a plain async function when it does not need its own Run.
A unified sync/async effect API is technically possible. It can detect Promise-like values with isPromiseLike, dispose synchronous resources first, continue with asynchronous disposal when necessary, and track whether callers must await the result. Evolu deliberately keeps the two models separate instead: plain functions and Result for synchronous code, Task and Run for asynchronous ownership. Most effects involve inherently asynchronous I/O, while synchronous code benefits from a simpler API and no Task overhead.
Keep synchronous computation as plain functions returning Result. Prefer passing values rather than dependencies, following the impure/pure/impure sandwich pattern where impure code gathers data, pure functions process it, and impure code performs effects with the result. For example, a pure function can accept a RandomNumber value instead of depending on Random.
Large CPU-bound computations, such as parsing large JSON, sorting millions of items, or complex cryptography, belong in a worker. Model the asynchronous call to that worker as a Task so Run can provide timeout, abort, cleanup, and monitoring.
Glossary
- Defect — a thrown or rejected value other than AbortError, rather than a declared Result error.
- Outcome — a Fiber's settlement: resolution with the Task Result, or rejection with AbortError. The original defect is reported through ReportDefectDep whether or not the Fiber is observed; the Fiber boundary represents the panic with AbortError whose reason is PanicAbortReason.
- Create — construct a new value or a resource.
- Acquire — obtain a usable resource. Acquisition may create a new resource, borrow one, open one, or take a lease/lock.
- Release — relinquish a previously acquired resource or lease. Release pairs with acquire and need not mean disposal; examples include unlock, logout, or returning a pooled resource.
- Dispose / disposal — owner-driven resource finalization via JavaScript
resource management (
Symbol.dispose,Symbol.asyncDispose,using,AsyncDisposableStack).
FAQ
Why is AbortError not part of every Task error type?
The E type parameter represents declared domain errors. Abort is
structured-concurrency control flow, not a domain error. A direct run(task)
rejects with AbortError when the Task observes abort. Use
run.abortable(task) when abort should be handled as an ordinary
Result error at the Fiber boundary, or daemon(task) when waiting for
a Task should stop immediately after abort.
Do I have to await every Fiber?
No. Awaiting a Fiber is join: it makes the child outcome part of the current
control flow. When the outcome does not matter — a fire-and-forget side
effect — discard the Fiber explicitly with void run(task).
That is safe because the Run tree supervises every Fiber it creates. A discarded Fiber whose Task observes abort (for example during Run disposal) never surfaces as an unhandled rejection, and cleanup is not lost — disposal already aborts and awaits the child. Defects are different: they still panic the root Run and are reported through ReportDefectDep, so discarding a Fiber never hides bugs.
Choose the boundary explicitly:
void run(task)— the outcome does not matter. Abort is silent; defects are still reported.await run(task)— the continuation depends on the Result, so abort rejects into the awaiter and the boundary must handle it.run.abortable(task)— abort is an expected outcome handled as a Result error.
What should Task code do with defects?
Nothing. Once a defect reaches the Run, it is too late: the root Run panics, running Tasks are aborted, and the Run tree shuts down. Use trySync or tryAsync to turn recoverable exceptions and Promise rejections into typed Result errors. Let unrecoverable failures propagate as defects.
Why does a defect panic the whole Run tree?
The obvious alternative is partial recovery: only the failing subtree shuts down or restarts while the rest keeps running. Erlang/OTP made this "let it crash" model with supervisors the benchmark for fault-tolerant runtime design.
Erlang can recover partially because of process isolation: each process owns its heap, so a crashed process cannot leave another process's state corrupted. JavaScript Tasks share a heap. A defect may throw after partially updating shared state, and the Run cannot prove which invariants are still valid. A subtree panic would stop the failing Task while leaving any corrupted shared state available to surviving Tasks. Locks make it worse: a defect inside a critical section may leave protected invariants half-updated. In-process restart is not a reliable recovery boundary either, because the restarted code may still share the same module state, closures, caches, or resources.
JavaScript does have a boundary with Erlang-like isolation: workers. A worker has its own heap and structured-clone messaging, so corruption cannot cross the boundary, and respawning a worker starts from clean state. A defect can panic the worker's Run tree, the worker boundary can be torn down, and the supervising side decides whether to respawn — retry with a Schedule around a "spawn worker, run until exit" Task is a one-for-one supervisor. Multiple root Runs that share no mutable state are a lighter alternative, but the share-nothing guarantee is then architectural discipline rather than enforced isolation, so keep it opt-in and rare.
Why imperative code instead of monadic effect composition?
Monads give pure functional languages a way to sequence effects while keeping
functions pure. JavaScript already has native effect sequencing: loops, early
returns, try/finally, exceptions, and async/await.
A monadic effect wrapper moves that control flow into a library DSL. The wrapper type becomes viral, and ordinary debugging, profiling, stack traces, and TypeScript narrowing have to work through the DSL instead of the language.
Task follows the opposite approach: Tasks are ordinary async functions, Run owns lifetimes and scoped context, Result carries expected domain errors, and defects keep real exceptions with real stacks. Result propagation is explicit at each async boundary, so TypeScript narrows it through ordinary control flow and readers can see where an error is handled or returned.
Are recursive Tasks stack-safe?
Tasks have native JavaScript stack behavior. A deeply recursive Task can
exceed the call stack when each step starts the next step synchronously.
await alone does not prevent this: JavaScript evaluates its operand before
suspending, and run(nextTask) starts the child Task immediately.
Implement deep recursive algorithms with a loop and an explicit worklist so each iteration reuses the same stack frame:
import { assertOk, createRun, ok, type Task } from "@evolu/common";
interface TreeNode {
readonly value: string;
readonly children: ReadonlyArray<TreeNode>;
}
const visitTree =
(root: TreeNode): Task<ReadonlyArray<string>> =>
() => {
const remaining = [root];
const visited: Array<string> = [];
while (remaining.length > 0) {
const node = remaining.pop();
if (!node) continue;
visited.push(node.value);
for (const child of node.children) remaining.push(child);
}
return ok(visited);
};
await using run = createRun();
assertOk(
await run(
visitTree({
value: "root",
children: [{ value: "child", children: [] }],
}),
),
["root", "child"],
);
Task favors direct native execution, async/await, and native tooling over
interpreted control flow. The trade-off is no transparent stack safety or
automatic scheduling fairness. Use loops or worklists for deep algorithms,
periodically await yieldNow for cooperative scheduling, and move
CPU-bound work to a worker.
Where are fork and join?
Calling run(task) is fork: it starts a child Task and returns a
Fiber. Awaiting or returning that Fiber is join: it makes the child
Result or rejection part of the parent Task control flow.
What runtime features does Task require?
Task uses modern JavaScript APIs such as Promise.withResolvers,
AbortSignal.throwIfAborted, Symbol.dispose, Symbol.asyncDispose,
DisposableStack, and AsyncDisposableStack. Evolu provides polyfills for
supported runtimes that need them: call installPolyfills from
@evolu/common/polyfills, or from the platform package such as
@evolu/react-native/polyfills. The using and await using syntax is
emitted by TypeScript; the polyfills provide the runtime resource-management
globals.
Core
| Name | Description |
|---|---|
| AbortableFiber | A Fiber with explicit abort and async-disposal controls. |
| AbortError | Structured-concurrency abort control-flow value. |
| AbortReason | Structured data explaining why a Run was aborted. |
| DisposableRun | A Run with explicit disposal. |
| Fiber | A Promise-backed handle to a Task started by a Run. |
| PanicAbortReason | Abort reason recorded when a defect panics the root Run. |
| Run | A callable object that starts Tasks and owns their lifetimes. |
| RunAbortState | Run abort state. |
| RunStateAborted | The Run has an abort request and may still be running or disposing. |
| RunStateRunning | The Run has no abort request, observed abort, or exit. |
| RunStateSettled | The Run has recorded its final outcome and all descendants have settled. |
| NextTask | A Task that can return a value, signal done, or return a Result error. |
| RunCustomDeps | Custom deps accepted by Run APIs. |
| RunExit | Final outcome recorded by a Run. |
| RunState | Run lifetime states. |
| Task | A function passed to Run that returns an Awaitable Result and declares its dependencies through D. |
| AbortError | Runtime Type for structured-concurrency abort control flow. |
| AbortReason | Runtime Type for structured data explaining why a Run was aborted. |
| createAbortError | Creates an AbortError from an AbortReason. |
| createPanicAbortReason | Creates a PanicAbortReason from a defect. |
Run
| Name | Description |
|---|---|
| CreateRun | Factory type for creating root DisposableRun instances. |
| ReportDefectDep | Dependency wrapper for ReportDefect. |
| ReportDefect | Reports a defect. |
| RunDefaultDeps | Default dependencies provided by createRun. |
| createRun | Creates a root DisposableRun. |
| explicitAbortReason | Shared abort reason used when callers explicitly request abort without a more specific reason. |
| reportDefectAfterMicrotask | Default ReportDefect for platform-independent createRun. |
| runDisposedAbortReason | Shared abort reason used for ordinary Run cleanup. |
| createRunDefaultDeps | Creates RunDefaultDeps. |
Collection
| Name | Description |
|---|---|
| AllOptions | Options for all. |
| TaskCollectionOptions | Options shared by Task collection helpers. |
| EachCallback | Handles one settled Task Result from each. |
| EachDecision | Decision returned by an each result handler. |
| all | Runs Tasks until all return Ok or one returns Err. |
| allSettled | Runs all Tasks and returns every Task Result. |
| each | Runs Tasks under a concurrency limit and calls onResult for each Task Result as it settles. |
Lifetime
| Function | Description |
|---|---|
| acquireUseRelease | Runs acquire, use, and release as one bracketed Task. |
| daemon | Starts a Task with Run.daemon and waits until it settles or the current Run aborts. |
Interop
| Function | Description |
|---|---|
| callback | Creates a Task from a callback-based API. |
Timing
| Name | Description |
|---|---|
| TimeoutError | Error returned by timeout when a Task exceeds its duration. |
| timeoutError | The TimeoutError instance returned by timeout. |
| TimeoutError | Runtime Type for the error returned by timeout when a Task exceeds its duration. |
| sleep | Pauses execution for a specified PositiveDuration. |
| timeout | Limits how long a Task may run. |
Resilience
| Name | Description |
|---|---|
| RetryAttempt | Information passed to RetryOptions.onRetry. |
| RetryError | Error returned by retry when retrying stops after a domain error. |
| RetryOptions | Options for retry. |
| RetryTaskError | Error type returned by retry. |
| retry | Retries a Task according to a Schedule. |
Repetition
| Name | Description |
|---|---|
| RepeatAttempt | Information passed to RepeatOptions.onRepeat. |
| RepeatOptions | Options for repeat. |
| repeat | Repeats a Task according to a Schedule. |
Racing
| Function | Description |
|---|---|
| any | Runs Tasks until one returns Ok or all return Err. |
| firstN | Runs Tasks until count Tasks return Ok or all Tasks settle. |
| firstNSettled | Runs Tasks until count Tasks settle or all Tasks settle. |
| race | Runs Tasks until the first Task settles. |
Scheduling
| Name | Description |
|---|---|
| TaskPriority | Scheduler priority for Tasks started through a native scheduler. |
| yieldNow | Yields execution to the host scheduler. |
| prioritized | Assigns static scheduler priority to a Task. |
Abortability
| Name | Description |
|---|---|
| AbortMask | Abort mask depth for a Run. |
| unabortable | Makes a Task unabortable after it starts. |
| waitForAbort | Waits until the current Run aborts, then rejects with its AbortError. |
| unabortableMask | Like unabortable, but provides restore for child Tasks that should run with the previous abort mask. |
Concurrency primitives
| Name | Description |
|---|---|
| CreateMutexByKeyOptions | Options for createMutexByKey. |
| CreateSemaphoreByKeyOptions | Options for createSemaphoreByKey. |
| Deferred | A one-shot value resolved from outside the waiting Task. |
| Gate | A reusable gate for blocking and releasing Tasks. |
| Mutex | Runs Tasks one at a time. |
| MutexByKey | Runs Tasks one at a time independently for each key, like Mutex. |
| MutexRef | Ref protected by a Mutex. |
| Semaphore | Coordinates concurrent Tasks by acquiring and releasing permits. |
| SemaphoreByKey | Coordinates concurrent Tasks independently for each key. |
| SemaphorePermit | An owned semaphore acquisition returned by Semaphore.take. |
| SemaphoreSnapshot | Snapshot returned by Semaphore.snapshot. |
| SemaphorePolicy | Scheduling policy for semaphore acquisition. |
| createDeferred | Creates a Deferred. |
| createGate | Creates a Gate. |
| createMutex | Creates a Mutex. |
| createMutexByKey | Creates a MutexByKey. |
| createMutexRef | Creates a MutexRef. |
| createSemaphore | Creates a Semaphore. |
| createSemaphoreByKey | Creates a SemaphoreByKey. |
Monitoring
| Name | Description |
|---|---|
| RunConfig | Configuration for Run monitoring behavior. |
| RunConfigDep | Dependency wrapper for RunConfig. |
| RunEvent | Event emitted by a Run for monitoring and debugging. |
| RunEventDataChildAdded | A child Run was added to the emitting Run. |
| RunEventDataChildRemoved | A child Run was removed from the emitting Run. |
| RunEventDataStateChanged | The emitting Run changed state. |
| RunSnapshot | Recursive snapshot of a Run tree. |
| RunEventData | Event-specific payload of a RunEvent. |
Testing
| Name | Description |
|---|---|
| TestReportDefect | Test ReportDefect that records reported defects. |
| TestReportDefectDep | Dependency wrapper for TestReportDefect. |
| TestRunDep | Provides a test Run with deterministic default dependencies. |
| TestRunDefaultDeps | Deterministic test variants of RunDefaultDeps. |
| testAbortError | Shared AbortError for tests, created from testAbortReason. |
| testAbortReason | Shared abort reason for tests that need a non-production abort reason. |
| testCreateDeps | Creates TestRunDefaultDeps. |
| testCreateReportDefect | Creates TestReportDefect. |
| testCreateRun | Creates a root DisposableRun with TestRunDefaultDeps. |
Type utilities
| Type Alias | Description |
|---|---|
| AnyFiber | Shorthand for a Fiber with any type parameters. |
| AnyTask | Shorthand for a Task with any type parameters. |
| InferFiberDeps | Extracts the dependency type from a Fiber. |
| InferFiberErr | Extracts the Result error type from a Fiber. |
| InferFiberOk | Extracts the Ok value type from a Fiber. |
| InferTaskDeps | Extracts the dependency type from a Task. |
| InferTaskDone | Extracts the done value type from a NextTask. |
| InferTaskErr | Extracts the Result error type from a Task. |
| InferTaskOk | Extracts the Ok value type from a Task. |
| InferTaskRecordDeps | Extracts the dependency intersection required by a Task record. |
| InferTasksDeps | Extracts the dependency intersection required by a readonly Task array. |
| InferTasksOk | Maps a Task array or record to the Ok values produced by its Tasks. |
| InferTasksResult | Extracts the Result type produced by one Task in a non-empty Task array. |
| InferTasksSettled | Maps a Task array or record to the Result values produced by its Tasks. |
| TaskRecord | A readonly record whose values are Tasks. |
| TaskWithError | A Task whose error type is not never. |