import { RpcClient, ConnectionState, DOClient } from '@dotdo/client'; /** * Shared types for @dotdo/react * * @module @dotdo/react */ /** * Connection state features that may be available on some client implementations. * These methods allow monitoring and managing the connection lifecycle. */ interface ConnectionStateFeatures { /** Current connection state (optional - may not be available on all implementations) */ connectionState?: ConnectionState; /** Register a connection state change listener */ on?(event: 'connectionStateChange', handler: (state: ConnectionState) => void): void; /** Remove a connection state change listener */ off?(event: 'connectionStateChange', handler: (state: ConnectionState) => void): void; /** Disconnect the client */ disconnect?(): void; } /** * Extended client interface for React bindings. * * This combines RpcClient with optional connection state management methods * that may be available on some client implementations. */ type ReactDotdoClient = RpcClient & ConnectionStateFeatures; /** * Base item type with required $id field. * * All items in a dotdo collection must have a unique $id field. * * @example * ```typescript * interface Task extends BaseItem { * $id: string * title: string * status: 'todo' | 'done' * } * ``` */ interface BaseItem { /** Unique identifier for the item */ $id: string; /** Additional properties */ [key: string]: unknown; } /** * Sync message types from server. * * These messages are sent over WebSocket to synchronize collection state. */ interface SyncMessage { /** Message type */ type: 'initial' | 'insert' | 'update' | 'delete'; /** Collection name */ collection: string; /** Item data (single item or array for initial) */ data?: T | T[]; /** Item key (for update/delete) */ key?: string; /** Transaction ID for ordering */ txid: number; } /** * Context value provided by DO provider. * * Contains the client instance and connection management. */ interface DotdoContextValue { /** Namespace URL for the Durable Object */ ns: string; /** The underlying client instance */ client: ReactDotdoClient | null; /** WebSocket connections by collection key */ connections: Map; /** Get an existing connection for a collection */ getConnection: (collection: string, branch?: string) => WebSocket | null; } /** * Collection configuration for hooks. * * @typeParam T - The item type for the collection */ interface CollectionConfig { /** Collection name (Noun type, e.g., 'Task', 'User') */ collection: string; /** Optional branch for branched data */ branch?: string; /** Optional Zod schema for validation (requires zod peer dep) */ schema?: unknown; } /** * Return type for useCollection hook. * * Provides reactive data and mutation methods for a collection. * * @typeParam T - The item type extending BaseItem */ interface UseDotdoCollectionResult { /** Current data in the collection */ data: T[]; /** Whether initial data is loading */ isLoading: boolean; /** Error if connection or mutation failed */ error: Error | null; /** Current transaction ID (increases with each change) */ txid: number; /** Number of pending optimistic mutations */ pendingMutations: number; /** Insert a new item with optimistic update */ insert: (item: T) => Promise; /** Update an existing item with optimistic update */ update: (id: string, changes: Partial) => Promise; /** Delete an item with optimistic update */ delete: (id: string) => Promise; /** Force refetch by reconnecting */ refetch: () => void; } /** * Query configuration for useLiveQuery. * * Defines filtering, joining, ordering, and pagination for live queries. * * @typeParam T - The source item type * @typeParam U - The join item type (defaults to unknown) * * @remarks * For optimal performance, memoize this config object with useMemo * to prevent unnecessary re-computations. */ interface LiveQueryConfig { /** Collection name (for type reference in joins) */ from: string; /** Filter conditions - object for equality, function for custom */ where?: Partial | ((item: T) => boolean); /** Join configuration for related data */ join?: { [key: string]: { /** Source data for the join */ from: U[]; /** Join condition function */ on: (item: T, joinItem: U) => boolean; /** Join type: 'left' (default) or 'inner' */ type?: 'left' | 'inner'; }; }; /** Field to order by, or custom comparator */ orderBy?: keyof T | ((a: T, b: T) => number); /** Order direction (default: 'asc') */ order?: 'asc' | 'desc'; /** Maximum number of results */ limit?: number; /** Skip first N results */ offset?: number; } /** * Return type for useRecord hook. * * Provides a single record with mutation methods. * * @typeParam T - The item type extending BaseItem */ interface UseRecordResult { /** The record data, or null if not found/loading */ data: T | null; /** Whether the record is loading */ isLoading: boolean; /** Error if connection or mutation failed */ error: Error | null; /** Update the record with optimistic update */ update: (changes: Partial) => Promise; /** Delete the record with optimistic update */ delete: () => Promise; /** Force refetch by reconnecting */ refetch: () => void; } /** * useDO - Direct Durable Object access hook * * Provides type-safe access to the Durable Object client * with RPC methods and real-time subscriptions. * * @example * ```tsx * import { useDO } from '@dotdo/react' * * interface WorkspaceAPI { * Task: { * create(data: TaskInput): Promise * list(): Promise * update(id: string, data: Partial): Promise * delete(id: string): Promise * } * User: { * me(): Promise * } * } * * function TaskManager() { * const workspace = useDO() * * const handleCreate = async () => { * // Type-safe RPC with promise pipelining * const task = await workspace.Task.create({ title: 'New Task' }) * } * * return * } * ``` * * @module @dotdo/react */ /** * Hook to access the Durable Object with type-safe RPC. * * Use this for direct RPC calls with full type safety and promise pipelining. * The client supports Cap'n Proto-style promise pipelining where multiple * chained calls execute in a single network round trip. * * @typeParam TMethods - The API interface for the Durable Object * @typeParam TEvents - Event types for subscriptions (optional) * @returns Typed DOClient instance * @throws Error if used outside of DO provider * * @example * ```tsx * // Define your API types * interface API { * Task: { * create(data: TaskInput): Promise * list(): Promise * } * } * * // Use with type safety * const client = useDO() * const tasks = await client.Task.list() * ``` */ declare function useDO(): DOClient; /** * use$ - Workflow context hook * * Provides access to the dotdo workflow context ($) for * durable operations, event handlers, and scheduling. * * @example * ```tsx * import { use$ } from '@dotdo/react' * * function TaskActions({ taskId }: { taskId: string }) { * const $ = use$() * * const handleComplete = async () => { * // Durable operation with retries * await $.do(async () => { * await $.Task(taskId).complete() * await $.notify.user('Task completed!') * }) * } * * const handleScheduleReminder = async () => { * // Schedule a future action * await $.in('1 hour').remind.task(taskId) * } * * return ( *
* * *
* ) * } * ``` */ /** * Workflow context interface * Mirrors the server-side $ context for client-side operations */ interface WorkflowContext { /** Fire-and-forget event */ send: (event: T) => void; /** Single attempt operation */ try: (action: () => Promise) => Promise; /** Durable operation with retries */ do: (action: () => Promise) => Promise; /** Schedule an action for later */ in: (delay: string | number) => WorkflowContext; /** Access to underlying client for direct RPC */ client: DOClient; /** Dynamic property access for Noun.verb pattern */ [key: string]: unknown; } /** * Hook to access the workflow context ($). * * Provides a client-side mirror of the server-side $ context * for durable operations and scheduling. * * @returns WorkflowContext instance * @throws Error if used outside of DotdoProvider */ declare function use$(): WorkflowContext; /** * useCollection - Real-time collection sync hook * * Connects to a Durable Object via WebSocket and provides reactive data * with optimistic mutations. * * @example * ```tsx * import { useCollection } from '@dotdo/react' * * function TaskList() { * const { data: tasks, isLoading, insert, update, delete: remove } = useCollection({ * collection: 'Task', * }) * * if (isLoading) return * * return ( *
    * {tasks.map(task => ( *
  • * {task.title} * * *
  • * ))} *
* ) * } * ``` * * @module @dotdo/react */ /** * Hook for managing a collection with real-time sync. * * Connects to a Durable Object via WebSocket and provides reactive data * with optimistic mutations. * * @param config - Collection configuration * @returns Collection state and mutation methods */ declare function useCollection(config: CollectionConfig): UseDotdoCollectionResult; /** * useLiveQuery - Reactive queries over collection data * * Filters, joins, and transforms collection data reactively. * Updates automatically when the source data changes. * * @example * ```tsx * import { useCollection, useLiveQuery } from '@dotdo/react' * * function TaskBoard() { * const { data: tasks } = useCollection({ collection: 'Task' }) * const { data: users } = useCollection({ collection: 'User' }) * * // Simple filter - memoize config to prevent re-renders * const todoConfig = React.useMemo(() => ({ * from: 'Task', * where: { status: 'todo' }, * orderBy: 'createdAt' as const, * order: 'desc' as const, * }), []) * const todoTasks = useLiveQuery(tasks, todoConfig) * * // With joins - memoize the join config * const joinConfig = React.useMemo(() => ({ * from: 'Task', * join: { * assignee: { * from: users, * on: (task: Task, user: User) => task.assigneeId === user.$id, * type: 'left' as const, * }, * }, * }), [users]) * const tasksWithAssignees = useLiveQuery(tasks, joinConfig) * } * ``` * * @remarks * For optimal performance, memoize the config object to prevent unnecessary * re-computations. The hook uses the config reference for dependency tracking. * * @module @dotdo/react */ /** * Hook for creating live queries over collection data. * * Filters, joins, and transforms collection data reactively. * Updates automatically when the source data changes. * * @param data - Source data array * @param config - Query configuration (memoize for best performance) * @returns Filtered and transformed data * * @remarks * This hook uses the config object reference for dependency tracking. * For optimal performance, wrap your config in useMemo to prevent * unnecessary re-computations on every render. */ declare function useLiveQuery(data: T[], config: LiveQueryConfig): R[]; /** * useRecord - Single record hook with real-time updates * * Optimized hook for working with a single record from a collection. * Uses direct RPC call to fetch only the specific record (O(1) instead of O(n)), * and subscribes to updates only for that record. * * @example * ```tsx * import { useRecord } from '@dotdo/react' * * function TaskDetail({ taskId }: { taskId: string }) { * const { data: task, isLoading, update, delete: remove } = useRecord({ * collection: 'Task', * id: taskId, * }) * * if (isLoading) return * if (!task) return * * return ( *
*

{task.title}

* * *
* ) * } * ``` * * @module @dotdo/react */ /** * Configuration for useRecord hook. * * @typeParam T - The item type for the record */ interface UseRecordConfig extends CollectionConfig { /** The record ID to fetch and watch */ id: string; } /** * Hook for working with a single record from a collection. * * Provides real-time updates and convenient mutation methods bound * to the specific record ID. * * This implementation uses direct RPC calls to fetch only the specific record, * providing O(1) performance instead of O(n) when using useCollection. * It also subscribes only to updates for the specific record ID. * * @param config - Record configuration including collection name and ID * @returns Record state and mutation methods */ declare function useRecord(config: UseRecordConfig): UseRecordResult; /** * useConnectionState - Monitor client connection state * * Provides reactive access to the WebSocket connection state. * Useful for showing connection indicators or handling offline states. * * @example * ```tsx * import { useConnectionState } from '@dotdo/react' * * function ConnectionIndicator() { * const state = useConnectionState() * * return ( *
* {state === 'connected' && 'Connected'} * {state === 'connecting' && 'Connecting...'} * {state === 'reconnecting' && 'Reconnecting...'} * {state === 'disconnected' && 'Disconnected'} *
* ) * } * ``` * * @module @dotdo/react */ /** * Hook to monitor the client connection state. * * Returns the current connection state and updates reactively * when the connection state changes. * * @returns Current connection state * * @remarks * States: * - 'connecting': Initial connection in progress * - 'connected': WebSocket is open and ready * - 'reconnecting': Connection lost, attempting to reconnect * - 'disconnected': Explicitly disconnected or failed */ declare function useConnectionState(): ConnectionState; export { type BaseItem as B, type CollectionConfig as C, type DotdoContextValue as D, type LiveQueryConfig as L, type SyncMessage as S, type UseRecordConfig as U, type WorkflowContext as W, use$ as a, useCollection as b, useLiveQuery as c, useRecord as d, useConnectionState as e, type UseDotdoCollectionResult as f, type UseRecordResult as g, useDO as u };