import type * as cf from "@cloudflare/workers-types";
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
import type { RuntimeContext } from "../../RuntimeContext.ts";
import {
fromDurableObjectStorage,
type DurableObjectStorage,
} from "./DurableObjectStorage.ts";
import { fromWebSocket, type WebSocket } from "./WebSocket.ts";
export class DurableObjectState extends Context.Service<
DurableObjectState,
{
readonly id: cf.DurableObjectId;
readonly storage: DurableObjectStorage;
container?: cf.Container;
/**
* Run an Effect in the background without blocking the current event,
* keeping the Durable Object alive until it settles. The Effect runs with
* the caller's full context (services, tracing), and the resulting
* promise is registered with workerd's `state.waitUntil`.
*/
waitUntil(
effect: Effect.Effect,
): Effect.Effect;
/**
* The raw workerd DurableObjectState, for interop with async APIs.
*/
readonly raw: cf.DurableObjectState;
blockConcurrencyWhile(
callback: () => Effect.Effect,
): Effect.Effect;
acceptWebSocket(
ws: WebSocket,
tags?: string[],
): Effect.Effect;
getWebSockets(
tag?: string,
): Effect.Effect;
setWebSocketAutoResponse(
maybeReqResp?: cf.WebSocketRequestResponsePair,
): Effect.Effect;
getWebSocketAutoResponse(): Effect.Effect<
cf.WebSocketRequestResponsePair | null,
never,
RuntimeContext
>;
getWebSocketAutoResponseTimestamp(
ws: cf.WebSocket,
): Effect.Effect;
setHibernatableWebSocketEventTimeout(
timeoutMs?: number,
): Effect.Effect;
getHibernatableWebSocketEventTimeout(): Effect.Effect<
number | null,
never,
RuntimeContext
>;
getTags(ws: cf.WebSocket): Effect.Effect;
abort(reason?: string): Effect.Effect;
}
>()("Cloudflare.DurableObjectState") {}
export const fromDurableObjectState = (
state: cf.DurableObjectState,
): DurableObjectState["Service"] => ({
id: state.id,
container: state.container,
storage: fromDurableObjectStorage(state.storage),
raw: state,
waitUntil: (effect: Effect.Effect) =>
Effect.gen(function* () {
const context = yield* Effect.context();
// Register the promise with workerd un-awaited — waitUntil extends the
// event's lifetime without blocking the caller.
yield* Effect.sync(() =>
state.waitUntil(
Effect.runPromise(effect.pipe(Effect.provide(context))),
),
);
}),
blockConcurrencyWhile: (callback: () => Effect.Effect) =>
Effect.tryPromise(() =>
state.blockConcurrencyWhile(() => Effect.runPromise(callback())),
),
acceptWebSocket: (ws: WebSocket, tags?: string[]) =>
Effect.sync(() => state.acceptWebSocket(ws.ws, tags)),
getWebSockets: (tag?: string) =>
Effect.sync(() => state.getWebSockets(tag).map(fromWebSocket)),
setWebSocketAutoResponse: (maybeReqResp?: cf.WebSocketRequestResponsePair) =>
Effect.sync(() => state.setWebSocketAutoResponse(maybeReqResp)),
getWebSocketAutoResponse: () =>
Effect.sync(() => state.getWebSocketAutoResponse()),
getWebSocketAutoResponseTimestamp: (ws: cf.WebSocket) =>
Effect.sync(() => state.getWebSocketAutoResponseTimestamp(ws)),
setHibernatableWebSocketEventTimeout: (timeoutMs?: number) =>
Effect.sync(() => state.setHibernatableWebSocketEventTimeout(timeoutMs)),
getHibernatableWebSocketEventTimeout: () =>
Effect.sync(() => state.getHibernatableWebSocketEventTimeout()),
getTags: (ws: cf.WebSocket) => Effect.sync(() => state.getTags(ws)),
abort: (reason?: string) => Effect.sync(() => state.abort(reason)),
});