kitcn
Version:
kitcn - React Query integration and CLI tools for Convex
1,011 lines (1,010 loc) • 62.8 kB
TypeScript
'use client';
import { ConvexProvider, ConvexProviderWithAuth as ConvexProviderWithAuth$1, ConvexReactClient, ConvexReactClient as ConvexReactClient$1, ConvexReactClientOptions, Watch, WatchQueryOptions, useConvex } from "convex/react";
import * as react from "react";
import { ReactNode } from "react";
import * as jotai_x0 from "jotai-x";
import * as react_jsx_runtime0 from "react/jsx-runtime";
import { FunctionArgs, FunctionReference, FunctionReturnType } from "convex/server";
import { DefaultError, QueryCache, QueryClient, QueryFilters, QueryFunction, QueryFunctionContext, QueryKey, SkipToken, UseMutationOptions, UseQueryOptions, UseQueryResult } from "@tanstack/react-query";
import { ConvexHttpClient } from "convex/browser";
import * as jotai_vanilla0 from "jotai/vanilla";
import { z } from "zod";
//#region src/crpc/auth-error.d.ts
/**
* Auth Mutation Error
*
* Framework-agnostic error class for Better Auth mutations.
*/
/**
* Error thrown when a Better Auth mutation fails.
* Contains the original error details from Better Auth.
*/
declare class AuthMutationError extends Error {
/** Error code from Better Auth (e.g., 'INVALID_PASSWORD', 'EMAIL_ALREADY_REGISTERED') */
code?: string;
/** HTTP status code */
status: number;
/** HTTP status text */
statusText: string;
constructor(authError: {
message?: string;
status: number;
statusText: string;
code?: string;
});
}
/**
* Type guard to check if an error is an AuthMutationError.
*/
declare function isAuthMutationError(error: unknown): error is AuthMutationError;
//#endregion
//#region src/react/auth-mutations.d.ts
type MutationOptionsHook<TData, TVariables = void> = (options?: Omit<UseMutationOptions<TData, DefaultError, TVariables>, 'mutationFn'>) => UseMutationOptions<TData, DefaultError, TVariables>;
type AnyAuthFn = (...args: never[]) => unknown;
type MutationArgsWithFetchOptions = {
fetchOptions?: Record<string, unknown>;
};
type AuthClient = {
$store?: {
atoms?: {
session?: {
get?: () => {
data?: unknown;
error?: unknown;
isPending?: boolean;
isRefetching?: boolean;
refetch?: (queryParams?: {
query?: Record<string, unknown>;
}) => Promise<void>;
};
set?: (value: {
data: unknown;
error: unknown;
isPending: boolean;
isRefetching: boolean;
refetch: (queryParams?: {
query?: Record<string, unknown>;
}) => Promise<void>;
}) => void;
};
};
};
getSession?: AnyAuthFn;
signOut?: AnyAuthFn;
signIn?: {
anonymous?: AnyAuthFn;
social?: AnyAuthFn;
email?: AnyAuthFn;
};
signUp?: {
email?: AnyAuthFn;
};
};
/**
* Create mutation option hooks from a better-auth client.
*
* @example
* ```tsx
* // lib/auth-client.ts
* import { createAuthMutations } from 'kitcn/react';
*
* export const authClient = createAuthClient({...});
*
* export const {
* useSignOutMutationOptions,
* useSignInSocialMutationOptions,
* useSignInMutationOptions,
* useSignUpMutationOptions,
* } = createAuthMutations(authClient);
*
* // components/header.tsx
* const signOutMutation = useMutation(useSignOutMutationOptions({
* onSuccess: () => router.push('/login'),
* }));
* ```
*/
type AuthMutationsResult = {
useSignOutMutationOptions: MutationOptionsHook<unknown, MutationArgsWithFetchOptions | void>;
useSignInSocialMutationOptions: MutationOptionsHook<unknown, unknown>;
useSignInMutationOptions: MutationOptionsHook<unknown, unknown>;
useSignUpMutationOptions: MutationOptionsHook<unknown, unknown>;
};
declare function createAuthMutations(authClient: AuthClient): AuthMutationsResult;
//#endregion
//#region src/react/auth-store.d.ts
type FetchAccessTokenFn = (args: {
forceRefreshToken: boolean;
}) => Promise<string | null>;
declare const FetchAccessTokenContext: react.Context<FetchAccessTokenFn | null>;
/** Get fetchAccessToken from context (available immediately, no race condition) */
declare const useFetchAccessToken: () => FetchAccessTokenFn | null;
type ConvexAuthResult = {
isAuthenticated: boolean;
isLoading: boolean;
};
/** Get auth from bridge context (null if no bridge configured) */
declare const useConvexAuthBridge: () => ConvexAuthResult | null;
type AuthStoreState = {
/** Callback when mutation/action called while unauthorized. Throws by default. */onMutationUnauthorized: () => void; /** Callback when query called while unauthorized. Noop by default. */
onQueryUnauthorized: (info: {
queryName: string;
}) => void; /** Custom function to detect UNAUTHORIZED errors. Default checks code or "auth" in message. */
isUnauthorized: (error: unknown) => boolean; /** Cached Convex JWT for HTTP requests */
token: string | null; /** JWT expiration timestamp (ms) */
expiresAt: number | null; /** Auth loading state (synced from useConvexAuth for class methods) */
isLoading: boolean; /** Auth state (synced from useConvexAuth for class methods) */
isAuthenticated: boolean; /** Grace window for freshly seeded auth tokens while session sync catches up */
sessionSyncGraceUntil: number | null;
};
declare const AUTH_SESSION_SYNC_GRACE_MS = 10000;
declare const isSessionSyncGraceActive: (sessionSyncGraceUntil: number | null) => boolean;
/** Decode JWT expiration (ms timestamp) from token */
declare function decodeJwtExp(token: string): number | null;
declare const AuthProvider: react.FC<jotai_x0.ProviderProps<{
onMutationUnauthorized: () => void;
onQueryUnauthorized: (info: {
queryName: string;
}) => void;
isUnauthorized: (error: unknown) => boolean;
token: string | null;
expiresAt: number | null;
isLoading: boolean;
isAuthenticated: boolean;
sessionSyncGraceUntil: number | null;
}>>, useAuthStore: jotai_x0.UseStoreApi<AuthStoreState, object>, useAuthState: <K extends keyof AuthStoreState>(key: K, options?: string | jotai_x0.UseAtomOptions) => ({
onMutationUnauthorized: jotai_x0.SimpleWritableAtom<() => void>;
onQueryUnauthorized: jotai_x0.SimpleWritableAtom<(info: {
queryName: string;
}) => void>;
isUnauthorized: jotai_x0.SimpleWritableAtom<(error: unknown) => boolean>;
token: jotai_x0.SimpleWritableAtom<string | null>;
expiresAt: jotai_x0.SimpleWritableAtom<number | null>;
isLoading: jotai_x0.SimpleWritableAtom<boolean>;
isAuthenticated: jotai_x0.SimpleWritableAtom<boolean>;
sessionSyncGraceUntil: jotai_x0.SimpleWritableAtom<number | null>;
} & object)[K] extends jotai_vanilla0.WritableAtom<infer V, infer A extends unknown[], infer R> ? [V, (...args: A) => R] : never, useAuthValue: <K extends keyof AuthStoreState, S = (({
onMutationUnauthorized: jotai_x0.SimpleWritableAtom<() => void>;
onQueryUnauthorized: jotai_x0.SimpleWritableAtom<(info: {
queryName: string;
}) => void>;
isUnauthorized: jotai_x0.SimpleWritableAtom<(error: unknown) => boolean>;
token: jotai_x0.SimpleWritableAtom<string | null>;
expiresAt: jotai_x0.SimpleWritableAtom<number | null>;
isLoading: jotai_x0.SimpleWritableAtom<boolean>;
isAuthenticated: jotai_x0.SimpleWritableAtom<boolean>;
sessionSyncGraceUntil: jotai_x0.SimpleWritableAtom<number | null>;
} & object)[K] extends jotai_vanilla0.Atom<infer V> ? V : never)>(key: K, options?: ({
selector?: ((v: ({
onMutationUnauthorized: jotai_x0.SimpleWritableAtom<() => void>;
onQueryUnauthorized: jotai_x0.SimpleWritableAtom<(info: {
queryName: string;
}) => void>;
isUnauthorized: jotai_x0.SimpleWritableAtom<(error: unknown) => boolean>;
token: jotai_x0.SimpleWritableAtom<string | null>;
expiresAt: jotai_x0.SimpleWritableAtom<number | null>;
isLoading: jotai_x0.SimpleWritableAtom<boolean>;
isAuthenticated: jotai_x0.SimpleWritableAtom<boolean>;
sessionSyncGraceUntil: jotai_x0.SimpleWritableAtom<number | null>;
} & object)[K] extends jotai_vanilla0.Atom<infer V_1> ? V_1 : never, prevSelectorOutput?: S | undefined) => S) | undefined;
equalityFn?: ((prev: S, next: S) => boolean) | undefined;
} & jotai_x0.UseAtomOptions) | undefined, deps?: unknown[]) => S;
type AuthStore = ReturnType<typeof useAuthStore>;
/**
* Safe wrapper around useConvexAuth that doesn't throw when used outside auth provider.
* Returns { isAuthenticated: false, isLoading: false } when no auth provider.
*
* Supports both:
* - better-auth users (via AuthProvider)
* - @convex-dev/auth users (via ConvexAuthBridge)
*/
declare function useSafeConvexAuth(): ConvexAuthResult;
/**
* Internal bridge component. Use `ConvexProviderWithAuth` instead.
* @internal
*/
declare function ConvexAuthBridge({
children
}: {
children: React.ReactNode;
}): react_jsx_runtime0.JSX.Element;
/**
* Convex provider with auth bridge for @convex-dev/auth users.
* Automatically wraps children with ConvexAuthBridge.
*
* @example
* ```tsx
* import { ConvexProviderWithAuth } from 'kitcn/react';
*
* <ConvexProviderWithAuth client={convex} useAuth={useAuthFromConvexDev}>
* <App />
* </ConvexProviderWithAuth>
* ```
*/
declare function ConvexProviderWithAuth({
children,
...props
}: React.ComponentProps<typeof ConvexProviderWithAuth$1>): react_jsx_runtime0.JSX.Element;
declare const useAuth: () => {
hasSession: boolean;
isAuthenticated: boolean;
isLoading: boolean;
};
/** Check if user maybe has auth (optimistic, has token) */
declare const useMaybeAuth: () => boolean;
/** Check if user is authenticated (server-verified) */
declare const useIsAuth: () => boolean;
declare const useAuthGuard: () => (callback?: () => Promise<void> | void) => boolean | undefined;
/** Render children only when maybe has auth (optimistic) */
declare function MaybeAuthenticated({
children
}: {
children: React.ReactNode;
}): react.ReactNode;
/** Render children only when authenticated (server-verified) */
declare function Authenticated({
children
}: {
children: React.ReactNode;
}): react.ReactNode;
/** Render children only when maybe not auth (optimistic) */
declare function MaybeUnauthenticated({
children
}: {
children: React.ReactNode;
}): react.ReactNode;
/** Render children only when not authenticated (server-verified) */
declare function Unauthenticated({
children
}: {
children: React.ReactNode;
}): react.ReactNode;
//#endregion
//#region src/crpc/transformer.d.ts
/**
* Generic transformer contract (mirrors tRPC shape).
*/
interface DataTransformer {
deserialize(object: any): any;
serialize(object: any): any;
}
/**
* Separate input/output transformers.
*/
interface CombinedDataTransformer {
input: DataTransformer;
output: DataTransformer;
}
/**
* Transformer config accepted by cRPC.
*/
type DataTransformerOptions = CombinedDataTransformer | DataTransformer;
//#endregion
//#region src/react/client.d.ts
interface ConvexQueryClientOptions extends ConvexReactClientOptions {
/** Auth store for checking auth state in queryFn */
authStore?: AuthStore;
/**
* Opt out of consistent SSR queries for faster performance.
* Trade-off: queries may return results from different timestamps.
*/
dangerouslyUseInconsistentQueriesDuringSSR?: boolean;
/** TanStack QueryClient. Can also be set later via .connect(queryClient) */
queryClient?: QueryClient;
/** Custom fetch for SSR. Avoid bundling on client. */
serverFetch?: typeof globalThis.fetch;
/** Optional payload transformer (always composed with built-in Date support). */
transformer?: DataTransformerOptions;
/**
* Delay in ms before unsubscribing when a query has no observers.
* Prevents wasteful unsubscribe/subscribe cycles from React StrictMode
* and quick back/forward navigation. Set to 0 to unsubscribe immediately.
* @default 3000
*/
unsubscribeDelay?: number;
}
/**
* Bridges TanStack Query with Convex real-time subscriptions.
*
* ## Setup
*
* ```ts
* const convexQueryClient = new ConvexQueryClient(CONVEX_URL);
*
* const queryClient = new QueryClient({
* defaultOptions: {
* queries: {
* queryFn: convexQueryClient.queryFn(),
* queryKeyHashFn: convexQueryClient.hashFn(),
* },
* },
* });
*
* convexQueryClient.connect(queryClient);
* ```
*
* ## How It Works
*
* 1. **Query Added**: When TanStack adds a Convex query to cache,
* ConvexQueryClient creates a WebSocket subscription via `watchQuery`.
*
* 2. **Real-time Updates**: When Convex pushes an update, the subscription
* callback updates the TanStack cache via `setQueryData`.
*
* 3. **Query Removed**: When TanStack removes a query (no observers),
* ConvexQueryClient unsubscribes from the WebSocket.
*
* ## Auth Integration
*
* Auth is handled via TanStack Query `meta`:
* - `convexQuery` includes `meta.authType` from generated Convex metadata
* - `queryFn` checks `meta.authType` via `getAuthState()` and throws `CRPCClientError` if unauthorized
* - `subscribeInner` respects `meta.subscribe === false` to skip WebSocket
*/
declare class ConvexQueryClient {
/** Convex client for WebSocket subscriptions (client) and one-shot queries */
convexClient: ConvexReactClient$1;
/**
* Active WebSocket subscriptions, keyed by TanStack query hash.
* Each subscription has:
* - watch: Convex Watch object for the query
* - unsubscribe: Cleanup function to remove the subscription
* - queryKey: Original query key for cache updates
*/
subscriptions: Record<string, {
watch: Watch<unknown>;
unsubscribe: () => void;
queryKey: QueryKey;
}>;
/** Cleanup function for QueryCache subscription */
unsubscribe: (() => void) | undefined;
/**
* Pending unsubscribes with timeout IDs.
* Used to debounce unsubscribe/subscribe cycles from React StrictMode.
*/
private pendingUnsubscribes;
/** HTTP client for SSR queries (no WebSocket on server) */
serverHttpClient?: ConvexHttpClient;
/** TanStack QueryClient reference */
_queryClient: QueryClient | undefined;
/** SSR query mode: 'consistent' guarantees same timestamp, 'inconsistent' is faster */
ssrQueryMode: 'consistent' | 'inconsistent';
/** Auth store for checking auth state */
private authStore?;
/** Delay before unsubscribing when query has no observers */
private unsubscribeDelay;
/** Payload transformer used across request/response boundaries. */
private transformer;
/** Runtime-safe accessor for pending unsubscribe map (defensive for HMR edge cases) */
private getPendingUnsubscribesMap;
/** Cancel a pending delayed unsubscribe for a query hash. */
private cancelPendingUnsubscribe;
/** Unsubscribe a live Convex watch (if present) and remove it from the subscription map. */
private unsubscribeQueryByHash;
private isAuthBoundQuery;
private isQueryDisabled;
private subscribeQuery;
/** Update auth store (for HMR where jotai store may reset) */
updateAuthStore(authStore?: AuthStore): void;
/** Get current auth state from store */
private getAuthState;
/**
* Check if subscription should be skipped due to auth state.
* Needed for useSuspenseQuery which ignores enabled: false.
* Regular useQuery already handles this via enabled: false in hooks.
*/
private shouldSkipSubscription;
/** Get QueryClient, throwing if not connected */
get queryClient(): QueryClient;
/**
* Create a ConvexQueryClient.
*
* @param client - Convex URL string or existing ConvexReactClient
* @param options - Configuration options
*/
constructor(client: ConvexReactClient$1 | string, options?: ConvexQueryClientOptions);
/**
* Connect to TanStack QueryClient.
* Starts listening to cache events for subscription management.
*/
connect(queryClient: QueryClient): void;
/**
* Clean up all subscriptions.
* Call this when the client is no longer needed.
*/
destroy(): void;
/**
* Unsubscribe from all auth-required queries.
* Call before logout to prevent UNAUTHORIZED errors during session invalidation.
*/
unsubscribeAuthQueries(): void;
resetAuthQueries(): Promise<void>;
/**
* Batch update all subscriptions.
* Called internally when Convex client reconnects.
*/
onUpdate: () => void;
/**
* Handle Convex subscription update for a specific query.
* Reads latest value from Watch and updates TanStack cache.
*
* @param queryHash - TanStack query hash identifying the query
*/
onUpdateQueryKeyHash(queryHash: string): void;
/**
* Subscribe to TanStack QueryCache events.
* Creates/removes Convex WebSocket subscriptions as queries are added/removed.
*
* @param queryCache - TanStack QueryCache to subscribe to
* @returns Cleanup function to unsubscribe
*/
subscribeInner(queryCache: QueryCache): () => void;
/**
* Create default queryFn for TanStack QueryClient.
*
* Handles:
* - Convex queries and actions
* - Auth checking (throws CRPCClientError if unauthorized)
* - SSR via HTTP client
* - Client via WebSocket client
*
* ## Usage
*
* ```ts
* const queryClient = new QueryClient({
* defaultOptions: {
* queries: {
* queryFn: convexQueryClient.queryFn(),
* },
* },
* });
* ```
*
* @param otherFetch - Fallback queryFn for non-Convex queries
* @returns QueryFunction compatible with TanStack Query
*/
queryFn(otherFetch?: QueryFunction<unknown, QueryKey>): <T extends FunctionReference<"query", "public">>(context: QueryFunctionContext<readonly unknown[]>) => Promise<FunctionReturnType<T>>;
/**
* Create hash function for TanStack QueryClient.
*
* Uses Convex-specific hashing for Convex queries to ensure
* consistent cache keys across serialization.
*
* ## Usage
*
* ```ts
* const queryClient = new QueryClient({
* defaultOptions: {
* queries: {
* queryKeyHashFn: convexQueryClient.hashFn(),
* },
* },
* });
* ```
*
* @param otherHashKey - Fallback hash function for non-Convex queries
* @returns Hash function compatible with TanStack Query
*/
hashFn(fallback?: (queryKey: readonly unknown[]) => string): (queryKey: readonly unknown[]) => string;
}
//#endregion
//#region src/internal/types.d.ts
/**
* Simplify complex type intersections for better IDE display.
* Pattern from Drizzle ORM: The & {} intersection "seals" the type to prevent
* distributive conditional behavior that can cause union widening.
* @see https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/utils.ts#L144-L149
*/
type Simplify<TType> = TType extends any[] | Date ? TType : { [K in keyof TType]: TType[K] } & {};
/**
* Omit keys without removing a potential union.
* Unlike standard Omit, this preserves union types.
*/
type DistributiveOmit<TObj, TKey extends keyof any> = TObj extends any ? Omit<TObj, TKey> : never;
/** Makes the object recursively optional */
type DeepPartial<TObject> = TObject extends object ? { [P in keyof TObject]?: DeepPartial<TObject[P]> } : TObject;
//#endregion
//#region src/server/types.d.ts
/** Marker for unset values - branded type to distinguish "not set" from actual types */
type UnsetMarker = {
readonly __brand: 'UnsetMarker';
};
//#endregion
//#region src/server/http-types.d.ts
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
interface HttpRouteDefinition<TMethod extends HttpMethod = HttpMethod> {
method: TMethod;
path: string;
pathParamNames: string[];
usePathPrefix: boolean;
}
interface HttpActionHandler {
isHttp: true;
}
interface HttpProcedure<TInput extends UnsetMarker | z.ZodTypeAny = any, TOutput extends UnsetMarker | z.ZodTypeAny = any, TParams extends UnsetMarker | z.ZodTypeAny = any, TQuery extends UnsetMarker | z.ZodTypeAny = any, TMethod extends HttpMethod = HttpMethod, TForm extends UnsetMarker | z.ZodTypeAny = any> extends HttpActionHandler {
_crpcHttpRoute: HttpRouteDefinition<TMethod>;
/** @internal Expose def for client-side type inference */
_def: {
inputSchema?: TInput;
outputSchema?: TOutput;
paramsSchema?: TParams;
querySchema?: TQuery;
formSchema?: TForm;
};
}
//#endregion
//#region src/server/http-router.d.ts
/**
* Recursive router record - can contain procedures or nested routers
*/
interface HttpRouterRecord {
[key: string]: HttpProcedure | HttpRouterRecord | CRPCHttpRouter<any>;
}
/**
* Router definition - stores both flat procedures and hierarchical record
*/
interface HttpRouterDef<TRecord extends HttpRouterRecord> {
/** Flat map with dot-notation keys (e.g., "todos.get") for lookup */
procedures: Record<string, HttpProcedure>;
/** Hierarchical structure for type inference */
record: TRecord;
router: true;
}
/**
* HTTP Router - like tRPC's BuiltRouter
*/
interface CRPCHttpRouter<TRecord extends HttpRouterRecord> {
_def: HttpRouterDef<TRecord>;
}
//#endregion
//#region src/crpc/http-types.d.ts
/** Error codes that can be returned from HTTP endpoints */
type HttpErrorCode = 'BAD_REQUEST' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'NOT_FOUND' | 'METHOD_NOT_SUPPORTED' | 'CONFLICT' | 'UNPROCESSABLE_CONTENT' | 'TOO_MANY_REQUESTS' | 'INTERNAL_SERVER_ERROR' | 'UNKNOWN';
/** HTTP client error */
declare class HttpClientError extends Error {
readonly name = "HttpClientError";
readonly code: HttpErrorCode;
readonly status: number;
readonly procedureName: string;
constructor(opts: {
code: HttpErrorCode;
status: number;
procedureName: string;
message?: string;
});
}
//#endregion
//#region src/crpc/types.d.ts
/** Symbol key for attaching FunctionReference to options (non-serializable) */
declare const FUNC_REF_SYMBOL: unique symbol;
/** Options controlled by convexQuery/convexAction factories */
type ReservedQueryOptions$2 = 'queryKey' | 'queryFn' | 'staleTime';
/** Options controlled by mutation factories */
type ReservedMutationOptions$2 = 'mutationFn';
/** Reserved options controlled by infinite query factories */
type ReservedInfiniteQueryOptions = 'queryKey' | 'queryFn' | 'staleTime' | 'refetchInterval' | 'refetchOnMount' | 'refetchOnReconnect' | 'refetchOnWindowFocus' | 'persister' | 'placeholderData';
/** Metadata for a single Convex function */
type FnMeta$1 = {
auth?: 'required' | 'optional';
role?: string;
ratelimit?: string;
type?: 'query' | 'mutation' | 'action';
limit?: number;
[key: string]: unknown;
};
/** Metadata for paginated functions (limit is required) */
type PaginatedFnMeta = Omit<FnMeta$1, 'limit'> & {
limit: number;
};
/** Metadata for all Convex functions by namespace.fnName, with _http for HTTP routes */
type Meta = Record<string, Record<string, FnMeta$1>> & {
_http?: Record<string, {
path: string;
method: string;
}>;
};
/** Query key structure for Convex queries */
type ConvexQueryKey<T extends FunctionReference<'query'>> = readonly ['convexQuery', string, FunctionArgs<T>];
/** Query key structure for Convex actions */
type ConvexActionKey<T extends FunctionReference<'action'>> = readonly ['convexAction', string, FunctionArgs<T>];
/** Mutation key structure for Convex mutations/actions */
type ConvexMutationKey = ['convexMutation', string];
/**
* Meta passed to TanStack Query for auth and subscription control.
* Set by convexQuery, read by ConvexQueryClient.queryFn() and subscribeInner().
*/
type ConvexQueryMeta = {
/** Auth type from generated Convex metadata via getMeta() */authType?: 'required' | 'optional'; /** Skip query silently when unauthenticated (returns null) */
skipUnauth?: boolean; /** Whether to create WebSocket subscription (default: true) */
subscribe?: boolean;
};
/** Hook options for Convex queries */
type ConvexQueryHookOptions = {
/** Skip query silently when unauthenticated (default: false, calls onQueryUnauthorized) */skipUnauth?: boolean; /** Set to false to fetch once without subscribing (default: true) */
subscribe?: boolean;
};
/** Extract input args without cursor/limit (user's filter args only) */
type InfiniteQueryInput<TInput> = Omit<TInput, 'cursor' | 'limit'>;
/** Extract item type from PaginationResult<T> */
type ExtractPaginatedItem<TOutput> = TOutput extends {
page: (infer T)[];
} ? T : never;
/** Metadata for infinite query (extends ConvexQueryMeta) */
type ConvexInfiniteQueryMeta = ConvexQueryMeta & {
/** The query function name (serializable for RSC) */queryName: string; /** Query args without cursor/limit (user's filter args only) */
args: Record<string, unknown>; /** Items per page (optional - server uses .paginated() default) */
limit?: number;
};
type EmptyObject = Record<string, never>;
/** Static query options parameter type (non-hook, for event handlers) */
type StaticQueryOptsParam = {
skipUnauth?: boolean;
};
/** Check if a type has cursor key (pagination detection) */
type IsPaginated<T> = 'cursor' extends keyof T ? true : false;
/** Mutation variables type - undefined when no args required (allows mutateAsync() without args) */
type MutationVariables<T extends FunctionReference<'mutation' | 'action'>> = keyof FunctionArgs<T> extends never ? void : EmptyObject extends FunctionArgs<T> ? FunctionArgs<T> | undefined : FunctionArgs<T>;
/** Vanilla mutation - direct .mutate() call without TanStack Query */
type VanillaMutation<T extends FunctionReference<'mutation'>> = {
mutate: keyof FunctionArgs<T> extends never ? (args?: EmptyObject) => Promise<FunctionReturnType<T>> : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T>) => Promise<FunctionReturnType<T>> : (args: FunctionArgs<T>) => Promise<FunctionReturnType<T>>;
};
/** Vanilla action - both .query() and .mutate() for direct calls */
type VanillaAction<T extends FunctionReference<'action'>> = {
query: keyof FunctionArgs<T> extends never ? (args?: EmptyObject) => Promise<FunctionReturnType<T>> : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T>) => Promise<FunctionReturnType<T>> : (args: FunctionArgs<T>) => Promise<FunctionReturnType<T>>;
mutate: keyof FunctionArgs<T> extends never ? (args?: EmptyObject) => Promise<FunctionReturnType<T>> : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T>) => Promise<FunctionReturnType<T>> : (args: FunctionArgs<T>) => Promise<FunctionReturnType<T>>;
};
//#endregion
//#region src/react/crpc-types.d.ts
/** Options returned by `convexQuery` factory */
type ConvexQueryOptions<T extends FunctionReference<'query'>> = Pick<UseQueryOptions<FunctionReturnType<T>, Error, FunctionReturnType<T>, ConvexQueryKey<T>>, 'queryKey' | 'staleTime' | 'enabled'>;
/** Options returned by `convexAction` factory */
type ConvexActionOptions<T extends FunctionReference<'action'>> = Pick<UseQueryOptions<FunctionReturnType<T>, Error, FunctionReturnType<T>, ConvexActionKey<T>>, 'queryKey' | 'staleTime' | 'enabled'>;
/** Query options parameter type */
type QueryOptsParam<T extends FunctionReference<'query'>> = Simplify<ConvexQueryHookOptions & DistributiveOmit<UseQueryOptions<FunctionReturnType<T>, DefaultError>, ReservedQueryOptions$2>>;
/** Query options return type */
type QueryOptsReturn<T extends FunctionReference<'query'>> = ConvexQueryOptions<T> & {
meta: ConvexQueryMeta;
};
/** Action query options parameter type (actions don't support subscriptions) */
type ActionQueryOptsParam<T extends FunctionReference<'action'>> = DistributiveOmit<UseQueryOptions<FunctionReturnType<T>, DefaultError>, ReservedQueryOptions$2>;
/** Action query options return type */
type ActionQueryOptsReturn<T extends FunctionReference<'action'>> = ConvexActionOptions<T>;
/**
* Decorated query procedure with queryOptions, queryKey, and queryFilter methods.
* Args are optional when the function has no required parameters.
* Supports skipToken for type-safe conditional queries.
*/
type DecorateQuery<T extends FunctionReference<'query'>> = {
queryOptions: keyof FunctionArgs<T> extends never ? (args?: EmptyObject | SkipToken, opts?: QueryOptsParam<T>) => QueryOptsReturn<T> : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T> | SkipToken, opts?: QueryOptsParam<T>) => QueryOptsReturn<T> : (args: FunctionArgs<T> | SkipToken, opts?: QueryOptsParam<T>) => QueryOptsReturn<T>; /** Static (non-hook) query options for event handlers and prefetching */
staticQueryOptions: keyof FunctionArgs<T> extends never ? (args?: EmptyObject | SkipToken, opts?: StaticQueryOptsParam) => ConvexQueryOptions<T> & {
meta: ConvexQueryMeta;
} : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T> | SkipToken, opts?: StaticQueryOptsParam) => ConvexQueryOptions<T> & {
meta: ConvexQueryMeta;
} : (args: FunctionArgs<T> | SkipToken, opts?: StaticQueryOptsParam) => ConvexQueryOptions<T> & {
meta: ConvexQueryMeta;
}; /** Get query key for QueryClient methods (setQueryData, getQueryData, etc.) */
queryKey: (args?: DeepPartial<FunctionArgs<T>>) => ConvexQueryKey<T>; /** Get query filter for QueryClient methods (invalidateQueries, removeQueries, etc.) */
queryFilter: (args?: DeepPartial<FunctionArgs<T>>, filters?: DistributiveOmit<QueryFilters, 'queryKey'>) => QueryFilters;
};
/** Options for infinite query - extends TanStack Query options */
type InfiniteQueryOptsParam<T extends FunctionReference<'query'> = FunctionReference<'query'>> = {
/** Items per page. Optional - server uses .paginated() default if not provided. */limit?: number; /** Skip query silently when unauthenticated */
skipUnauth?: boolean; /** Placeholder data shown while loading (item array, not pagination result) */
placeholderData?: ExtractPaginatedItem<FunctionReturnType<T>>[];
} & DistributiveOmit<UseQueryOptions<FunctionReturnType<T>, DefaultError>, ReservedInfiniteQueryOptions>;
/** Return type of infiniteQueryOptions - compatible with TanStack prefetch */
type ConvexInfiniteQueryOptions<T extends FunctionReference<'query'>> = Pick<UseQueryOptions<FunctionReturnType<T>, Error, FunctionReturnType<T>, ConvexQueryKey<T>>, 'queryKey' | 'staleTime' | 'enabled'> & {
meta: ConvexInfiniteQueryMeta;
refetchInterval: false;
refetchOnMount: false;
refetchOnReconnect: false;
refetchOnWindowFocus: false; /** Placeholder data shown while loading (item array, not pagination result) */
placeholderData?: ExtractPaginatedItem<FunctionReturnType<T>>[];
} & DistributiveOmit<UseQueryOptions<FunctionReturnType<T>, DefaultError>, ReservedInfiniteQueryOptions>;
/** Infinite query options with attached function reference (client-only) */
type ConvexInfiniteQueryOptionsWithRef<T extends FunctionReference<'query'>> = ConvexInfiniteQueryOptions<T> & {
[FUNC_REF_SYMBOL]: T;
};
/** Infinite query options return type */
type InfiniteQueryOptsReturn<T extends FunctionReference<'query'>> = ConvexInfiniteQueryOptionsWithRef<T>;
/**
* Decorated infinite query procedure.
* Only available on queries that have cursor/limit in their input (paginated).
* Supports skipToken for conditional queries.
* Args are optional when the function has no required parameters (besides cursor/limit).
*/
type DecorateInfiniteQuery<T extends FunctionReference<'query'>> = {
/** Create infinite query options for useInfiniteQuery and prefetch */infiniteQueryOptions: keyof InfiniteQueryInput<FunctionArgs<T>> extends never ? (args?: EmptyObject | SkipToken, opts?: InfiniteQueryOptsParam<T>) => InfiniteQueryOptsReturn<T> : EmptyObject extends InfiniteQueryInput<FunctionArgs<T>> ? (args?: InfiniteQueryInput<FunctionArgs<T>> | SkipToken, opts?: InfiniteQueryOptsParam<T>) => InfiniteQueryOptsReturn<T> : (args: InfiniteQueryInput<FunctionArgs<T>> | SkipToken, opts?: InfiniteQueryOptsParam<T>) => InfiniteQueryOptsReturn<T>; /** Get query key for infinite query (QueryClient methods like setQueryData, getQueryData) */
infiniteQueryKey: (args?: DeepPartial<InfiniteQueryInput<FunctionArgs<T>>>) => ConvexQueryKey<T>; /** Function metadata from server (auth, limit, rateLimit, role, type) */
meta: PaginatedFnMeta;
};
/**
* Decorated mutation procedure with mutationOptions and mutationKey methods.
*/
type DecorateMutation<T extends FunctionReference<'mutation'>> = {
mutationOptions: (opts?: DistributiveOmit<UseMutationOptions<FunctionReturnType<T>, DefaultError, MutationVariables<T>>, ReservedMutationOptions$2>) => UseMutationOptions<FunctionReturnType<T>, DefaultError, MutationVariables<T>>; /** Get mutation key for QueryClient methods */
mutationKey: () => ConvexMutationKey;
};
/**
* Decorated action procedure with queryOptions, mutationOptions, and key methods.
* Actions can be used as one-shot queries (no subscription) or as mutations.
* Supports skipToken for conditional queries.
*/
type DecorateAction<T extends FunctionReference<'action'>> = {
/** Use action as a one-shot query (no WebSocket subscription) */queryOptions: keyof FunctionArgs<T> extends never ? (args?: EmptyObject | SkipToken, opts?: ActionQueryOptsParam<T>) => ActionQueryOptsReturn<T> : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T> | SkipToken, opts?: ActionQueryOptsParam<T>) => ActionQueryOptsReturn<T> : (args: FunctionArgs<T> | SkipToken, opts?: ActionQueryOptsParam<T>) => ActionQueryOptsReturn<T>; /** Static (non-hook) action query options for event handlers and prefetching */
staticQueryOptions: keyof FunctionArgs<T> extends never ? (args?: EmptyObject | SkipToken, opts?: StaticQueryOptsParam) => ConvexActionOptions<T> & {
meta: ConvexQueryMeta;
} : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T> | SkipToken, opts?: StaticQueryOptsParam) => ConvexActionOptions<T> & {
meta: ConvexQueryMeta;
} : (args: FunctionArgs<T> | SkipToken, opts?: StaticQueryOptsParam) => ConvexActionOptions<T> & {
meta: ConvexQueryMeta;
}; /** Use action as a mutation */
mutationOptions: (opts?: DistributiveOmit<UseMutationOptions<FunctionReturnType<T>, DefaultError, MutationVariables<T>>, ReservedMutationOptions$2>) => UseMutationOptions<FunctionReturnType<T>, DefaultError, MutationVariables<T>>; /** Get mutation key for QueryClient methods */
mutationKey: () => ConvexMutationKey; /** Get query key for QueryClient methods */
queryKey: (args?: DeepPartial<FunctionArgs<T>>) => ConvexActionKey<T>; /** Get query filter for QueryClient methods */
queryFilter: (args?: DeepPartial<FunctionArgs<T>>, filters?: DistributiveOmit<QueryFilters, 'queryKey'>) => QueryFilters;
};
/** Vanilla query - direct .query() and .watchQuery() calls without React Query */
type VanillaQuery<T extends FunctionReference<'query'>> = {
query: keyof FunctionArgs<T> extends never ? (args?: EmptyObject) => Promise<FunctionReturnType<T>> : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T>) => Promise<FunctionReturnType<T>> : (args: FunctionArgs<T>) => Promise<FunctionReturnType<T>>;
watchQuery: keyof FunctionArgs<T> extends never ? (args?: EmptyObject, opts?: WatchQueryOptions) => Watch<FunctionReturnType<T>> : EmptyObject extends FunctionArgs<T> ? (args?: FunctionArgs<T>, opts?: WatchQueryOptions) => Watch<FunctionReturnType<T>> : (args: FunctionArgs<T>, opts?: WatchQueryOptions) => Watch<FunctionReturnType<T>>;
};
/**
* Recursively creates vanilla client type for direct procedural calls.
*/
type VanillaCRPCClient<TApi> = { [K in keyof TApi as K extends string ? K extends `_${string}` ? never : K : K]: TApi[K] extends FunctionReference<'query'> ? VanillaQuery<TApi[K]> : TApi[K] extends FunctionReference<'mutation'> ? VanillaMutation<TApi[K]> : TApi[K] extends FunctionReference<'action'> ? VanillaAction<TApi[K]> : TApi[K] extends Record<string, unknown> ? VanillaCRPCClient<TApi[K]> : never };
/**
* Recursively decorates all procedures in a Convex API object.
*/
type CRPCClient<TApi> = { [K in keyof TApi as K extends string ? K extends `_${string}` ? never : K : K]: TApi[K] extends FunctionReference<'query'> ? IsPaginated<FunctionArgs<TApi[K]>> extends true ? DecorateQuery<TApi[K]> & DecorateInfiniteQuery<TApi[K]> : DecorateQuery<TApi[K]> : TApi[K] extends FunctionReference<'mutation'> ? DecorateMutation<TApi[K]> : TApi[K] extends FunctionReference<'action'> ? DecorateAction<TApi[K]> : TApi[K] extends Record<string, unknown> ? CRPCClient<TApi[K]> : never };
//#endregion
//#region src/crpc/http-client.d.ts
/** Form value types (matches Hono's FormValue) */
type HttpFormValue = string | Blob;
/**
* Hybrid input args: JSON body fields at root, explicit params/searchParams/form.
* - JSON body: spread at root level (tRPC-style)
* - Path params: { params: { id: '123' } }
* - Query params: { searchParams: { limit: '10' } }
* - Form data: { form: { file: blob } } - typed via .form() builder
* - Client options: { headers, fetch, init } - for per-call customization
*/
type HttpInputArgs = {
/** Path parameters (e.g., :id in /users/:id) */params?: Record<string, string>; /** Query string parameters */
searchParams?: Record<string, string | string[]>; /** Form data body (Content-Type: multipart/form-data) - typed via .form() builder */
form?: Record<string, HttpFormValue | HttpFormValue[]>; /** Custom fetch function (per-call override) */
fetch?: typeof fetch; /** Standard RequestInit (per-call override) */
init?: RequestInit; /** Additional headers (per-call override) */
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>); /** Any other properties are JSON body fields */
[key: string]: unknown;
};
/**
* Client request options (matches Hono's ClientRequestOptions).
* Standard RequestInit in `init` takes highest priority and can override
* things that are set automatically like body, method, headers.
*/
type HttpClientOptions = {
/** Custom fetch function */fetch?: typeof fetch;
/**
* Standard RequestInit - takes highest priority.
* Can override body, method, headers if needed.
*/
init?: RequestInit; /** Additional headers (or async function returning headers) */
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
};
//#endregion
//#region src/react/http-proxy.d.ts
type HttpRouteInfo = {
path: string;
method: string;
};
type HttpRouteMap = Record<string, HttpRouteInfo>;
/** Infer schema type or return empty object if UnsetMarker */
type InferSchemaOrEmpty<T> = T extends UnsetMarker ? object : T extends z.ZodTypeAny ? z.infer<T> : object;
/** Infer merged input from HttpProcedure (flat - used internally) */
type InferHttpInput<T> = T extends HttpProcedure<infer TInput, infer _TOutput, infer TParams, infer TQuery> ? Simplify<InferSchemaOrEmpty<TParams> & InferSchemaOrEmpty<TQuery> & InferSchemaOrEmpty<TInput>> : object;
/**
* Extract string keys from a Zod object schema.
* Used for param/query which are always strings in URLs.
*/
type ZodObjectKeys<T> = T extends z.ZodObject<infer Shape> ? { [K in keyof Shape]: string } : Record<string, string>;
/**
* Extract string or string[] keys from a Zod object schema.
* Query params can have array values.
*/
type ZodQueryKeys<T> = T extends z.ZodObject<infer Shape> ? { [K in keyof Shape]?: string | string[] } : Record<string, string | string[]>;
/**
* Infer client-side args from HttpProcedure with proper nesting.
* - params: only present if TParams is defined, always strings (URL path params)
* - searchParams: only present if TQuery is defined, always strings (URL query params)
* - form: only present if TForm is defined, typed from schema
* - JSON body fields spread at root level (typed from schema)
* - Client options (fetch, init, headers) always optional for per-call overrides
*/
type InferHttpClientArgs<T> = T extends HttpProcedure<infer TInput, infer _TOutput, infer TParams, infer TQuery, infer _TMethod, infer TForm> ? Simplify<(TParams extends UnsetMarker ? object : {
params: ZodObjectKeys<TParams>;
}) & (TQuery extends UnsetMarker ? object : {
searchParams: ZodQueryKeys<TQuery>;
}) & (TForm extends UnsetMarker ? object : TForm extends z.ZodTypeAny ? {
form: z.infer<TForm>;
} : object) & (TInput extends UnsetMarker ? object : TInput extends z.ZodTypeAny ? z.infer<TInput> : object) & {
fetch?: typeof fetch;
init?: RequestInit;
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
}> : HttpInputArgs;
/** Infer output type from HttpProcedure */
type InferHttpOutput<T> = T extends HttpProcedure<infer _TInput, infer TOutput, infer _TParams, infer _TQuery> ? TOutput extends UnsetMarker ? unknown : TOutput extends z.ZodTypeAny ? z.infer<TOutput> : unknown : unknown;
/** Query key with args (3-element) or prefix key without args (2-element) for invalidation */
type HttpQueryKey = readonly ['httpQuery', string, unknown] | readonly ['httpQuery', string];
type HttpMutationKey = readonly ['httpMutation', string];
type ReservedQueryOptions$1 = 'queryKey' | 'queryFn';
type ReservedMutationOptions$1 = 'mutationFn';
/** Query options for GET HTTP endpoints - compatible with both useQuery and useSuspenseQuery */
type HttpQueryOptsReturn<T extends HttpProcedure> = Omit<UseQueryOptions<InferHttpOutput<T>, Error, InferHttpOutput<T>, HttpQueryKey>, 'queryFn'> & {
queryFn: () => Promise<InferHttpOutput<T>>;
};
/** Mutation options for POST/PUT/PATCH/DELETE HTTP endpoints - typed variables */
type HttpMutationOptsReturn<T extends HttpProcedure> = UseMutationOptions<InferHttpOutput<T>, DefaultError, InferHttpClientArgs<T>>;
/** Query options (TanStack Query only - client opts go in args) */
type HttpQueryOptions<T extends HttpProcedure> = DistributiveOmit<HttpQueryOptsReturn<T>, ReservedQueryOptions$1>;
/** Mutation options (TanStack Query only - client opts go in mutate args) */
type HttpMutationOptions<T extends HttpProcedure> = DistributiveOmit<HttpMutationOptsReturn<T>, ReservedMutationOptions$1>;
/**
* Decorated GET procedure with queryOptions and mutationOptions.
* - queryOptions: For cached data fetching (useQuery/useSuspenseQuery)
* - mutationOptions: For one-time actions like exports (useMutation)
*/
type DecorateHttpQuery<T extends HttpProcedure> = {
queryOptions: keyof InferHttpInput<T> extends never ? (args?: InferHttpClientArgs<T>, opts?: HttpQueryOptions<T>) => HttpQueryOptsReturn<T> : object extends InferHttpInput<T> ? (args?: InferHttpClientArgs<T>, opts?: HttpQueryOptions<T>) => HttpQueryOptsReturn<T> : (args: InferHttpClientArgs<T>, opts?: HttpQueryOptions<T>) => HttpQueryOptsReturn<T>; /** Get query key for QueryClient methods (with args = exact match, without = prefix) */
queryKey: (args?: InferHttpClientArgs<T>) => HttpQueryKey; /** Get query filter for QueryClient methods (e.g., invalidateQueries) */
queryFilter: (args?: InferHttpClientArgs<T>, filters?: DistributiveOmit<QueryFilters, 'queryKey'>) => QueryFilters; /** Mutation options for GET endpoints (exports, downloads - no caching) */
mutationOptions: (opts?: HttpMutationOptions<T>) => HttpMutationOptsReturn<T>; /** Get mutation key for QueryClient methods */
mutationKey: () => HttpMutationKey;
};
/**
* Decorated POST/PUT/PATCH/DELETE procedure with mutationOptions.
* The mutationFn receives typed args inferred from server schemas.
*/
type DecorateHttpMutation<T extends HttpProcedure> = {
mutationOptions: (opts?: HttpMutationOptions<T>) => HttpMutationOptsReturn<T>; /** Get mutation key for QueryClient methods */
mutationKey: () => HttpMutationKey;
};
/** Vanilla HTTP query - only direct call, no React Query */
type VanillaHttpQuery<T extends HttpProcedure> = {
query: keyof InferHttpInput<T> extends never ? (args?: InferHttpClientArgs<T>) => Promise<InferHttpOutput<T>> : object extends InferHttpInput<T> ? (args?: InferHttpClientArgs<T>) => Promise<InferHttpOutput<T>> : (args: InferHttpClientArgs<T>) => Promise<InferHttpOutput<T>>;
};
/** Vanilla HTTP mutation - only direct call, no React Query */
type VanillaHttpMutation<T extends HttpProcedure> = {
mutate: keyof InferHttpInput<T> extends never ? (args?: InferHttpClientArgs<T>) => Promise<InferHttpOutput<T>> : object extends InferHttpInput<T> ? (args?: InferHttpClientArgs<T>) => Promise<InferHttpOutput<T>> : (args: InferHttpClientArgs<T>) => Promise<InferHttpOutput<T>>;
};
/** Vanilla HTTP client type - only query/mutate methods */
type VanillaHttpCRPCClient<T extends HttpRouterRecord> = { [K in keyof T]: T[K] extends HttpProcedure<infer _TInput, infer _TOutput, infer _TParams, infer _TQuery, infer TMethod, infer _TForm> ? TMethod extends 'GET' ? VanillaHttpQuery<T[K]> : VanillaHttpMutation<T[K]> : T[K] extends CRPCHttpRouter<infer R> ? VanillaHttpCRPCClient<R> : T[K] extends HttpRouterRecord ? VanillaHttpCRPCClient<T[K]> : never };
/** Extract vanilla HTTP client from router */
type VanillaHttpCRPCClientFromRouter<T> = T extends CRPCHttpRouter<infer R> ? VanillaHttpCRPCClient<R> : never;
/**
* HTTP Client type from router record.
* Maps each procedure to queryOptions (GET) or mutationOptions (POST/etc).
* Uses infer to extract the method type literal for proper GET/non-GET distinction.
*/
type HttpCRPCClient<T extends HttpRouterRecord> = { [K in keyof T]: T[K] extends HttpProcedure<infer _TInput, infer _TOutput, infer _TParams, infer _TQuery, infer TMethod, infer _TForm> ? TMethod extends 'GET' ? DecorateHttpQuery<T[K]> : DecorateHttpMutation<T[K]> : T[K] extends CRPCHttpRouter<infer R> ? HttpCRPCClient<R> : T[K] extends HttpRouterRecord ? HttpCRPCClient<T[K]> : never };
/**
* HTTP Client type from a CRPCHttpRouter.
* Use this when your type is the router object (with _def).
*/
type HttpCRPCClientFromRouter<TRouter extends CRPCHttpRouter<any>> = HttpCRPCClient<TRouter['_def']['record']>;
interface HttpProxyOptions<TRoutes extends HttpRouteMap> {
/** Base URL for the Convex HTTP API (e.g., https://your-site.convex.site) */
convexSiteUrl: string;
/** Custom fetch function (defaults to global fetch) */
fetch?: typeof fetch;
/** Default headers or async function returning headers (for auth tokens) */
headers?: {
[key: string]: string | undefined;
} | (() => {
[key: string]: string | undefined;
} | Promise<{
[key: string]: string | undefined;
}>);
/** Error handler called on HTTP errors */
onError?: (error: HttpClientError) => void;
/** Runtime route definitions (from codegen httpRoutes) */
routes: TRoutes;
/** Optional payload transformer (always composed with built-in Date support). */
transformer?: DataTransformerOptions;
}
/**
* Create an HTTP proxy with TanStack Query integration.
*
* Returns a proxy that provides:
* - `queryOptions` for GET endpoints (no subscription)
* - `mutationOptions` for POST/PUT/PATCH/DELETE endpoints
*
* @example
* ```ts
* const httpProxy = createHttpProxy<AppRouter>({
* convexSiteUrl: process.env.NEXT_PUBLIC_CONVEX_SITE_URL!,
* routes: httpRoutes,
* });
*
* // GET endpoint
* const opts = httpProxy.todos.get.queryOptions({ id: '123' });
* const { data } = useQuery(opts);
*
* // POST endpoint
* const mutation = useMutation(httpProxy.todos.create.mutationOptions());
* await mutation.mutateAsync({ title: 'New todo' });
* ```
*/
declare function createHttpProxy<TRouter extends CRPCHttpRouter<any>, TRoutes extends HttpRouteMap = HttpRouteMap>(opts: HttpProxyOptions<TRoutes>): HttpCRPCClientFromRouter<TRouter>;
//#endregion
//#region src/react/context.d.ts
/** Access ConvexQueryClient (e.g., for logout cleanup) */
declare const useConvexQueryClient: () => ConvexQueryClient | null;
/**
* Hook to access the meta object from context.
* Returns undefined if meta was not provided to createCRPCContext.
*/
declare function useMeta(): Meta | undefined;
/**
* Hook to get auth type for a function from meta.
*/
declare function useFnMeta(): (namespace: string, fnName: string) => FnMeta$1 | undefined;
/** Headers record that allows empty objects and optional properties */
type HeadersInput = {
[key: string]: string | undefined;
};
type CRPCHttpOptions = {
/** Base URL for the Convex HTTP API (e.g., https://your-site.convex.site) */convexSiteUrl: string; /** Default headers or async function returning headers (for auth tokens) */
headers?: HeadersInput | (() => HeadersInput | Promise<HeadersInput>); /** Custom fetch function (defaults to global fetch) */
fetch?: typeof fetch; /** Error handler called on HTTP errors */
onError?: (error: HttpClientError) => void;
};
type CreateCRPCContextOptions<TApi> = {
api: TApi; /** Optional payload transformer (always composed with built-in Date support). */
transformer?: DataTransformerOptions;
} & Partial<CRPCHttpOptions>;
/**
* Extract HTTP router from TApi['http'] if present (optional).
* Uses NonNullable to handle optional http property.
*/
type ExtractHttpRouter<TApi> = TApi extends {
http?: infer R;
} ? NonNullable<R> extends CRPCHttpRouter<HttpRouterRecord> ? NonNullable<R> : undefined : undefined;
/**
* Create CRPC context, provider, and hooks for a Convex API.
*
* @param options - Configuration object containing api and optional HTTP settings
* @returns Object with CRPCProvider, useCRPC, and useCRPCClient
*
* @example
* ```tsx
* // lib/crpc.ts
* import { api } from '@convex/api';
* import { createCRPCContext } from 'kitcn/react';
*
* // Works for both regular Convex functions and generated HTTP router types
* export const { useCRPC } = createCRPCContext({
* api,
* convexSiteUrl: process.env.NEXT_PUBLIC_CONVEX_SITE_URL!,
* });
*
* // components/user-profile.tsx
* function UserProfile({ id }) {
* const crpc = useCRPC();
* const { data } = useQuery(crpc.user.get.queryOptions({ id }));
*
* // HTTP endpoints (if configured)
* const { data: httpData } = useQuery(crpc.http.todos.get.queryOptions({ id }));
* }
* ```
*/
declare function createCRPCContext<TApi extends Record<string, unknown>>(options: CreateCRPCContextOptions<TApi>): {
CRPCProvider: ({
children,
convexClient,
convexQueryClient
}: {
children: ReactNode;
convexClient: ConvexReactClient$1;
convexQueryClient: ConvexQueryClient;
}) => react_jsx_runtime0.JSX.Element;
useCRPC: () => Extract