UNPKG

actor-kit

Version:

Actor Kit is a library for running state machines in Cloudflare Workers, leveraging XState for robust state management. It provides a framework for managing the logic, lifecycle, persistence, synchronization, and access control of actors in a distributed

112 lines (96 loc) 2.96 kB
import { StateValueFrom } from "xstate"; import { z } from "zod"; import { AnyActorKitStateMachine, CallerSnapshotFrom, ClientEventFrom } from "./types"; const ResponseSchema = z.object({ snapshot: z.record(z.any()), checksum: z.string(), }); export function createActorFetch<TMachine extends AnyActorKitStateMachine>({ actorType, host, }: { actorType: string; host: string; }) { return async function fetchActor( props: { actorId: string; accessToken: string; input?: Record<string, unknown>; waitForEvent?: ClientEventFrom<TMachine>; waitForState?: StateValueFrom<TMachine>; timeout?: number; errorOnWaitTimeout?: boolean; }, options?: RequestInit ): Promise<{ snapshot: CallerSnapshotFrom<TMachine>; checksum: string; }> { const input = props.input ?? {}; if (!host) throw new Error("Actor Kit host is not defined"); const route = getActorRoute(actorType, props.actorId); const protocol = getHttpProtocol(host); const url = new URL(`${protocol}://${host}${route}`); // Add input to URL parameters url.searchParams.append("input", JSON.stringify(input)); // Add waitForEvent or waitForState to URL parameters if (props.waitForEvent) { url.searchParams.append( "waitForEvent", JSON.stringify(props.waitForEvent) ); } if (props.waitForState) { url.searchParams.append( "waitForState", JSON.stringify(props.waitForState) ); } // Add timeout to URL parameters if specified if (props.timeout) { url.searchParams.append("timeout", props.timeout.toString()); } // Add errorOnWaitTimeout to URL parameters if specified if (props.errorOnWaitTimeout !== undefined) { url.searchParams.append( "errorOnWaitTimeout", props.errorOnWaitTimeout.toString() ); } const response = await fetch(url.toString(), { ...options, headers: { ...options?.headers, Authorization: `Bearer ${props.accessToken}`, }, }); if (!response.ok) { if (response.status === 408 && props.errorOnWaitTimeout !== false) { throw new Error( `Timeout waiting for actor response: ${response.statusText}` ); } throw new Error(`Failed to fetch actor: ${response.statusText}`); } const data = await response.json(); const { checksum, snapshot } = ResponseSchema.parse(data); return { snapshot: snapshot as CallerSnapshotFrom<TMachine>, checksum, }; }; } function getActorRoute(actorType: string, actorId: string) { return `/api/${actorType}/${actorId}`; } function getHttpProtocol(host: string): "http" | "https" { return isLocal(host) ? "http" : "https"; } function isLocal(host: string): boolean { return ( host.startsWith("localhost") || host.startsWith("127.0.0.1") || host.startsWith("0.0.0.0") ); }