runtime:context

Values that follow the work rather than the call stack. run() makes a value current for a callback and everything that callback schedules; get() reads it back, however deep and however many awaits later.

Capability: none, on any export

The only runtime: module with no gate anywhere in it. No ambient authority, no I/O, nothing observable that was not already in the caller's own scope. Status: Available.

Import

JavaScript
import { createContext, snapshot, bind, withTrace, currentTask } from "runtime:context";

const tenant = createContext({ name: "tenant", defaultValue: null });

await tenant.run("acme", async () => {
  await somethingSlow();
  tenant.get(); // "acme"
});

tenant.get(); // null

Exports

ExportTypeDescriptionExample
createContext(options?)({ name?, defaultValue? }) => ContextA context keyed by its own object identity. name is a label, never an identity key.createContext({ name: "user" })
snapshot()() => (fn, ...args) => RCaptures the current mapping; the returned function runs fn under it.const run = snapshot(); run(job)
bind(fn)(fn) => fnPins fn to the mapping current now, forwarding this.el.addEventListener("x", bind(h))
withTrace(traceId, fn)(string, fn) => RRuns fn under an explicit W3C trace id.withTrace(msg.traceId, handle)
currentTask()() => TaskInfoThe executing task.currentTask().traceId
defaultobjectAn aggregate of all named exports.

Context

MemberTypeDescriptionExample
namestring | undefinedThe label passed to createContext.tenant.name // "tenant"
get()() => TThe value current in this scope, or defaultValue outside any run().tenant.get()
run(value, fn, ...args)(T, fn, ...args) => RRuns fn with value current. Returns what fn returned, promise included.tenant.run("acme", load)

TaskInfo

FieldTypeDescription
idnumberThis task's id. Node's executionAsyncId().
parentIdnumber | nullThe task that scheduled this one; null at the root. Node's triggerAsyncId().
traceIdstring32 lowercase hex, W3C trace-id. Read-only — withTrace is the only override.
kindstring"main", "http-request" or "worker".

Propagation

Captured where work is scheduled, restored when the continuation runs.

BoundaryPropagates
await, .then(), promise combinatorsYes
queueMicrotaskYes
setTimeout / setIntervalYes — captured when armed; an interval reuses that one capture
runtime:* op callbacksYes — captured when the op is issued
EventTarget dispatchNo — the listener runs in the dispatcher's mapping
A spawned WorkerNo — every context starts at its defaultValue

EventTarget is the one a Node habit gets wrong. Wrap the listener to opt in:

JavaScript
target.addEventListener("message", bind(onMessage));

A scope is copy-on-write, so a write in one branch is invisible to a concurrent sibling and to the parent.

Trace ids

runtime:http mints one per inbound request. Outside a request one is minted lazily on first read and shared by everything under the agent's root.

JavaScript
import { serve } from "runtime:http";
serve({ port: 8080 }, () => new Response(currentTask().traceId));

An inbound traceparent is ignored unless the server sets trustTraceHeaders: true. A worker gets its own trace; to continue one, send the id and adopt it:

JavaScript
self.addEventListener("message", (e) =>
  withTrace(e.data.traceId, () => run(e.data.job)));

Coming from Node

NodeHere
new AsyncLocalStorage()createContext({ name })
.getStore().get()
.run(store, fn).run(value, fn)
AsyncLocalStorage.snapshot()snapshot()
AsyncLocalStorage.bind(fn)bind(fn)
executionAsyncId()currentTask().id
triggerAsyncId()currentTask().parentId
.enterWith()Removed — snapshot()
.exit(fn)Removed — ctx.run(undefined, fn)
.disable()Removed
AsyncResourceRemoved

Errors

ErrorWhen
TypeErrorrun, snapshot()(…), bind or withTrace given a non-function.
TypeErrorwithTrace given anything but a W3C trace-id (32 hex characters, not all zero).
TypeErrorcreateContext given a non-string name.
Last updated on
Edit this page