API reference
Exports of @sigx/actors-cloudflare v0.11.0.
Assembled entries
Most apps use these two rather than the seams below.
createHostDurableObject<Env>(options)
Returns a class to extend. The object hosts exactly the actor its id names.
interface HostDurableObjectOptions<Env> {
actors?: AnyActorDefinition[];
namespace(env: Env): DurableObjectNamespaceLike;
app?(base: DurableAppOptions): ActorApp;
placement?: Pick<DurableObjectPlacementOptions,
'objectName' | 'locationHint' | 'jurisdiction' | 'base'>;
endpoint?: HostEndpointOptions;
/** Terminate client sockets inside this object. */
socket?: Omit<ActorSocketSessionOptions,
'host' | 'request' | 'send' | 'close' | 'pingMs'> & { path?: string };
}
The instance exposes fetch(request), alarm() and host(): Promise<Host>.
The app factory receives the object's own storage, reminders and defaults and must pass them
on. It never receives env, so it cannot reach a namespace binding and build a placement
of its own.
createWorkerHandler<Env>(options)
The edge. Hosts nothing, routes everything. Same options minus endpoint, plus
fetch?: FetchHandlerOptions and socket?:
socket?:
| (WorkerSocketOptions & { terminate?: 'worker' })
| { terminate: 'object'; path?: string };
A discriminated union on purpose: in 'object' mode the session runs inside the Durable
Object, so the type refusing session options on the Worker side is what stops them being
configured where they would never run. See
client sockets.
Seams
| Export | Role |
|---|---|
durableObjectStorage(storage, options?) | ActorStorage on DO storage |
durableObjectReminders(options) | ActorReminders on the DO alarm |
durableObjectPlacement(options?) | routes a ref to its object |
durableObjects(options) | the placement as a plugin; options (with namespace) is required. Carries never as its placement type: an app that .use(durableObjects(...)) cannot declare placement on its actors |
durableObjectsHosted() | a type-only plugin for the app factory — installs nothing, carries never as its placement type, so placement: on an actor is a compile error |
durableObjectName(...) | the ref → object-name function |
unhostedStorage() | storage for the Worker half, which hosts nothing |
durableObjectStubResolver(...) | the ref → stub derivation, shared by placement and the forwarding socket route so the two cannot disagree about where an actor lives |
workerSocket(options?) | the Worker-terminated socket route, as a plugin |
objectSocketRoute(options) | the forwarding half of the object-terminated mode |
parseSocketActorPath(pathname, path?) | {path}/{type}/{key} → the actor it names |
Placement runs on both sides
// inside the object
durableObjectPlacement({ isSelf: (ref) => actorId(ref) === state.id.name });
// in the Worker — no isSelf; everything is remote
durableObjectPlacement();
Using the plain local host inside an object silently corrupts state: a cross-actor call would activate the callee inside the caller's object, writing its record into the wrong storage and violating single activation. See Cloudflare Workers.
locationHint is a hint and safe to change. jurisdiction and objectName are part of
identity, so changing either is a state migration.
No per-actor placement
Durable Objects read no placement strategy. durableObjects() and durableObjectsHosted() are
both ActorPlugin<Record<never, never>, never>, so the app-bound defineActor of an app that
installs either refuses placement at compile time — omitting it (or placement: undefined)
is the only spelling left.
What the types cannot see — the bare defineActor from @sigx/actors, a packaged actor,
JavaScript — meets the runtime floor in durableObjectPlacement(): a strategy tagged
backend: 'cluster' throws at dispatch, naming the type and the strategy, and any other
declared strategy logs one __DEV__ warning per type.
Alarm concurrency
onAlarm() runs in three phases and does not hold blockConcurrencyWhile across
delivery: claim and persist (gated) → deliver (ungated) → re-read and re-arm (gated).
Holding the gate across delivery deadlocked the object, because rescheduling from inside
onReminder takes the gate itself and blockConcurrencyWhile does not nest.
Relatedly, an expected failure — the wrong-owner error from ctx.reminders.set() — is
returned as a value and thrown after the gate closes. An exception escaping
blockConcurrencyWhile resets the Durable Object.
Types
DurableObjectIdLike, DurableObjectNamespaceLike, DurableObjectStubLike,
DurableObjectStorageOptions, DurableObjectRemindersOptions,
DurableObjectPlacementOptions, DurableAppOptions, DurableObjectStateLike,
HostDurableObjectInstance, WorkerHandler, WorkerHandlerOptions, BlockConcurrencyWhile,
DurableStorage (its optional list({ prefix }) enables the
append path), DurableAlarms, DurableObjectStubResolver, WorkerSocketOptions,
ObjectSocketRouteOptions, CloudflareWebSocketLike, DurableWebSocketLike.
DurableObjectStateLike carries the hibernation members — acceptWebSocket,
setWebSocketAutoResponse and friends — as optional, so a runtime without them still
type-checks.
Next steps
- Overview — the two seams.
- Client sockets — the two termination modes.
- Installation — bindings and migrations.
- Cloudflare Workers — the deployment guide.
