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
417 lines (411 loc) • 12.4 kB
TypeScript
import React, { ReactNode } from 'react';
import { AnyStateMachine, SnapshotFrom, StateMachine, StateValueFrom, AnyEventObject } from 'xstate';
import { DurableObject } from 'cloudflare:workers';
import { z } from 'zod';
declare const RequestInfoSchema: z.ZodObject<{
longitude: z.ZodString;
latitude: z.ZodString;
continent: z.ZodString;
country: z.ZodString;
city: z.ZodString;
timezone: z.ZodString;
postalCode: z.ZodString;
region: z.ZodString;
regionCode: z.ZodString;
metroCode: z.ZodString;
botManagement: z.ZodObject<{
corporateProxy: z.ZodBoolean;
verifiedBot: z.ZodBoolean;
jsDetection: z.ZodObject<{
passed: z.ZodBoolean;
}, "strip", z.ZodTypeAny, {
passed: boolean;
}, {
passed: boolean;
}>;
staticResource: z.ZodBoolean;
detectionIds: z.ZodRecord<z.ZodString, z.ZodAny>;
score: z.ZodNumber;
}, "strip", z.ZodTypeAny, {
corporateProxy: boolean;
verifiedBot: boolean;
jsDetection: {
passed: boolean;
};
staticResource: boolean;
detectionIds: Record<string, any>;
score: number;
}, {
corporateProxy: boolean;
verifiedBot: boolean;
jsDetection: {
passed: boolean;
};
staticResource: boolean;
detectionIds: Record<string, any>;
score: number;
}>;
}, "strip", z.ZodTypeAny, {
longitude: string;
latitude: string;
continent: string;
country: string;
city: string;
timezone: string;
postalCode: string;
region: string;
regionCode: string;
metroCode: string;
botManagement: {
corporateProxy: boolean;
verifiedBot: boolean;
jsDetection: {
passed: boolean;
};
staticResource: boolean;
detectionIds: Record<string, any>;
score: number;
};
}, {
longitude: string;
latitude: string;
continent: string;
country: string;
city: string;
timezone: string;
postalCode: string;
region: string;
regionCode: string;
metroCode: string;
botManagement: {
corporateProxy: boolean;
verifiedBot: boolean;
jsDetection: {
passed: boolean;
};
staticResource: boolean;
detectionIds: Record<string, any>;
score: number;
};
}>;
declare const CallerSchema: z.ZodObject<{
id: z.ZodString;
type: z.ZodEnum<["client", "system", "service"]>;
}, "strip", z.ZodTypeAny, {
type: "client" | "system" | "service";
id: string;
}, {
type: "client" | "system" | "service";
id: string;
}>;
declare const SystemEventSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
type: z.ZodLiteral<"INITIALIZE">;
caller: z.ZodObject<{
type: z.ZodLiteral<"system">;
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "system";
id: string;
}, {
type: "system";
id: string;
}>;
}, "strip", z.ZodTypeAny, {
type: "INITIALIZE";
caller: {
type: "system";
id: string;
};
}, {
type: "INITIALIZE";
caller: {
type: "system";
id: string;
};
}>, z.ZodObject<{
type: z.ZodLiteral<"CONNECT">;
caller: z.ZodObject<{
type: z.ZodLiteral<"system">;
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "system";
id: string;
}, {
type: "system";
id: string;
}>;
connectingCaller: z.ZodObject<{
id: z.ZodString;
type: z.ZodEnum<["client", "system", "service"]>;
}, "strip", z.ZodTypeAny, {
type: "client" | "system" | "service";
id: string;
}, {
type: "client" | "system" | "service";
id: string;
}>;
}, "strip", z.ZodTypeAny, {
type: "CONNECT";
caller: {
type: "system";
id: string;
};
connectingCaller: {
type: "client" | "system" | "service";
id: string;
};
}, {
type: "CONNECT";
caller: {
type: "system";
id: string;
};
connectingCaller: {
type: "client" | "system" | "service";
id: string;
};
}>, z.ZodObject<{
type: z.ZodLiteral<"DISCONNECT">;
caller: z.ZodObject<{
type: z.ZodLiteral<"system">;
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "system";
id: string;
}, {
type: "system";
id: string;
}>;
disconnectingCaller: z.ZodObject<{
id: z.ZodString;
type: z.ZodEnum<["client", "system", "service"]>;
}, "strip", z.ZodTypeAny, {
type: "client" | "system" | "service";
id: string;
}, {
type: "client" | "system" | "service";
id: string;
}>;
}, "strip", z.ZodTypeAny, {
type: "DISCONNECT";
caller: {
type: "system";
id: string;
};
disconnectingCaller: {
type: "client" | "system" | "service";
id: string;
};
}, {
type: "DISCONNECT";
caller: {
type: "system";
id: string;
};
disconnectingCaller: {
type: "client" | "system" | "service";
id: string;
};
}>, z.ZodObject<{
type: z.ZodLiteral<"RESUME">;
caller: z.ZodObject<{
type: z.ZodLiteral<"system">;
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "system";
id: string;
}, {
type: "system";
id: string;
}>;
}, "strip", z.ZodTypeAny, {
type: "RESUME";
caller: {
type: "system";
id: string;
};
}, {
type: "RESUME";
caller: {
type: "system";
id: string;
};
}>, z.ZodObject<{
type: z.ZodLiteral<"MIGRATE">;
caller: z.ZodObject<{
type: z.ZodLiteral<"system">;
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "system";
id: string;
}, {
type: "system";
id: string;
}>;
operations: z.ZodArray<z.ZodAny, "many">;
}, "strip", z.ZodTypeAny, {
type: "MIGRATE";
caller: {
type: "system";
id: string;
};
operations: any[];
}, {
type: "MIGRATE";
caller: {
type: "system";
id: string;
};
operations: any[];
}>]>;
type EnvWithDurableObjects = {
ACTOR_KIT_SECRET: string;
[key: string]: DurableObjectNamespace<ActorServer<any>> | unknown;
};
interface ActorServerMethods<TMachine extends BaseActorKitStateMachine> {
fetch(request: Request): Promise<Response>;
spawn(props: {
actorType: string;
actorId: string;
caller: Caller;
input: Record<string, unknown>;
}): void;
send(event: ClientEventFrom<TMachine> | ServiceEventFrom<TMachine>): void;
getSnapshot(caller: Caller, options?: {
waitForEvent?: ClientEventFrom<TMachine>;
waitForState?: StateValueFrom<TMachine>;
timeout?: number;
errorOnWaitTimeout?: boolean;
}): Promise<{
checksum: string;
snapshot: CallerSnapshotFrom<TMachine>;
}>;
}
type ActorServer<TMachine extends AnyActorKitStateMachine> = DurableObject & ActorServerMethods<TMachine>;
type Caller = z.infer<typeof CallerSchema>;
type RequestInfo = z.infer<typeof RequestInfoSchema>;
type CallerType = "client" | "system" | "service";
type EventObject = {
type: string;
};
type BaseActorKitContext<TPublicProps extends {
[key: string]: unknown;
}, TPrivateProps extends {
[key: string]: unknown;
}> = {
public: TPublicProps;
private: Record<string, TPrivateProps>;
};
type ActorKitStateMachine<TEvent extends BaseActorKitEvent<EnvWithDurableObjects>, TInput extends {
id: string;
caller: Caller;
storage: DurableObjectStorage;
}, TContext extends BaseActorKitContext<any, any> & {
[key: string]: unknown;
}> = StateMachine<TContext, TEvent & EventObject, any, any, any, any, any, any, any, TInput, any, any, any, any>;
type BaseActorKitInput<TEnv = EnvWithDurableObjects> = {
id: string;
caller: Caller;
env: TEnv;
storage: DurableObjectStorage;
};
type WithActorKitInput<TInputProps extends {
[key: string]: unknown;
}, TEnv extends EnvWithDurableObjects> = TInputProps & BaseActorKitInput<TEnv>;
type AnyActorKitStateMachine = ActorKitStateMachine<any, any, any>;
type AnyActorKitEvent = (WithActorKitEvent<AnyEventObject, "client"> | WithActorKitEvent<AnyEventObject, "service"> | ActorKitSystemEvent) & BaseActorKitEvent<EnvWithDurableObjects>;
type AnyActorKitInput = WithActorKitInput<{
[key: string]: unknown;
}, EnvWithDurableObjects> & {
storage: DurableObjectStorage;
};
type AnyActorKitContext = {
public: {
[key: string]: unknown;
};
private: Record<string, {
[key: string]: unknown;
}>;
};
type BaseActorKitStateMachine = ActorKitStateMachine<AnyActorKitEvent, AnyActorKitInput, AnyActorKitContext>;
interface BaseActorKitEvent<TEnv extends EnvWithDurableObjects> {
caller: Caller;
storage: DurableObjectStorage;
requestInfo?: RequestInfo;
env: TEnv;
}
type ActorKitSystemEvent = z.infer<typeof SystemEventSchema>;
type WithActorKitEvent<T extends {
type: string;
}, C extends CallerType> = T & BaseActorKitEvent<EnvWithDurableObjects> & {
caller: {
type: C;
};
};
type CallerSnapshotFrom<TMachine extends AnyStateMachine> = {
public: SnapshotFrom<TMachine> extends {
context: {
public: infer P;
};
} ? P : unknown;
private: SnapshotFrom<TMachine> extends {
context: {
private: Partial<Record<string, infer PR>>;
};
} ? PR : unknown;
value: SnapshotFrom<TMachine> extends {
value: infer V;
} ? V : unknown;
};
type ClientEventFrom<T extends AnyActorKitStateMachine> = T extends StateMachine<any, infer TEvent, any, any, any, any, any, any, any, any, any, any, any, any> ? TEvent extends WithActorKitEvent<infer E, "client"> ? Omit<E, keyof BaseActorKitEvent<EnvWithDurableObjects>> : never : never;
type ServiceEventFrom<T extends AnyActorKitStateMachine> = T extends StateMachine<any, infer TEvent, any, any, any, any, any, any, any, any, any, any, any, any> ? TEvent extends WithActorKitEvent<infer E, "service"> ? Omit<E, keyof BaseActorKitEvent<EnvWithDurableObjects>> : never : never;
interface MatchesProps<TMachine extends AnyActorKitStateMachine> {
state: StateValueFrom<TMachine>;
and?: StateValueFrom<TMachine>;
or?: StateValueFrom<TMachine>;
not?: boolean;
initialValueOverride?: boolean;
}
type ActorKitClient<TMachine extends AnyActorKitStateMachine> = {
connect: () => Promise<void>;
disconnect: () => void;
send: (event: ClientEventFrom<TMachine>) => void;
getState: () => CallerSnapshotFrom<TMachine>;
subscribe: (listener: (state: CallerSnapshotFrom<TMachine>) => void) => () => void;
waitFor: (predicateFn: (state: CallerSnapshotFrom<TMachine>) => boolean, timeoutMs?: number) => Promise<void>;
};
type ActorKitClientProps<TMachine extends AnyActorKitStateMachine> = {
host: string;
actorType: string;
actorId: string;
checksum: string;
accessToken: string;
initialSnapshot: CallerSnapshotFrom<TMachine>;
onStateChange?: (newState: CallerSnapshotFrom<TMachine>) => void;
onError?: (error: Error) => void;
};
declare function createActorKitContext<TMachine extends AnyActorKitStateMachine>(actorType: string): {
Provider: React.FC<{
children: ReactNode;
} & Omit<ActorKitClientProps<TMachine>, "actorType">>;
ProviderFromClient: React.FC<{
children: ReactNode;
client: ActorKitClient<TMachine>;
}>;
useClient: () => ActorKitClient<TMachine>;
useSelector: <T>(selector: (snapshot: CallerSnapshotFrom<TMachine>) => T) => T;
useSend: () => (event: ClientEventFrom<TMachine>) => void;
useMatches: (stateValue: StateValueFrom<TMachine>) => boolean;
Matches: React.FC<MatchesProps<TMachine> & {
children: ReactNode;
}> & {
create: (state: StateValueFrom<TMachine>, options?: {
and?: StateValueFrom<TMachine>;
or?: StateValueFrom<TMachine>;
not?: boolean;
}) => React.FC<Omit<MatchesProps<TMachine>, "state" | "and" | "or" | "not"> & {
children: ReactNode;
}>;
};
};
export { createActorKitContext };