@richie-rpc/react-query 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -169
- package/dist/cjs/index.cjs +203 -38
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/index.mjs +206 -38
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/index.d.ts +195 -57
- package/package.json +9 -9
package/dist/mjs/index.mjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type {\n Client,\n ClientMethod,\n EndpointRequestOptions,\n EndpointResponse,\n SSEClientMethod,\n StreamingClientMethod,\n} from '@richie-rpc/client';\nimport type {\n Contract,\n SSEEndpointDefinition,\n StandardEndpointDefinition,\n StreamingEndpointDefinition,\n} from '@richie-rpc/core';\nimport {\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n type UseSuspenseQueryOptions,\n type UseSuspenseQueryResult,\n useMutation,\n useQuery,\n useSuspenseQuery,\n} from '@tanstack/react-query';\n\n// HTTP methods that should use query hooks (read operations)\ntype QueryMethods = 'GET' | 'HEAD';\n\n// HTTP methods that should use mutation hooks (write operations)\ntype MutationMethods = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';\n\n/**\n * Hook wrapper for query endpoints (GET, HEAD)\n * Provides useQuery and useSuspenseQuery methods\n */\nexport type QueryHook<T extends StandardEndpointDefinition> = {\n /**\n * Standard query hook that returns loading states\n */\n useQuery: (\n options: EndpointRequestOptions<T>,\n queryOptions?: Omit<UseQueryOptions<EndpointResponse<T>, Error>, 'queryKey' | 'queryFn'>,\n ) => UseQueryResult<EndpointResponse<T>, Error>;\n\n /**\n * Suspense-enabled query hook that throws promises for React Suspense\n */\n useSuspenseQuery: (\n options: EndpointRequestOptions<T>,\n queryOptions?: Omit<\n UseSuspenseQueryOptions<EndpointResponse<T>, Error>,\n 'queryKey' | 'queryFn'\n >,\n ) => UseSuspenseQueryResult<EndpointResponse<T>, Error>;\n};\n\n/**\n * Hook wrapper for mutation endpoints (POST, PUT, PATCH, DELETE)\n * Provides useMutation method\n */\nexport type MutationHook<T extends StandardEndpointDefinition> = {\n /**\n * Mutation hook for write operations\n */\n useMutation: (\n mutationOptions?: Omit<\n UseMutationOptions<EndpointResponse<T>, Error, EndpointRequestOptions<T>>,\n 'mutationFn'\n >,\n ) => UseMutationResult<EndpointResponse<T>, Error, EndpointRequestOptions<T>>;\n};\n\n/**\n * Conditionally apply hook type based on HTTP method\n */\nexport type EndpointHook<T extends StandardEndpointDefinition> = T['method'] extends QueryMethods\n ? QueryHook<T>\n : T['method'] extends MutationMethods\n ? MutationHook<T>\n : never;\n\n/**\n * Hook wrapper for streaming endpoints\n * Exposes the streaming client method directly since React Query\n * doesn't fit well with long-lived streaming connections\n */\nexport type StreamingHook<T extends StreamingEndpointDefinition> = {\n /**\n * Start a streaming request\n */\n stream: StreamingClientMethod<T>;\n};\n\n/**\n * Hook wrapper for SSE endpoints\n * Exposes the SSE client method directly since React Query\n * doesn't fit well with long-lived SSE connections\n */\nexport type SSEHook<T extends SSEEndpointDefinition> = {\n /**\n * Create an SSE connection\n */\n connect: SSEClientMethod<T>;\n};\n\n/**\n * Complete hooks object for a contract\n * Each endpoint gets appropriate hooks based on its type and HTTP method\n */\nexport type Hooks<T extends Contract> = {\n [K in keyof T]: T[K] extends StandardEndpointDefinition\n ? EndpointHook<T[K]>\n : T[K] extends StreamingEndpointDefinition\n ? StreamingHook<T[K]>\n : T[K] extends SSEEndpointDefinition\n ? SSEHook<T[K]>\n : never;\n};\n\n/**\n * Create typed React hooks for all endpoints in a contract\n *\n * Query endpoints (GET, HEAD) get useQuery and useSuspenseQuery methods\n * Mutation endpoints (POST, PUT, PATCH, DELETE) get useMutation method\n *\n * @param client - The typed client created with createClient()\n * @param contract - The contract definition\n * @returns Hooks object with methods for each endpoint\n *\n * @example\n * ```tsx\n * const client = createClient(contract, { baseUrl: 'http://localhost:3000' });\n * const hooks = createHooks(client, contract);\n *\n * // In a component - Query\n * function UserList() {\n * const { data, isLoading } = hooks.listUsers.useQuery({\n * query: { limit: \"10\" }\n * });\n * // ...\n * }\n *\n * // In a component - Mutation\n * function CreateUser() {\n * const mutation = hooks.createUser.useMutation();\n * return (\n * <button onClick={() => mutation.mutate({\n * body: { name: \"Alice\", email: \"alice@example.com\" }\n * })}>\n * Create User\n * </button>\n * );\n * }\n * ```\n */\nexport function createHooks<T extends Contract>(client: Client<T>, contract: T): Hooks<T> {\n const hooks: Record<string, unknown> = {};\n\n for (const [name, endpoint] of Object.entries(contract)) {\n // Handle streaming endpoints\n if (endpoint.type === 'streaming') {\n const streamMethod = client[\n name as keyof T\n ] as unknown as StreamingClientMethod<StreamingEndpointDefinition>;\n hooks[name] = {\n stream: streamMethod,\n };\n continue;\n }\n\n // Handle SSE endpoints\n if (endpoint.type === 'sse') {\n const connectMethod = client[\n name as keyof T\n ] as unknown as SSEClientMethod<SSEEndpointDefinition>;\n hooks[name] = {\n connect: connectMethod,\n };\n continue;\n }\n\n // Handle standard endpoints\n const method = endpoint.method;\n const clientMethod = client[\n name as keyof T\n ] as unknown as ClientMethod<StandardEndpointDefinition>;\n\n if (method === 'GET' || method === 'HEAD') {\n // Create query hooks for read operations\n hooks[name] = {\n useQuery: (\n options: EndpointRequestOptions<StandardEndpointDefinition>,\n queryOptions?: Omit<\n UseQueryOptions<EndpointResponse<StandardEndpointDefinition>, Error>,\n 'queryKey' | 'queryFn'\n >,\n ) => {\n return useQuery({\n queryKey: [\n name,\n options.params ?? null,\n options.query ?? null,\n options.headers ?? null,\n options.body ?? null,\n ],\n queryFn: () => clientMethod(options),\n ...queryOptions,\n });\n },\n useSuspenseQuery: (\n options: EndpointRequestOptions<StandardEndpointDefinition>,\n queryOptions?: Omit<\n UseSuspenseQueryOptions<EndpointResponse<StandardEndpointDefinition>, Error>,\n 'queryKey' | 'queryFn'\n >,\n ) => {\n return useSuspenseQuery({\n queryKey: [\n name,\n options.params ?? null,\n options.query ?? null,\n options.headers ?? null,\n options.body ?? null,\n ],\n queryFn: () => clientMethod(options),\n ...queryOptions,\n });\n },\n };\n } else {\n // Create mutation hooks for write operations\n hooks[name] = {\n useMutation: (\n mutationOptions?: Omit<\n UseMutationOptions<\n EndpointResponse<StandardEndpointDefinition>,\n Error,\n EndpointRequestOptions<StandardEndpointDefinition>\n >,\n 'mutationFn'\n >,\n ) => {\n return useMutation({\n mutationFn: (options: EndpointRequestOptions<StandardEndpointDefinition>) =>\n clientMethod(options),\n ...mutationOptions,\n });\n },\n };\n }\n }\n\n return hooks as Hooks<T>;\n}\n"
|
|
5
|
+
"/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type {\n Client,\n ClientMethod,\n DownloadClientMethod,\n EndpointRequestOptions,\n EndpointResponse,\n SSEClientMethod,\n StreamingClientMethod,\n} from '@richie-rpc/client';\nimport type {\n Contract,\n DownloadEndpointDefinition,\n ExtractChunk,\n SSEEndpointDefinition,\n StandardEndpointDefinition,\n StreamingEndpointDefinition,\n} from '@richie-rpc/core';\nimport {\n type InfiniteData,\n type QueryClient,\n type QueryKey,\n type Updater,\n type UseInfiniteQueryOptions,\n type UseInfiniteQueryResult,\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n type UseSuspenseInfiniteQueryOptions,\n type UseSuspenseInfiniteQueryResult,\n type UseSuspenseQueryOptions,\n type UseSuspenseQueryResult,\n experimental_streamedQuery as streamedQuery,\n useInfiniteQuery,\n useMutation,\n useQuery,\n useSuspenseInfiniteQuery,\n useSuspenseQuery,\n} from '@tanstack/react-query';\n\n// ============================================\n// HTTP Method Categories\n// ============================================\n\ntype QueryMethods = 'GET' | 'HEAD';\ntype MutationMethods = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';\n\n// ============================================\n// Query Options Types (ts-rest style)\n// ============================================\n\n/**\n * Unified query options - combines queryKey, queryData, and TanStack Query options\n */\nexport type TsrQueryOptions<T extends StandardEndpointDefinition> = Omit<\n UseQueryOptions<EndpointResponse<T>, Error>,\n 'queryKey' | 'queryFn'\n> & {\n queryKey: QueryKey;\n queryData: EndpointRequestOptions<T>;\n};\n\n/**\n * Suspense query options\n */\nexport type TsrSuspenseQueryOptions<T extends StandardEndpointDefinition> = Omit<\n UseSuspenseQueryOptions<EndpointResponse<T>, Error>,\n 'queryKey' | 'queryFn'\n> & {\n queryKey: QueryKey;\n queryData: EndpointRequestOptions<T>;\n};\n\n/**\n * Infinite query options - queryData is a function that receives pageParam\n */\nexport type TsrInfiniteQueryOptions<\n T extends StandardEndpointDefinition,\n TPageParam = unknown,\n> = Omit<\n UseInfiniteQueryOptions<EndpointResponse<T>, Error, unknown, QueryKey, TPageParam>,\n 'queryKey' | 'queryFn'\n> & {\n queryKey: QueryKey;\n queryData: (context: { pageParam: TPageParam }) => EndpointRequestOptions<T>;\n};\n\n/**\n * Suspense infinite query options\n */\nexport type TsrSuspenseInfiniteQueryOptions<\n T extends StandardEndpointDefinition,\n TPageParam = unknown,\n> = Omit<\n UseSuspenseInfiniteQueryOptions<EndpointResponse<T>, Error, unknown, QueryKey, TPageParam>,\n 'queryKey' | 'queryFn'\n> & {\n queryKey: QueryKey;\n queryData: (context: { pageParam: TPageParam }) => EndpointRequestOptions<T>;\n};\n\n/**\n * Mutation options - same as TanStack Query but typed\n */\nexport type TsrMutationOptions<T extends StandardEndpointDefinition> = Omit<\n UseMutationOptions<EndpointResponse<T>, Error, EndpointRequestOptions<T>>,\n 'mutationFn'\n>;\n\n/**\n * Stream query options for streaming endpoints\n */\nexport type TsrStreamQueryOptions<T extends StreamingEndpointDefinition> = Omit<\n UseQueryOptions<ExtractChunk<T>[], Error>,\n 'queryKey' | 'queryFn'\n> & {\n queryKey: QueryKey;\n queryData: EndpointRequestOptions<T>;\n refetchMode?: 'reset' | 'append' | 'replace';\n};\n\n// ============================================\n// Error Handling Utilities\n// ============================================\n\n/**\n * Response type from hooks - includes status and body like ts-rest\n */\nexport type TsrResponse<T extends StandardEndpointDefinition> = EndpointResponse<T>;\n\n/**\n * Error response type - either fetch error or typed response error\n */\nexport type TsrError =\n | Error\n | {\n status: number;\n data: unknown;\n };\n\n/**\n * Check if an error is a fetch/network error (Error instance, not a response)\n */\nexport function isFetchError(error: unknown): error is Error {\n return error instanceof Error && !('status' in error);\n}\n\n/**\n * Check if error is a response with a status code not defined in the contract\n */\nexport function isUnknownErrorResponse<T extends StandardEndpointDefinition>(\n error: unknown,\n endpoint: T,\n): error is { status: number; data: unknown } {\n if (!error || typeof error !== 'object' || !('status' in error)) {\n return false;\n }\n const status = (error as { status: number }).status;\n return !(status in endpoint.responses);\n}\n\n/**\n * Check if error is either a fetch error or unknown response error\n */\nexport function isNotKnownResponseError<T extends StandardEndpointDefinition>(\n error: unknown,\n endpoint: T,\n): error is Error | { status: number; data: unknown } {\n return isFetchError(error) || isUnknownErrorResponse(error, endpoint);\n}\n\n/**\n * Exhaustive guard for compile-time exhaustiveness checking\n * Use after handling all known error cases to ensure nothing is missed\n */\nexport function exhaustiveGuard(_value: never): never {\n throw new Error(`Unhandled case: ${JSON.stringify(_value)}`);\n}\n\n// ============================================\n// Typed QueryClient Types\n// ============================================\n\n/**\n * Typed query client methods for a single query endpoint\n */\nexport type TypedQueryEndpointClient<T extends StandardEndpointDefinition> = {\n getQueryData: (queryKey: QueryKey) => EndpointResponse<T> | undefined;\n setQueryData: (\n queryKey: QueryKey,\n updater: Updater<EndpointResponse<T> | undefined, EndpointResponse<T> | undefined>,\n ) => EndpointResponse<T> | undefined;\n getQueryState: (queryKey: QueryKey) => ReturnType<QueryClient['getQueryState']>;\n fetchQuery: (options: TsrQueryOptions<T>) => Promise<EndpointResponse<T>>;\n prefetchQuery: (options: TsrQueryOptions<T>) => Promise<void>;\n ensureQueryData: (options: TsrQueryOptions<T>) => Promise<EndpointResponse<T>>;\n};\n\n/**\n * Typed query client methods for a single mutation endpoint\n * Mutations don't have query-specific cache methods\n */\nexport type TypedMutationEndpointClient<_T extends StandardEndpointDefinition> = object;\n\n/**\n * Full typed query client - extends QueryClient with per-endpoint methods\n */\nexport type TypedQueryClient<T extends Contract> = QueryClient & {\n [K in keyof T]: T[K] extends StandardEndpointDefinition\n ? T[K]['method'] extends QueryMethods\n ? TypedQueryEndpointClient<T[K]>\n : TypedMutationEndpointClient<T[K]>\n : Record<string, never>;\n};\n\n// ============================================\n// Endpoint API Types\n// ============================================\n\n/**\n * API for query endpoints (GET, HEAD)\n */\nexport type QueryEndpointApi<T extends StandardEndpointDefinition> = {\n /** Standard query hook */\n useQuery: (options: TsrQueryOptions<T>) => UseQueryResult<EndpointResponse<T>, Error> & {\n contractEndpoint: T;\n };\n /** Suspense query hook */\n useSuspenseQuery: (options: TsrSuspenseQueryOptions<T>) => UseSuspenseQueryResult<EndpointResponse<T>, Error> & {\n contractEndpoint: T;\n };\n /** Infinite query hook */\n useInfiniteQuery: <TPageParam = unknown>(\n options: TsrInfiniteQueryOptions<T, TPageParam>,\n ) => UseInfiniteQueryResult<InfiniteData<EndpointResponse<T>, TPageParam>, Error> & {\n contractEndpoint: T;\n };\n /** Suspense infinite query hook */\n useSuspenseInfiniteQuery: <TPageParam = unknown>(\n options: TsrSuspenseInfiniteQueryOptions<T, TPageParam>,\n ) => UseSuspenseInfiniteQueryResult<InfiniteData<EndpointResponse<T>, TPageParam>, Error> & {\n contractEndpoint: T;\n };\n /** Direct fetch without React Query */\n query: (options: EndpointRequestOptions<T>) => Promise<EndpointResponse<T>>;\n};\n\n/**\n * API for mutation endpoints (POST, PUT, PATCH, DELETE)\n */\nexport type MutationEndpointApi<T extends StandardEndpointDefinition> = {\n /** Mutation hook */\n useMutation: (\n options?: TsrMutationOptions<T>,\n ) => UseMutationResult<EndpointResponse<T>, Error, EndpointRequestOptions<T>> & {\n contractEndpoint: T;\n };\n /** Direct mutate without React Query */\n mutate: (options: EndpointRequestOptions<T>) => Promise<EndpointResponse<T>>;\n};\n\n/**\n * API for streaming endpoints\n */\nexport type StreamingEndpointApi<T extends StreamingEndpointDefinition> = {\n /** Direct stream access (event-based) */\n stream: StreamingClientMethod<T>;\n /** Query hook using experimental streamedQuery */\n useStreamQuery: (options: TsrStreamQueryOptions<T>) => UseQueryResult<ExtractChunk<T>[], Error> & {\n contractEndpoint: T;\n };\n};\n\n/**\n * API for SSE endpoints\n */\nexport type SSEEndpointApi<T extends SSEEndpointDefinition> = {\n /** Direct SSE connection access (event-based) */\n connect: SSEClientMethod<T>;\n};\n\n/**\n * API for download endpoints\n */\nexport type DownloadEndpointApi<T extends DownloadEndpointDefinition> = {\n /** Direct download without React Query */\n download: DownloadClientMethod<T>;\n};\n\n/**\n * Select appropriate API type based on endpoint type\n */\nexport type EndpointApi<T> = T extends StandardEndpointDefinition\n ? T['method'] extends QueryMethods\n ? QueryEndpointApi<T>\n : T['method'] extends MutationMethods\n ? MutationEndpointApi<T>\n : never\n : T extends StreamingEndpointDefinition\n ? StreamingEndpointApi<T>\n : T extends SSEEndpointDefinition\n ? SSEEndpointApi<T>\n : T extends DownloadEndpointDefinition\n ? DownloadEndpointApi<T>\n : never;\n\n// ============================================\n// Main TanStack Query API Type\n// ============================================\n\n/**\n * Full TanStack Query API for a contract\n */\nexport type TanstackQueryApi<T extends Contract> = {\n [K in keyof T]: EndpointApi<T[K]>;\n};\n\n\n// ============================================\n// Async Iterator Adapter for Streaming\n// ============================================\n\n/**\n * Convert StreamingResult to an AsyncIterable for use with streamedQuery\n */\nfunction streamToAsyncIterable<T extends StreamingEndpointDefinition>(\n streamingMethod: StreamingClientMethod<T>,\n options: EndpointRequestOptions<T>,\n): () => Promise<AsyncIterable<ExtractChunk<T>>> {\n return async () => {\n const result = await streamingMethod(options);\n\n return {\n [Symbol.asyncIterator](): AsyncIterator<ExtractChunk<T>> {\n let resolveNext: ((value: IteratorResult<ExtractChunk<T>>) => void) | null = null;\n let rejectNext: ((error: Error) => void) | null = null;\n const queue: ExtractChunk<T>[] = [];\n let done = false;\n let error: Error | null = null;\n\n result.on('chunk', (chunk) => {\n if (resolveNext) {\n resolveNext({ value: chunk, done: false });\n resolveNext = null;\n rejectNext = null;\n } else {\n queue.push(chunk);\n }\n });\n\n result.on('close', () => {\n done = true;\n if (resolveNext) {\n resolveNext({ value: undefined as any, done: true });\n resolveNext = null;\n rejectNext = null;\n }\n });\n\n result.on('error', (err) => {\n error = err;\n done = true;\n if (rejectNext) {\n rejectNext(err);\n resolveNext = null;\n rejectNext = null;\n }\n });\n\n return {\n next(): Promise<IteratorResult<ExtractChunk<T>>> {\n if (error) {\n return Promise.reject(error);\n }\n if (queue.length > 0) {\n return Promise.resolve({ value: queue.shift()!, done: false });\n }\n if (done) {\n return Promise.resolve({ value: undefined as any, done: true });\n }\n return new Promise((resolve, reject) => {\n resolveNext = resolve;\n rejectNext = reject;\n });\n },\n };\n },\n };\n };\n}\n\n// ============================================\n// Create Typed QueryClient\n// ============================================\n\n/**\n * Create a typed QueryClient wrapper with per-endpoint cache methods\n *\n * @param queryClient - The TanStack QueryClient instance\n * @param client - The typed client created with createClient()\n * @param contract - The contract definition\n * @returns A typed QueryClient with per-endpoint methods\n *\n * @example\n * ```tsx\n * const typedQueryClient = createTypedQueryClient(queryClient, client, contract);\n *\n * // Type-safe cache operations\n * typedQueryClient.listUsers.getQueryData(['users']);\n * typedQueryClient.listUsers.setQueryData(['users'], newData);\n * await typedQueryClient.listUsers.prefetchQuery({\n * queryKey: ['users'],\n * queryData: { query: { limit: '10' } }\n * });\n * ```\n */\nexport function createTypedQueryClient<T extends Contract>(\n queryClient: QueryClient,\n client: Client<T>,\n contract: T,\n): TypedQueryClient<T> {\n const typed = queryClient as TypedQueryClient<T>;\n\n for (const [name, endpoint] of Object.entries(contract)) {\n if (endpoint.type !== 'standard') continue;\n\n const clientMethod = client[name as keyof T] as unknown as ClientMethod<StandardEndpointDefinition>;\n const typedEndpoint = endpoint as StandardEndpointDefinition;\n\n if (typedEndpoint.method === 'GET' || typedEndpoint.method === 'HEAD') {\n (typed as any)[name] = {\n getQueryData: (queryKey: QueryKey) => queryClient.getQueryData(queryKey),\n setQueryData: (queryKey: QueryKey, updater: any) => queryClient.setQueryData(queryKey, updater),\n getQueryState: (queryKey: QueryKey) => queryClient.getQueryState(queryKey),\n fetchQuery: ({ queryKey, queryData, ...rest }: TsrQueryOptions<StandardEndpointDefinition>) =>\n queryClient.fetchQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n }),\n prefetchQuery: ({ queryKey, queryData, ...rest }: TsrQueryOptions<StandardEndpointDefinition>) =>\n queryClient.prefetchQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n }),\n ensureQueryData: ({ queryKey, queryData, ...rest }: TsrQueryOptions<StandardEndpointDefinition>) =>\n queryClient.ensureQueryData({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n }),\n } as TypedQueryEndpointClient<StandardEndpointDefinition>;\n } else {\n (typed as any)[name] = {} as TypedMutationEndpointClient<StandardEndpointDefinition>;\n }\n }\n\n return typed;\n}\n\n// ============================================\n// Main Factory Function\n// ============================================\n\n/**\n * Create typed TanStack Query API for a contract\n *\n * @param client - The typed client created with createClient()\n * @param contract - The contract definition\n * @returns API object with hooks for each endpoint\n *\n * @example\n * ```tsx\n * const client = createClient(contract, { baseUrl: 'http://localhost:3000' });\n * const api = createTanstackQueryApi(client, contract);\n *\n * // Use in components - Query\n * function UserList() {\n * const { data, isLoading } = api.listUsers.useQuery({\n * queryKey: ['users'],\n * queryData: { query: { limit: '10' } }\n * });\n * }\n *\n * // Use in components - Mutation\n * function CreateUser() {\n * const { mutate } = api.createUser.useMutation();\n * return (\n * <button onClick={() => mutate({ body: { name: 'Alice' } })}>\n * Create\n * </button>\n * );\n * }\n *\n * // Direct fetch (no hooks)\n * const users = await api.listUsers.query({ query: { limit: '10' } });\n *\n * // Streaming with React Query\n * const { data: chunks, isFetching } = api.streamChat.useStreamQuery({\n * queryKey: ['chat', prompt],\n * queryData: { body: { prompt } }\n * });\n * ```\n */\nexport function createTanstackQueryApi<T extends Contract>(\n client: Client<T>,\n contract: T,\n): TanstackQueryApi<T> {\n // Build endpoint APIs\n const endpoints: Record<string, unknown> = {};\n\n for (const [name, endpoint] of Object.entries(contract)) {\n // Handle streaming endpoints\n if (endpoint.type === 'streaming') {\n const streamMethod = client[name as keyof T] as unknown as StreamingClientMethod<StreamingEndpointDefinition>;\n\n endpoints[name] = {\n stream: streamMethod,\n useStreamQuery: ({ queryKey, queryData, refetchMode, ...rest }: TsrStreamQueryOptions<StreamingEndpointDefinition>) => {\n const result = useQuery({\n queryKey,\n queryFn: streamedQuery({\n streamFn: streamToAsyncIterable(streamMethod, queryData),\n refetchMode,\n }),\n ...rest,\n });\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n } as StreamingEndpointApi<StreamingEndpointDefinition>;\n continue;\n }\n\n // Handle SSE endpoints\n if (endpoint.type === 'sse') {\n const connectMethod = client[name as keyof T] as unknown as SSEClientMethod<SSEEndpointDefinition>;\n endpoints[name] = {\n connect: connectMethod,\n } as SSEEndpointApi<SSEEndpointDefinition>;\n continue;\n }\n\n // Handle download endpoints\n if (endpoint.type === 'download') {\n const downloadMethod = client[name as keyof T] as unknown as DownloadClientMethod<DownloadEndpointDefinition>;\n endpoints[name] = {\n download: downloadMethod,\n } as DownloadEndpointApi<DownloadEndpointDefinition>;\n continue;\n }\n\n // Handle standard endpoints\n const method = endpoint.method;\n const clientMethod = client[name as keyof T] as unknown as ClientMethod<StandardEndpointDefinition>;\n\n if (method === 'GET' || method === 'HEAD') {\n // Query endpoint\n endpoints[name] = {\n useQuery: ({ queryKey, queryData, ...rest }: TsrQueryOptions<StandardEndpointDefinition>) => {\n const result = useQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n });\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n useSuspenseQuery: ({ queryKey, queryData, ...rest }: TsrSuspenseQueryOptions<StandardEndpointDefinition>) => {\n const result = useSuspenseQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n });\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n useInfiniteQuery: <TPageParam = unknown>({\n queryKey,\n queryData,\n ...rest\n }: TsrInfiniteQueryOptions<StandardEndpointDefinition, TPageParam>) => {\n const result = useInfiniteQuery({\n queryKey,\n queryFn: (ctx: { pageParam: TPageParam }) =>\n clientMethod(queryData({ pageParam: ctx.pageParam })),\n ...rest,\n } as any);\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n useSuspenseInfiniteQuery: <TPageParam = unknown>({\n queryKey,\n queryData,\n ...rest\n }: TsrSuspenseInfiniteQueryOptions<StandardEndpointDefinition, TPageParam>) => {\n const result = useSuspenseInfiniteQuery({\n queryKey,\n queryFn: (ctx: { pageParam: TPageParam }) =>\n clientMethod(queryData({ pageParam: ctx.pageParam })),\n ...rest,\n } as any);\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n query: (options: EndpointRequestOptions<StandardEndpointDefinition>) => clientMethod(options),\n } as QueryEndpointApi<StandardEndpointDefinition>;\n } else {\n // Mutation endpoint\n endpoints[name] = {\n useMutation: (options?: TsrMutationOptions<StandardEndpointDefinition>) => {\n const result = useMutation({\n mutationFn: (data: EndpointRequestOptions<StandardEndpointDefinition>) => clientMethod(data),\n ...options,\n });\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n mutate: (options: EndpointRequestOptions<StandardEndpointDefinition>) => clientMethod(options),\n } as MutationEndpointApi<StandardEndpointDefinition>;\n }\n }\n\n return endpoints as TanstackQueryApi<T>;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "8C0C3CC1741EF8A064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -1,100 +1,238 @@
|
|
|
1
|
-
import type { Client, EndpointRequestOptions, EndpointResponse, SSEClientMethod, StreamingClientMethod } from '@richie-rpc/client';
|
|
2
|
-
import type { Contract, SSEEndpointDefinition, StandardEndpointDefinition, StreamingEndpointDefinition } from '@richie-rpc/core';
|
|
3
|
-
import { type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseQueryResult, type UseSuspenseQueryOptions, type UseSuspenseQueryResult } from '@tanstack/react-query';
|
|
1
|
+
import type { Client, DownloadClientMethod, EndpointRequestOptions, EndpointResponse, SSEClientMethod, StreamingClientMethod } from '@richie-rpc/client';
|
|
2
|
+
import type { Contract, DownloadEndpointDefinition, ExtractChunk, SSEEndpointDefinition, StandardEndpointDefinition, StreamingEndpointDefinition } from '@richie-rpc/core';
|
|
3
|
+
import { type InfiniteData, type QueryClient, type QueryKey, type Updater, type UseInfiniteQueryOptions, type UseInfiniteQueryResult, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseQueryResult, type UseSuspenseInfiniteQueryOptions, type UseSuspenseInfiniteQueryResult, type UseSuspenseQueryOptions, type UseSuspenseQueryResult } from '@tanstack/react-query';
|
|
4
4
|
type QueryMethods = 'GET' | 'HEAD';
|
|
5
5
|
type MutationMethods = 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* Provides useQuery and useSuspenseQuery methods
|
|
7
|
+
* Unified query options - combines queryKey, queryData, and TanStack Query options
|
|
9
8
|
*/
|
|
10
|
-
export type
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
useQuery: (options: EndpointRequestOptions<T>, queryOptions?: Omit<UseQueryOptions<EndpointResponse<T>, Error>, 'queryKey' | 'queryFn'>) => UseQueryResult<EndpointResponse<T>, Error>;
|
|
15
|
-
/**
|
|
16
|
-
* Suspense-enabled query hook that throws promises for React Suspense
|
|
17
|
-
*/
|
|
18
|
-
useSuspenseQuery: (options: EndpointRequestOptions<T>, queryOptions?: Omit<UseSuspenseQueryOptions<EndpointResponse<T>, Error>, 'queryKey' | 'queryFn'>) => UseSuspenseQueryResult<EndpointResponse<T>, Error>;
|
|
9
|
+
export type TsrQueryOptions<T extends StandardEndpointDefinition> = Omit<UseQueryOptions<EndpointResponse<T>, Error>, 'queryKey' | 'queryFn'> & {
|
|
10
|
+
queryKey: QueryKey;
|
|
11
|
+
queryData: EndpointRequestOptions<T>;
|
|
19
12
|
};
|
|
20
13
|
/**
|
|
21
|
-
*
|
|
22
|
-
* Provides useMutation method
|
|
14
|
+
* Suspense query options
|
|
23
15
|
*/
|
|
24
|
-
export type
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*/
|
|
28
|
-
useMutation: (mutationOptions?: Omit<UseMutationOptions<EndpointResponse<T>, Error, EndpointRequestOptions<T>>, 'mutationFn'>) => UseMutationResult<EndpointResponse<T>, Error, EndpointRequestOptions<T>>;
|
|
16
|
+
export type TsrSuspenseQueryOptions<T extends StandardEndpointDefinition> = Omit<UseSuspenseQueryOptions<EndpointResponse<T>, Error>, 'queryKey' | 'queryFn'> & {
|
|
17
|
+
queryKey: QueryKey;
|
|
18
|
+
queryData: EndpointRequestOptions<T>;
|
|
29
19
|
};
|
|
30
20
|
/**
|
|
31
|
-
*
|
|
21
|
+
* Infinite query options - queryData is a function that receives pageParam
|
|
32
22
|
*/
|
|
33
|
-
export type
|
|
23
|
+
export type TsrInfiniteQueryOptions<T extends StandardEndpointDefinition, TPageParam = unknown> = Omit<UseInfiniteQueryOptions<EndpointResponse<T>, Error, unknown, QueryKey, TPageParam>, 'queryKey' | 'queryFn'> & {
|
|
24
|
+
queryKey: QueryKey;
|
|
25
|
+
queryData: (context: {
|
|
26
|
+
pageParam: TPageParam;
|
|
27
|
+
}) => EndpointRequestOptions<T>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Suspense infinite query options
|
|
31
|
+
*/
|
|
32
|
+
export type TsrSuspenseInfiniteQueryOptions<T extends StandardEndpointDefinition, TPageParam = unknown> = Omit<UseSuspenseInfiniteQueryOptions<EndpointResponse<T>, Error, unknown, QueryKey, TPageParam>, 'queryKey' | 'queryFn'> & {
|
|
33
|
+
queryKey: QueryKey;
|
|
34
|
+
queryData: (context: {
|
|
35
|
+
pageParam: TPageParam;
|
|
36
|
+
}) => EndpointRequestOptions<T>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Mutation options - same as TanStack Query but typed
|
|
40
|
+
*/
|
|
41
|
+
export type TsrMutationOptions<T extends StandardEndpointDefinition> = Omit<UseMutationOptions<EndpointResponse<T>, Error, EndpointRequestOptions<T>>, 'mutationFn'>;
|
|
42
|
+
/**
|
|
43
|
+
* Stream query options for streaming endpoints
|
|
44
|
+
*/
|
|
45
|
+
export type TsrStreamQueryOptions<T extends StreamingEndpointDefinition> = Omit<UseQueryOptions<ExtractChunk<T>[], Error>, 'queryKey' | 'queryFn'> & {
|
|
46
|
+
queryKey: QueryKey;
|
|
47
|
+
queryData: EndpointRequestOptions<T>;
|
|
48
|
+
refetchMode?: 'reset' | 'append' | 'replace';
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Response type from hooks - includes status and body like ts-rest
|
|
52
|
+
*/
|
|
53
|
+
export type TsrResponse<T extends StandardEndpointDefinition> = EndpointResponse<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Error response type - either fetch error or typed response error
|
|
56
|
+
*/
|
|
57
|
+
export type TsrError = Error | {
|
|
58
|
+
status: number;
|
|
59
|
+
data: unknown;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Check if an error is a fetch/network error (Error instance, not a response)
|
|
63
|
+
*/
|
|
64
|
+
export declare function isFetchError(error: unknown): error is Error;
|
|
65
|
+
/**
|
|
66
|
+
* Check if error is a response with a status code not defined in the contract
|
|
67
|
+
*/
|
|
68
|
+
export declare function isUnknownErrorResponse<T extends StandardEndpointDefinition>(error: unknown, endpoint: T): error is {
|
|
69
|
+
status: number;
|
|
70
|
+
data: unknown;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Check if error is either a fetch error or unknown response error
|
|
74
|
+
*/
|
|
75
|
+
export declare function isNotKnownResponseError<T extends StandardEndpointDefinition>(error: unknown, endpoint: T): error is Error | {
|
|
76
|
+
status: number;
|
|
77
|
+
data: unknown;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Exhaustive guard for compile-time exhaustiveness checking
|
|
81
|
+
* Use after handling all known error cases to ensure nothing is missed
|
|
82
|
+
*/
|
|
83
|
+
export declare function exhaustiveGuard(_value: never): never;
|
|
84
|
+
/**
|
|
85
|
+
* Typed query client methods for a single query endpoint
|
|
86
|
+
*/
|
|
87
|
+
export type TypedQueryEndpointClient<T extends StandardEndpointDefinition> = {
|
|
88
|
+
getQueryData: (queryKey: QueryKey) => EndpointResponse<T> | undefined;
|
|
89
|
+
setQueryData: (queryKey: QueryKey, updater: Updater<EndpointResponse<T> | undefined, EndpointResponse<T> | undefined>) => EndpointResponse<T> | undefined;
|
|
90
|
+
getQueryState: (queryKey: QueryKey) => ReturnType<QueryClient['getQueryState']>;
|
|
91
|
+
fetchQuery: (options: TsrQueryOptions<T>) => Promise<EndpointResponse<T>>;
|
|
92
|
+
prefetchQuery: (options: TsrQueryOptions<T>) => Promise<void>;
|
|
93
|
+
ensureQueryData: (options: TsrQueryOptions<T>) => Promise<EndpointResponse<T>>;
|
|
94
|
+
};
|
|
34
95
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* doesn't fit well with long-lived streaming connections
|
|
96
|
+
* Typed query client methods for a single mutation endpoint
|
|
97
|
+
* Mutations don't have query-specific cache methods
|
|
38
98
|
*/
|
|
39
|
-
export type
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
99
|
+
export type TypedMutationEndpointClient<_T extends StandardEndpointDefinition> = object;
|
|
100
|
+
/**
|
|
101
|
+
* Full typed query client - extends QueryClient with per-endpoint methods
|
|
102
|
+
*/
|
|
103
|
+
export type TypedQueryClient<T extends Contract> = QueryClient & {
|
|
104
|
+
[K in keyof T]: T[K] extends StandardEndpointDefinition ? T[K]['method'] extends QueryMethods ? TypedQueryEndpointClient<T[K]> : TypedMutationEndpointClient<T[K]> : Record<string, never>;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* API for query endpoints (GET, HEAD)
|
|
108
|
+
*/
|
|
109
|
+
export type QueryEndpointApi<T extends StandardEndpointDefinition> = {
|
|
110
|
+
/** Standard query hook */
|
|
111
|
+
useQuery: (options: TsrQueryOptions<T>) => UseQueryResult<EndpointResponse<T>, Error> & {
|
|
112
|
+
contractEndpoint: T;
|
|
113
|
+
};
|
|
114
|
+
/** Suspense query hook */
|
|
115
|
+
useSuspenseQuery: (options: TsrSuspenseQueryOptions<T>) => UseSuspenseQueryResult<EndpointResponse<T>, Error> & {
|
|
116
|
+
contractEndpoint: T;
|
|
117
|
+
};
|
|
118
|
+
/** Infinite query hook */
|
|
119
|
+
useInfiniteQuery: <TPageParam = unknown>(options: TsrInfiniteQueryOptions<T, TPageParam>) => UseInfiniteQueryResult<InfiniteData<EndpointResponse<T>, TPageParam>, Error> & {
|
|
120
|
+
contractEndpoint: T;
|
|
121
|
+
};
|
|
122
|
+
/** Suspense infinite query hook */
|
|
123
|
+
useSuspenseInfiniteQuery: <TPageParam = unknown>(options: TsrSuspenseInfiniteQueryOptions<T, TPageParam>) => UseSuspenseInfiniteQueryResult<InfiniteData<EndpointResponse<T>, TPageParam>, Error> & {
|
|
124
|
+
contractEndpoint: T;
|
|
125
|
+
};
|
|
126
|
+
/** Direct fetch without React Query */
|
|
127
|
+
query: (options: EndpointRequestOptions<T>) => Promise<EndpointResponse<T>>;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* API for mutation endpoints (POST, PUT, PATCH, DELETE)
|
|
131
|
+
*/
|
|
132
|
+
export type MutationEndpointApi<T extends StandardEndpointDefinition> = {
|
|
133
|
+
/** Mutation hook */
|
|
134
|
+
useMutation: (options?: TsrMutationOptions<T>) => UseMutationResult<EndpointResponse<T>, Error, EndpointRequestOptions<T>> & {
|
|
135
|
+
contractEndpoint: T;
|
|
136
|
+
};
|
|
137
|
+
/** Direct mutate without React Query */
|
|
138
|
+
mutate: (options: EndpointRequestOptions<T>) => Promise<EndpointResponse<T>>;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* API for streaming endpoints
|
|
142
|
+
*/
|
|
143
|
+
export type StreamingEndpointApi<T extends StreamingEndpointDefinition> = {
|
|
144
|
+
/** Direct stream access (event-based) */
|
|
43
145
|
stream: StreamingClientMethod<T>;
|
|
146
|
+
/** Query hook using experimental streamedQuery */
|
|
147
|
+
useStreamQuery: (options: TsrStreamQueryOptions<T>) => UseQueryResult<ExtractChunk<T>[], Error> & {
|
|
148
|
+
contractEndpoint: T;
|
|
149
|
+
};
|
|
44
150
|
};
|
|
45
151
|
/**
|
|
46
|
-
*
|
|
47
|
-
* Exposes the SSE client method directly since React Query
|
|
48
|
-
* doesn't fit well with long-lived SSE connections
|
|
152
|
+
* API for SSE endpoints
|
|
49
153
|
*/
|
|
50
|
-
export type
|
|
51
|
-
/**
|
|
52
|
-
* Create an SSE connection
|
|
53
|
-
*/
|
|
154
|
+
export type SSEEndpointApi<T extends SSEEndpointDefinition> = {
|
|
155
|
+
/** Direct SSE connection access (event-based) */
|
|
54
156
|
connect: SSEClientMethod<T>;
|
|
55
157
|
};
|
|
56
158
|
/**
|
|
57
|
-
*
|
|
58
|
-
* Each endpoint gets appropriate hooks based on its type and HTTP method
|
|
159
|
+
* API for download endpoints
|
|
59
160
|
*/
|
|
60
|
-
export type
|
|
61
|
-
|
|
161
|
+
export type DownloadEndpointApi<T extends DownloadEndpointDefinition> = {
|
|
162
|
+
/** Direct download without React Query */
|
|
163
|
+
download: DownloadClientMethod<T>;
|
|
62
164
|
};
|
|
63
165
|
/**
|
|
64
|
-
*
|
|
166
|
+
* Select appropriate API type based on endpoint type
|
|
167
|
+
*/
|
|
168
|
+
export type EndpointApi<T> = T extends StandardEndpointDefinition ? T['method'] extends QueryMethods ? QueryEndpointApi<T> : T['method'] extends MutationMethods ? MutationEndpointApi<T> : never : T extends StreamingEndpointDefinition ? StreamingEndpointApi<T> : T extends SSEEndpointDefinition ? SSEEndpointApi<T> : T extends DownloadEndpointDefinition ? DownloadEndpointApi<T> : never;
|
|
169
|
+
/**
|
|
170
|
+
* Full TanStack Query API for a contract
|
|
171
|
+
*/
|
|
172
|
+
export type TanstackQueryApi<T extends Contract> = {
|
|
173
|
+
[K in keyof T]: EndpointApi<T[K]>;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Create a typed QueryClient wrapper with per-endpoint cache methods
|
|
65
177
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
178
|
+
* @param queryClient - The TanStack QueryClient instance
|
|
179
|
+
* @param client - The typed client created with createClient()
|
|
180
|
+
* @param contract - The contract definition
|
|
181
|
+
* @returns A typed QueryClient with per-endpoint methods
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```tsx
|
|
185
|
+
* const typedQueryClient = createTypedQueryClient(queryClient, client, contract);
|
|
186
|
+
*
|
|
187
|
+
* // Type-safe cache operations
|
|
188
|
+
* typedQueryClient.listUsers.getQueryData(['users']);
|
|
189
|
+
* typedQueryClient.listUsers.setQueryData(['users'], newData);
|
|
190
|
+
* await typedQueryClient.listUsers.prefetchQuery({
|
|
191
|
+
* queryKey: ['users'],
|
|
192
|
+
* queryData: { query: { limit: '10' } }
|
|
193
|
+
* });
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
export declare function createTypedQueryClient<T extends Contract>(queryClient: QueryClient, client: Client<T>, contract: T): TypedQueryClient<T>;
|
|
197
|
+
/**
|
|
198
|
+
* Create typed TanStack Query API for a contract
|
|
68
199
|
*
|
|
69
200
|
* @param client - The typed client created with createClient()
|
|
70
201
|
* @param contract - The contract definition
|
|
71
|
-
* @returns
|
|
202
|
+
* @returns API object with hooks for each endpoint
|
|
72
203
|
*
|
|
73
204
|
* @example
|
|
74
205
|
* ```tsx
|
|
75
206
|
* const client = createClient(contract, { baseUrl: 'http://localhost:3000' });
|
|
76
|
-
* const
|
|
207
|
+
* const api = createTanstackQueryApi(client, contract);
|
|
77
208
|
*
|
|
78
|
-
* //
|
|
209
|
+
* // Use in components - Query
|
|
79
210
|
* function UserList() {
|
|
80
|
-
* const { data, isLoading } =
|
|
81
|
-
*
|
|
211
|
+
* const { data, isLoading } = api.listUsers.useQuery({
|
|
212
|
+
* queryKey: ['users'],
|
|
213
|
+
* queryData: { query: { limit: '10' } }
|
|
82
214
|
* });
|
|
83
|
-
* // ...
|
|
84
215
|
* }
|
|
85
216
|
*
|
|
86
|
-
* //
|
|
217
|
+
* // Use in components - Mutation
|
|
87
218
|
* function CreateUser() {
|
|
88
|
-
* const
|
|
219
|
+
* const { mutate } = api.createUser.useMutation();
|
|
89
220
|
* return (
|
|
90
|
-
* <button onClick={() =>
|
|
91
|
-
*
|
|
92
|
-
* })}>
|
|
93
|
-
* Create User
|
|
221
|
+
* <button onClick={() => mutate({ body: { name: 'Alice' } })}>
|
|
222
|
+
* Create
|
|
94
223
|
* </button>
|
|
95
224
|
* );
|
|
96
225
|
* }
|
|
226
|
+
*
|
|
227
|
+
* // Direct fetch (no hooks)
|
|
228
|
+
* const users = await api.listUsers.query({ query: { limit: '10' } });
|
|
229
|
+
*
|
|
230
|
+
* // Streaming with React Query
|
|
231
|
+
* const { data: chunks, isFetching } = api.streamChat.useStreamQuery({
|
|
232
|
+
* queryKey: ['chat', prompt],
|
|
233
|
+
* queryData: { body: { prompt } }
|
|
234
|
+
* });
|
|
97
235
|
* ```
|
|
98
236
|
*/
|
|
99
|
-
export declare function
|
|
237
|
+
export declare function createTanstackQueryApi<T extends Contract>(client: Client<T>, contract: T): TanstackQueryApi<T>;
|
|
100
238
|
export {};
|
package/package.json
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@richie-rpc/react-query",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
|
5
|
-
"exports": {
|
|
6
|
-
".": {
|
|
7
|
-
"types": "./dist/types/index.d.ts",
|
|
8
|
-
"require": "./dist/cjs/index.cjs",
|
|
9
|
-
"import": "./dist/mjs/index.mjs"
|
|
10
|
-
}
|
|
11
|
-
},
|
|
12
5
|
"peerDependencies": {
|
|
13
|
-
"@richie-rpc/client": "^1.2.
|
|
6
|
+
"@richie-rpc/client": "^1.2.6",
|
|
14
7
|
"@richie-rpc/core": "^1.2.4",
|
|
15
8
|
"@tanstack/react-query": "^5.0.0",
|
|
16
9
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -41,6 +34,13 @@
|
|
|
41
34
|
"description": "A TypeScript-first, type-safe API contract library for Bun with Zod validation",
|
|
42
35
|
"module": "./dist/mjs/index.mjs",
|
|
43
36
|
"types": "./dist/types/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/types/index.d.ts",
|
|
40
|
+
"require": "./dist/cjs/index.cjs",
|
|
41
|
+
"import": "./dist/mjs/index.mjs"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|