Actors/Packages/Cloudflare/API reference
@sigx/actors-cloudflare · Preview

API reference#

Exports of @sigx/actors-cloudflare v0.7.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.

TypeScript
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?:

TypeScript
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#

ExportRole
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
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#

TypeScript
// 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.

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, 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#