import type { DurableObject } from "cloudflare:workers"; import type { Operation } from "fast-json-patch"; import type { AnyEventObject, AnyStateMachine, SnapshotFrom, StateMachine, StateValueFrom, } from "xstate"; import type { z } from "zod"; import type { AnyEventSchema, CallerSchema, RequestInfoSchema, SystemEventSchema, } from "./schemas"; export type ActorKitStorage = DurableObjectStorage; export interface ActorKitEnv { ACTOR_KIT_SECRET: string; [key: string]: DurableObjectNamespace | unknown; } export type EnvWithDurableObjects = ActorKitEnv; export type AnyEvent = z.infer; export interface ActorServerMethods { fetch(request: Request): Promise; spawn(props: { actorType: string; actorId: string; caller: Caller; input: Record; }): void; send(event: ClientEventFrom | ServiceEventFrom): void; getSnapshot( caller: Caller, options?: { waitForEvent?: ClientEventFrom; waitForState?: StateValueFrom; timeout?: number; errorOnWaitTimeout?: boolean; } ): Promise<{ checksum: string; snapshot: CallerSnapshotFrom; }>; } export type ActorServer = DurableObject & ActorServerMethods; export type AnyActorServer = ActorServer; export type Caller = z.infer; export type RequestInfo = z.infer; export type ActorKitInputProps = { id: string; caller: Caller; storage: ActorKitStorage; [key: string]: unknown; }; export type CallerType = "client" | "system" | "service"; type EventObject = { type: string; }; type EventSchemaUnion = z.ZodDiscriminatedUnion< "type", [ z.ZodObject, ...z.ZodObject[] ] >; export type EventSchemas = { client: EventSchemaUnion; service: EventSchemaUnion; }; export type BaseActorKitContext< TPublicProps extends { [key: string]: unknown }, TPrivateProps extends { [key: string]: unknown } > = { public: TPublicProps; private: Record; }; export type ActorKitStateMachine< TEvent extends BaseActorKitEvent, TInput extends { id: string; caller: Caller; storage: ActorKitStorage; }, TContext extends BaseActorKitContext & { [key: string]: unknown; } > = StateMachine< TContext, TEvent & EventObject, any, any, any, any, any, any, any, TInput, any, any, any, any >; export type BaseActorKitInput = { id: string; caller: Caller; env: TEnv; storage: ActorKitStorage; }; export type WithActorKitInput< TInputProps extends { [key: string]: unknown }, TEnv extends ActorKitEnv = ActorKitEnv > = TInputProps & BaseActorKitInput; export type AnyActorKitStateMachine = ActorKitStateMachine; type AnyActorKitEvent = ( | WithActorKitEvent | WithActorKitEvent | ActorKitSystemEvent ) & BaseActorKitEvent; type AnyActorKitInput = WithActorKitInput< { [key: string]: unknown }, ActorKitEnv > & { storage: ActorKitStorage; }; type AnyActorKitContext = { public: { [key: string]: unknown }; private: Record; }; export type BaseActorKitStateMachine = ActorKitStateMachine< AnyActorKitEvent, AnyActorKitInput, AnyActorKitContext >; export type MachineServerOptions = { persisted?: boolean; }; export type ExtraContext = { requestId: string; }; export interface BaseActorKitEvent { caller: Caller; storage: ActorKitStorage; requestInfo?: RequestInfo; env: TEnv; } export type ActorKitSystemEvent = z.infer; // Utility type to merge custom event types with the base event export type WithActorKitEvent< T extends { type: string }, C extends CallerType > = T & BaseActorKitEvent & { caller: { type: C } }; export type WithActorKitContext< TExtraProps extends { [key: string]: unknown }, TPrivateProps extends { [key: string]: unknown }, TPublicProps extends { [key: string]: unknown } > = TExtraProps & { public: TPublicProps; private: Record; }; export type CallerSnapshotFrom = { public: SnapshotFrom extends { context: { public: infer P } } ? P : unknown; private: SnapshotFrom extends { context: { private: Partial> }; } ? PR : unknown; value: SnapshotFrom extends { value: infer V } ? V : unknown; }; export type ClientEventFrom = T extends StateMachine< any, infer TEvent, any, any, any, any, any, any, any, any, any, any, any, any > ? TEvent extends WithActorKitEvent ? Omit> : never : never; export type ServiceEventFrom = T extends StateMachine< any, infer TEvent, any, any, any, any, any, any, any, any, any, any, any, any > ? TEvent extends WithActorKitEvent ? Omit> : never : never; // Helper type to convert from SCREAMING_SNAKE_CASE to kebab-case export type ScreamingSnakeToKebab = S extends `${infer T}_${infer U}` ? `${Lowercase}-${ScreamingSnakeToKebab}` : Lowercase; export type DurableObjectActor = ActorServer; type CamelToSnakeCase = S extends `${infer T}${infer U}` ? U extends Uncapitalize ? `${Lowercase}${CamelToSnakeCase}` : `${Lowercase}_${CamelToSnakeCase}` : S; type KebabToCamelCase = S extends `${infer T}-${infer U}` ? `${T}${Capitalize>}` : S; export type KebabToScreamingSnake = Uppercase< CamelToSnakeCase> >; export interface MatchesProps { state: StateValueFrom; and?: StateValueFrom; or?: StateValueFrom; not?: boolean; initialValueOverride?: boolean; } export type MachineFromServer = T extends ActorServer ? M : never; export type ActorKitEmittedEvent = { operations: Operation[]; checksum: string; }; export type ActorKitClient = { connect: () => Promise; disconnect: () => void; send: (event: ClientEventFrom) => void; getState: () => CallerSnapshotFrom; subscribe: ( listener: (state: CallerSnapshotFrom) => void ) => () => void; waitFor: ( predicateFn: (state: CallerSnapshotFrom) => boolean, timeoutMs?: number ) => Promise; }; // First define a helper to extract the event type from a machine type ExtractEventType = TMachine extends ActorKitStateMachine< infer TEvent, any, any > ? TEvent : never; // Then extract the env type from the event type ExtractEnvType = TEvent extends { env: infer TEnv } ? TEnv : never; // Finally, our InferEnvFromMachine type export type EnvFromMachine = ExtractEnvType> extends never ? ActorKitEnv : ExtractEnvType> & ActorKitEnv;