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
1 lines • 9.6 kB
Source Map (JSON)
{"version":3,"file":"react.mjs","names":[],"sources":["../src/createActorKitContext.tsx"],"sourcesContent":["\"use client\";\n\nimport React, {\n createContext,\n memo,\n ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { matchesState, StateValueFrom } from \"xstate\";\nimport type { ActorKitClientProps } from \"./createActorKitClient\";\nimport { createActorKitClient } from \"./createActorKitClient\";\nimport type {\n ActorKitClient,\n AnyActorKitStateMachine,\n CallerSnapshotFrom,\n ClientEventFrom,\n MatchesProps,\n} from \"./types\";\n\nexport function createActorKitContext<TMachine extends AnyActorKitStateMachine>(\n actorType: string\n) {\n const ActorKitContext = createContext<ActorKitClient<TMachine> | null>(null);\n\n const ProviderFromClient: React.FC<{\n children: ReactNode;\n client: ActorKitClient<TMachine>;\n }> = ({ children, client }) => {\n return (\n <ActorKitContext.Provider value={client}>\n {children}\n </ActorKitContext.Provider>\n );\n };\n\n const InitialSnapshotContext = createContext<CallerSnapshotFrom<TMachine> | null>(null);\n\n const Provider: React.FC<\n {\n children: ReactNode;\n } & Omit<ActorKitClientProps<TMachine>, \"actorType\">\n > = memo((props) => {\n const clientRef = useRef(\n createActorKitClient<TMachine>({\n host: props.host,\n actorId: props.actorId,\n accessToken: props.accessToken,\n checksum: props.checksum,\n initialSnapshot: props.initialSnapshot,\n actorType,\n })\n );\n const initializedRef = useRef(false);\n\n useEffect(() => {\n if (!initializedRef.current) {\n initializedRef.current = true;\n clientRef.current.connect().then(() => {});\n }\n }, [initializedRef]);\n\n return (\n <InitialSnapshotContext.Provider value={props.initialSnapshot}>\n <ActorKitContext.Provider value={clientRef.current}>\n {props.children}\n </ActorKitContext.Provider>\n </InitialSnapshotContext.Provider>\n );\n });\n\n function useClient(): ActorKitClient<TMachine> {\n const client = useContext(ActorKitContext);\n if (!client) {\n throw new Error(\n \"useClient must be used within an ActorKitContext.Provider\"\n );\n }\n return client;\n }\n\n const useSelector = <T,>(\n selector: (snapshot: CallerSnapshotFrom<TMachine>) => T\n ) => {\n const client = useClient();\n const initialSnapshot = useContext(InitialSnapshotContext);\n\n // Use the initial snapshot for SSR to ensure hydration stability.\n // The server render uses initialSnapshot, and the client's first render\n // must return the same value to avoid hydration mismatches.\n const getServerSnapshot = useMemo(() => {\n if (!initialSnapshot) return undefined;\n return () => initialSnapshot;\n }, [initialSnapshot]);\n\n return useSyncExternalStoreWithSelector(\n client.subscribe,\n client.getState,\n getServerSnapshot,\n selector,\n defaultCompare\n );\n };\n\n function useSend(): (event: ClientEventFrom<TMachine>) => void {\n const client = useClient();\n return client.send;\n }\n\n function useMatches(stateValue: StateValueFrom<TMachine>): boolean {\n return useSelector((state) =>\n matchesState(stateValue, state.value as StateValueFrom<TMachine>)\n );\n }\n\n const Matches: React.FC<MatchesProps<TMachine> & { children: ReactNode }> & {\n create: (\n state: StateValueFrom<TMachine>,\n options?: {\n and?: StateValueFrom<TMachine>;\n or?: StateValueFrom<TMachine>;\n not?: boolean;\n }\n ) => React.FC<\n Omit<MatchesProps<TMachine>, \"state\" | \"and\" | \"or\" | \"not\"> & {\n children: ReactNode;\n }\n >;\n } = (props) => {\n const active = useMatches(props.state);\n const matchesAnd = props.and ? useMatches(props.and) : true;\n const matchesOr = props.or ? useMatches(props.or) : false;\n const value =\n typeof props.initialValueOverride === \"boolean\"\n ? props.initialValueOverride\n : (active && matchesAnd) || matchesOr;\n const finalValue = props.not ? !value : value;\n return finalValue ? <>{props.children}</> : null;\n };\n\n Matches.create = (state, options = {}) => {\n const Component: React.FC<\n Omit<MatchesProps<TMachine>, \"state\" | \"and\" | \"or\" | \"not\"> & {\n children: ReactNode;\n }\n > = ({ children, initialValueOverride }) => (\n <Matches\n state={state}\n and={options.and}\n or={options.or}\n not={options.not}\n initialValueOverride={initialValueOverride}\n >\n {children}\n </Matches>\n );\n Component.displayName = `MatchesComponent(${state.toString()})`;\n return Component;\n };\n\n return {\n Provider,\n ProviderFromClient,\n useClient,\n useSelector,\n useSend,\n useMatches,\n Matches,\n };\n}\n\nfunction useSyncExternalStoreWithSelector<Snapshot, Selection>(\n subscribe: (onStoreChange: () => void) => () => void,\n getSnapshot: () => Snapshot,\n getServerSnapshot: undefined | null | (() => Snapshot),\n selector: (snapshot: Snapshot) => Selection,\n isEqual?: (a: Selection, b: Selection) => boolean\n): Selection {\n const [getSelection, getServerSelection] = useMemo(() => {\n let hasMemo = false;\n let memoizedSnapshot: Snapshot;\n let memoizedSelection: Selection;\n\n const memoizedSelector = (nextSnapshot: Snapshot) => {\n if (!hasMemo) {\n hasMemo = true;\n memoizedSnapshot = nextSnapshot;\n memoizedSelection = selector(nextSnapshot);\n return memoizedSelection;\n }\n\n if (Object.is(memoizedSnapshot, nextSnapshot)) {\n return memoizedSelection;\n }\n\n const nextSelection = selector(nextSnapshot);\n\n if (isEqual && isEqual(memoizedSelection, nextSelection)) {\n memoizedSnapshot = nextSnapshot;\n return memoizedSelection;\n }\n\n memoizedSnapshot = nextSnapshot;\n memoizedSelection = nextSelection;\n return nextSelection;\n };\n\n const getSnapshotWithSelector = () => memoizedSelector(getSnapshot());\n const getServerSnapshotWithSelector = getServerSnapshot\n ? () => memoizedSelector(getServerSnapshot())\n : undefined;\n\n return [getSnapshotWithSelector, getServerSnapshotWithSelector];\n }, [getSnapshot, getServerSnapshot, selector, isEqual]);\n\n const subscribeWithSelector = useCallback(\n (onStoreChange: () => void) => {\n let previousSelection = getSelection();\n return subscribe(() => {\n const nextSelection = getSelection();\n if (!isEqual || !isEqual(previousSelection, nextSelection)) {\n previousSelection = nextSelection;\n onStoreChange();\n }\n });\n },\n [subscribe, getSelection, isEqual]\n );\n\n return useSyncExternalStore(\n subscribeWithSelector,\n getSelection,\n getServerSelection\n );\n}\n\nfunction defaultCompare<T>(a: T, b: T) {\n return a === b;\n}\n"],"mappings":"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"}