/** * React Hooks for TanStack Query with postgres.do * * Provides React hooks that wrap TanStack Query v5 for seamless integration * with postgres.do queries and mutations. * * @example * ```typescript * import { usePostgresQuery, usePostgresMutation } from '@dotdo/tanstack' * * function UserList() { * const { data: users, isLoading } = usePostgresQuery({ * sql: 'SELECT * FROM users WHERE active = $1', * params: [true], * }) * * const { mutate: createUser } = usePostgresMutation({ * onSuccess: () => { * // Invalidate and refetch * }, * }) * * if (isLoading) return
Loading...
* return * } * ``` * * NOTE: This module requires React 18+ and @tanstack/react-query v5+ * The hooks are designed to work with TanStack Query's useQuery/useMutation * but provide a simplified API specifically for postgres.do. */ import type { BaseRecord, Collection } from './types'; import type { QueryAdapter, QueryParams, MutationParams, PostgresQueryKey, QueryResult, MutationContext, InvalidationPattern, PostgresQueryOptions, PostgresMutationOptions } from './adapter'; /** * Minimal QueryClient interface for type safety * This represents the subset of QueryClient methods we use */ interface QueryClientLike { invalidateQueries(options: { queryKey: readonly unknown[]; }): Promise; prefetchQuery(options: PostgresQueryOptions): Promise; } /** * Query state returned by usePostgresQuery */ export interface PostgresQueryState { data: T[] | undefined; error: Error | null; isLoading: boolean; isError: boolean; isSuccess: boolean; isFetching: boolean; refetch: () => Promise; } /** * Mutation state returned by usePostgresMutation */ export interface PostgresMutationState { data: QueryResult | undefined; error: Error | null; isLoading: boolean; isPending: boolean; isError: boolean; isSuccess: boolean; isIdle: boolean; mutate: (variables: MutationParams) => void; mutateAsync: (variables: MutationParams) => Promise>; reset: () => void; } /** * Options for usePostgresQuery hook */ export interface UsePostgresQueryOptions extends Omit { /** Query adapter instance */ adapter: QueryAdapter; /** Transform function for results */ select?: (data: T[]) => T[]; /** Placeholder data while loading */ placeholderData?: T[] | (() => T[]); /** Keep previous data while refetching */ keepPreviousData?: boolean; } /** * Options for usePostgresMutation hook */ export interface UsePostgresMutationOptions { /** Query adapter instance */ adapter: QueryAdapter; /** Invalidation pattern after mutation */ invalidate?: InvalidationPattern; /** Called before mutation (for optimistic updates) */ onMutate?: (variables: MutationParams) => Promise | MutationContext | undefined; /** Called on successful mutation */ onSuccess?: (data: QueryResult, variables: MutationParams, context: MutationContext | undefined) => void; /** Called on mutation error */ onError?: (error: Error, variables: MutationParams, context: MutationContext | undefined) => void; /** Called when mutation settles */ onSettled?: (data: QueryResult | undefined, error: Error | null, variables: MutationParams, context: MutationContext | undefined) => void; } /** * Live query subscription options */ export interface UseLiveQueryOptions { /** Collection to query */ collection: Collection; /** Filter function or object */ where?: Partial | ((item: T) => boolean); /** Sort configuration */ orderBy?: { field: keyof T; direction: 'asc' | 'desc'; }; /** Limit results */ limit?: number; /** Offset results */ offset?: number; /** Whether the query is enabled */ enabled?: boolean; } /** * Live query state */ export interface LiveQueryState { data: T[]; isLoading: boolean; error: Error | null; } /** * Result type for usePostgresQuery * Compatible with TanStack Query's useQuery result */ export interface UsePostgresQueryResult { data: T[] | undefined; error: Error | null; isLoading: boolean; isError: boolean; isSuccess: boolean; isFetching: boolean; isPending: boolean; isStale: boolean; status: 'pending' | 'error' | 'success'; fetchStatus: 'fetching' | 'paused' | 'idle'; refetch: () => Promise<{ data: T[] | undefined; error: Error | null; }>; queryKey: PostgresQueryKey; } /** * Result type for usePostgresMutation * Compatible with TanStack Query's useMutation result */ export interface UsePostgresMutationResult { data: QueryResult | undefined; error: Error | null; variables: MutationParams | undefined; isLoading: boolean; isPending: boolean; isError: boolean; isSuccess: boolean; isIdle: boolean; status: 'idle' | 'pending' | 'error' | 'success'; mutate: (variables: MutationParams) => void; mutateAsync: (variables: MutationParams) => Promise>; reset: () => void; } /** * Create a usePostgresQuery hook factory. * This pattern allows frameworks to provide their own TanStack Query integration. * * @param useQuery - The framework-specific useQuery hook (e.g., from @tanstack/react-query) * @returns A configured usePostgresQuery hook * * @example * ```typescript * import { useQuery } from '@tanstack/react-query' * import { createUsePostgresQuery } from '@dotdo/tanstack' * * const usePostgresQuery = createUsePostgresQuery(useQuery) * * // Now use it in components * const { data } = usePostgresQuery(adapter, { * sql: 'SELECT * FROM users', * }) * ``` */ export declare function createUsePostgresQuery) => unknown>(useQuery: TUseQuery): (adapter: QueryAdapter, params: QueryParams | string) => ReturnType; /** * Create a usePostgresMutation hook factory * * @example * ```typescript * import { useMutation, useQueryClient } from '@tanstack/react-query' * import { createUsePostgresMutation } from '@dotdo/tanstack' * * const usePostgresMutation = createUsePostgresMutation(useMutation, useQueryClient) * * // Now use it in components * const { mutate } = usePostgresMutation(adapter, { * invalidate: { tables: ['users'] }, * }) * mutate({ sql: 'INSERT INTO users (name) VALUES ($1)', params: ['Alice'] }) * ``` */ /** * Extended invalidation pattern with advanced features */ interface ExtendedInvalidationPattern extends InvalidationPattern { autoDetect?: boolean; awaitInvalidation?: boolean; shouldInvalidate?: (result: QueryResult) => boolean; cascade?: Record; } export declare function createUsePostgresMutation, Error, MutationParams, MutationContext>) => unknown, TUseQueryClient extends () => QueryClientLike>(useMutation: TUseMutation, useQueryClient: TUseQueryClient): (adapter: QueryAdapter, options?: Omit, "adapter"> & { invalidate?: ExtendedInvalidationPattern; }) => ReturnType & { getMutationHistory: () => { sql: string; params?: unknown[]; timestamp: number; success: boolean; }[]; getStats: () => { totalMutations: number; successCount: number; errorCount: number; averageDurationMs: number; }; }; /** * Create a useLiveQuery hook factory for reactive collection queries * * @example * ```typescript * import { useSyncExternalStore } from 'react' * import { createUseLiveQuery } from '@dotdo/tanstack' * * const useLiveQuery = createUseLiveQuery(useSyncExternalStore) * * // Now use it in components * const todos = useLiveQuery(collection, { * where: { completed: false }, * orderBy: { field: 'createdAt', direction: 'desc' }, * }) * ``` */ /** * Extended live query options including advanced features */ interface ExtendedLiveQueryOptions extends Omit, 'collection'> { select?: (items: T[]) => unknown; groupBy?: keyof T; distinct?: keyof T; debounce?: number; throttle?: number; optimisticData?: T[]; onDiff?: (diff: { added: T[]; removed: T[]; updated: Array<{ previous: T; current: T; }>; }) => void; onError?: (error: Error) => void; cursor?: { field: keyof T; after?: unknown; pageSize: number; }; search?: { query: string; fields: Array; fuzzy?: boolean; }; } export declare function createUseLiveQuery void) => () => void, getSnapshot: () => unknown, getServerSnapshot?: () => unknown) => unknown>(useSyncExternalStore: TUseSyncExternalStore): { (collection: Collection, options?: ExtendedLiveQueryOptions): unknown; connectionState(collection: Collection): unknown; join(collection1: Collection, collection2: Collection, joinOptions: { on: (item1: T1, item2: T2) => boolean; select: (item1: T1, item2: T2) => unknown; }): unknown[]; multiCollection(store: { getCollection: (id: string) => Collection | undefined; }, collectionIds: string[], multiOptions: { combine: (...collections: unknown[][]) => unknown; }): unknown; aggregate(store: { getCollection: (id: string) => Collection | undefined; }, aggregateOptions: { collections: string[]; aggregation: Record unknown>; }): unknown; } & { connectionState: (collection: Collection) => unknown; join: (collection1: Collection, collection2: Collection, joinOptions: { on: (item1: T1, item2: T2) => boolean; select: (item1: T1, item2: T2) => unknown; }) => unknown[]; multiCollection: (store: { getCollection: (id: string) => Collection | undefined; }, collectionIds: string[], multiOptions: { combine: (...collections: unknown[][]) => unknown; }) => unknown; aggregate: (store: { getCollection: (id: string) => Collection | undefined; }, aggregateOptions: { collections: string[]; aggregation: Record unknown>; }) => unknown; }; /** * Minimal mutation options for collection hooks */ interface CollectionMutationOptionsLike { mutationKey: readonly unknown[]; mutationFn: (variables: TVariables) => Promise; onSuccess?: ((data: TData) => void) | undefined; onError?: ((error: Error) => void) | undefined; } /** * Create hook for collection insert operations */ export declare function createUseCollectionInsert(options: CollectionMutationOptionsLike) => unknown>(useMutation: TUseMutation): (collection: Collection, options?: { onSuccess?: (data: T) => void; onError?: (error: Error) => void; }) => ReturnType; /** * Create hook for collection update operations */ export declare function createUseCollectionUpdate(options: CollectionMutationOptionsLike) => unknown>(useMutation: TUseMutation): (collection: Collection, options?: { onSuccess?: (data: T) => void; onError?: (error: Error) => void; }) => ReturnType; /** * Create hook for collection delete operations */ export declare function createUseCollectionDelete(options: CollectionMutationOptionsLike) => unknown>(useMutation: TUseMutation): (collection: Collection, options?: { onSuccess?: () => void; onError?: (error: Error) => void; }) => ReturnType; /** * Create a hook for prefetching queries */ export declare function createUsePrefetchQuery QueryClientLike>(useQueryClient: TUseQueryClient): (adapter: QueryAdapter) => (params: QueryParams | string) => Promise; /** * Create a hook for invalidating queries */ export declare function createUseInvalidateQueries QueryClientLike>(useQueryClient: TUseQueryClient): (adapter: QueryAdapter) => { /** Invalidate a specific query by its SQL and parameters */ invalidateQuery: (params: QueryParams | string) => Promise; /** Invalidate queries involving specific tables */ invalidateTables: (tables: string[]) => Promise; /** Invalidate queries matching a pattern */ invalidatePattern: (pattern: RegExp) => Promise; /** Invalidate all queries for this database */ invalidateAll: () => Promise; }; export { createQueryKey, normalizeSQL, normalizeQueryParams, extractTablesFromSQL, getMutationType, isReadOnlyQuery, filterByPartialMatch, sortByField, applyPagination, } from './adapter'; //# sourceMappingURL=hooks.d.ts.map