@richie-rpc/react-query 1.0.8 → 1.0.9
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 +6 -4
- package/dist/cjs/index.cjs +32 -7
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/index.mjs +32 -7
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -99,10 +99,12 @@ function CreateUserForm() {
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
return (
|
|
102
|
-
<form
|
|
103
|
-
e
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
<form
|
|
103
|
+
onSubmit={(e) => {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
mutation.mutate({ body: { name: 'Alice', email: 'alice@example.com' } });
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
106
108
|
<button type="submit" disabled={mutation.isPending}>
|
|
107
109
|
{mutation.isPending ? 'Creating...' : 'Create User'}
|
|
108
110
|
</button>
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -124,17 +124,29 @@ function createTypedQueryClient(queryClient, client, contract) {
|
|
|
124
124
|
getQueryData: (queryKey) => queryClient.getQueryData(queryKey),
|
|
125
125
|
setQueryData: (queryKey, updater) => queryClient.setQueryData(queryKey, updater),
|
|
126
126
|
getQueryState: (queryKey) => queryClient.getQueryState(queryKey),
|
|
127
|
-
fetchQuery: ({
|
|
127
|
+
fetchQuery: ({
|
|
128
|
+
queryKey,
|
|
129
|
+
queryData,
|
|
130
|
+
...rest
|
|
131
|
+
}) => queryClient.fetchQuery({
|
|
128
132
|
queryKey,
|
|
129
133
|
queryFn: () => clientMethod(queryData),
|
|
130
134
|
...rest
|
|
131
135
|
}),
|
|
132
|
-
prefetchQuery: ({
|
|
136
|
+
prefetchQuery: ({
|
|
137
|
+
queryKey,
|
|
138
|
+
queryData,
|
|
139
|
+
...rest
|
|
140
|
+
}) => queryClient.prefetchQuery({
|
|
133
141
|
queryKey,
|
|
134
142
|
queryFn: () => clientMethod(queryData),
|
|
135
143
|
...rest
|
|
136
144
|
}),
|
|
137
|
-
ensureQueryData: ({
|
|
145
|
+
ensureQueryData: ({
|
|
146
|
+
queryKey,
|
|
147
|
+
queryData,
|
|
148
|
+
...rest
|
|
149
|
+
}) => queryClient.ensureQueryData({
|
|
138
150
|
queryKey,
|
|
139
151
|
queryFn: () => clientMethod(queryData),
|
|
140
152
|
...rest
|
|
@@ -153,7 +165,12 @@ function createTanstackQueryApi(client, contract) {
|
|
|
153
165
|
const streamMethod = client[name];
|
|
154
166
|
endpoints[name] = {
|
|
155
167
|
stream: streamMethod,
|
|
156
|
-
useStreamQuery: ({
|
|
168
|
+
useStreamQuery: ({
|
|
169
|
+
queryKey,
|
|
170
|
+
queryData,
|
|
171
|
+
refetchMode,
|
|
172
|
+
...rest
|
|
173
|
+
}) => {
|
|
157
174
|
const result = import_react_query.useQuery({
|
|
158
175
|
queryKey,
|
|
159
176
|
queryFn: import_react_query.experimental_streamedQuery({
|
|
@@ -188,7 +205,11 @@ function createTanstackQueryApi(client, contract) {
|
|
|
188
205
|
const clientMethod = client[name];
|
|
189
206
|
if (method === "GET" || method === "HEAD") {
|
|
190
207
|
endpoints[name] = {
|
|
191
|
-
useQuery: ({
|
|
208
|
+
useQuery: ({
|
|
209
|
+
queryKey,
|
|
210
|
+
queryData,
|
|
211
|
+
...rest
|
|
212
|
+
}) => {
|
|
192
213
|
const result = import_react_query.useQuery({
|
|
193
214
|
queryKey,
|
|
194
215
|
queryFn: () => clientMethod(queryData),
|
|
@@ -199,7 +220,11 @@ function createTanstackQueryApi(client, contract) {
|
|
|
199
220
|
contractEndpoint: endpoint
|
|
200
221
|
};
|
|
201
222
|
},
|
|
202
|
-
useSuspenseQuery: ({
|
|
223
|
+
useSuspenseQuery: ({
|
|
224
|
+
queryKey,
|
|
225
|
+
queryData,
|
|
226
|
+
...rest
|
|
227
|
+
}) => {
|
|
203
228
|
const result = import_react_query.useSuspenseQuery({
|
|
204
229
|
queryKey,
|
|
205
230
|
queryFn: () => clientMethod(queryData),
|
|
@@ -262,4 +287,4 @@ function createTanstackQueryApi(client, contract) {
|
|
|
262
287
|
}
|
|
263
288
|
})
|
|
264
289
|
|
|
265
|
-
//# debugId=
|
|
290
|
+
//# debugId=07150FF2CF93343A64756E2164756E21
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
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"
|
|
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<\n EndpointResponse<T>,\n Error\n > & {\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: (options?: TsrMutationOptions<T>) => UseMutationResult<\n EndpointResponse<T>,\n Error,\n EndpointRequestOptions<T>\n > & {\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<\n ExtractChunk<T>[],\n Error\n > & {\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// 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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\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[\n name as keyof T\n ] 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) =>\n queryClient.setQueryData(queryKey, updater),\n getQueryState: (queryKey: QueryKey) => queryClient.getQueryState(queryKey),\n fetchQuery: ({\n queryKey,\n queryData,\n ...rest\n }: TsrQueryOptions<StandardEndpointDefinition>) =>\n queryClient.fetchQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n }),\n prefetchQuery: ({\n queryKey,\n queryData,\n ...rest\n }: TsrQueryOptions<StandardEndpointDefinition>) =>\n queryClient.prefetchQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n }),\n ensureQueryData: ({\n queryKey,\n queryData,\n ...rest\n }: 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[\n name as keyof T\n ] as unknown as StreamingClientMethod<StreamingEndpointDefinition>;\n\n endpoints[name] = {\n stream: streamMethod,\n useStreamQuery: ({\n queryKey,\n queryData,\n refetchMode,\n ...rest\n }: 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[\n name as keyof T\n ] 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[\n name as keyof T\n ] 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[\n name as keyof T\n ] as unknown as ClientMethod<StandardEndpointDefinition>;\n\n if (method === 'GET' || method === 'HEAD') {\n // Query endpoint\n endpoints[name] = {\n useQuery: ({\n queryKey,\n queryData,\n ...rest\n }: 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: ({\n queryKey,\n queryData,\n ...rest\n }: 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>) =>\n 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>) =>\n clientMethod(data),\n ...options,\n });\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n mutate: (options: EndpointRequestOptions<StandardEndpointDefinition>) =>\n clientMethod(options),\n } as MutationEndpointApi<StandardEndpointDefinition>;\n }\n }\n\n return endpoints as TanstackQueryApi<T>;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCO,IArBP;AA8HO,SAAS,YAAY,CAAC,OAAgC;AAAA,EAC3D,OAAO,iBAAiB,SAAS,EAAE,YAAY;AAAA;AAM1C,SAAS,sBAA4D,CAC1E,OACA,UAC4C;AAAA,EAC5C,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,YAAY,QAAQ;AAAA,IAC/D,OAAO;AAAA,EACT;AAAA,EACA,MAAM,SAAU,MAA6B;AAAA,EAC7C,OAAO,EAAE,UAAU,SAAS;AAAA;AAMvB,SAAS,uBAA6D,CAC3E,OACA,UACoD;AAAA,EACpD,OAAO,aAAa,KAAK,KAAK,uBAAuB,OAAO,QAAQ;AAAA;AAO/D,SAAS,eAAe,CAAC,QAAsB;AAAA,EACpD,MAAM,IAAI,MAAM,mBAAmB,KAAK,UAAU,MAAM,GAAG;AAAA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "07150FF2CF93343A64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
package/dist/mjs/index.mjs
CHANGED
|
@@ -93,17 +93,29 @@ function createTypedQueryClient(queryClient, client, contract) {
|
|
|
93
93
|
getQueryData: (queryKey) => queryClient.getQueryData(queryKey),
|
|
94
94
|
setQueryData: (queryKey, updater) => queryClient.setQueryData(queryKey, updater),
|
|
95
95
|
getQueryState: (queryKey) => queryClient.getQueryState(queryKey),
|
|
96
|
-
fetchQuery: ({
|
|
96
|
+
fetchQuery: ({
|
|
97
|
+
queryKey,
|
|
98
|
+
queryData,
|
|
99
|
+
...rest
|
|
100
|
+
}) => queryClient.fetchQuery({
|
|
97
101
|
queryKey,
|
|
98
102
|
queryFn: () => clientMethod(queryData),
|
|
99
103
|
...rest
|
|
100
104
|
}),
|
|
101
|
-
prefetchQuery: ({
|
|
105
|
+
prefetchQuery: ({
|
|
106
|
+
queryKey,
|
|
107
|
+
queryData,
|
|
108
|
+
...rest
|
|
109
|
+
}) => queryClient.prefetchQuery({
|
|
102
110
|
queryKey,
|
|
103
111
|
queryFn: () => clientMethod(queryData),
|
|
104
112
|
...rest
|
|
105
113
|
}),
|
|
106
|
-
ensureQueryData: ({
|
|
114
|
+
ensureQueryData: ({
|
|
115
|
+
queryKey,
|
|
116
|
+
queryData,
|
|
117
|
+
...rest
|
|
118
|
+
}) => queryClient.ensureQueryData({
|
|
107
119
|
queryKey,
|
|
108
120
|
queryFn: () => clientMethod(queryData),
|
|
109
121
|
...rest
|
|
@@ -122,7 +134,12 @@ function createTanstackQueryApi(client, contract) {
|
|
|
122
134
|
const streamMethod = client[name];
|
|
123
135
|
endpoints[name] = {
|
|
124
136
|
stream: streamMethod,
|
|
125
|
-
useStreamQuery: ({
|
|
137
|
+
useStreamQuery: ({
|
|
138
|
+
queryKey,
|
|
139
|
+
queryData,
|
|
140
|
+
refetchMode,
|
|
141
|
+
...rest
|
|
142
|
+
}) => {
|
|
126
143
|
const result = useQuery({
|
|
127
144
|
queryKey,
|
|
128
145
|
queryFn: streamedQuery({
|
|
@@ -157,7 +174,11 @@ function createTanstackQueryApi(client, contract) {
|
|
|
157
174
|
const clientMethod = client[name];
|
|
158
175
|
if (method === "GET" || method === "HEAD") {
|
|
159
176
|
endpoints[name] = {
|
|
160
|
-
useQuery: ({
|
|
177
|
+
useQuery: ({
|
|
178
|
+
queryKey,
|
|
179
|
+
queryData,
|
|
180
|
+
...rest
|
|
181
|
+
}) => {
|
|
161
182
|
const result = useQuery({
|
|
162
183
|
queryKey,
|
|
163
184
|
queryFn: () => clientMethod(queryData),
|
|
@@ -168,7 +189,11 @@ function createTanstackQueryApi(client, contract) {
|
|
|
168
189
|
contractEndpoint: endpoint
|
|
169
190
|
};
|
|
170
191
|
},
|
|
171
|
-
useSuspenseQuery: ({
|
|
192
|
+
useSuspenseQuery: ({
|
|
193
|
+
queryKey,
|
|
194
|
+
queryData,
|
|
195
|
+
...rest
|
|
196
|
+
}) => {
|
|
172
197
|
const result = useSuspenseQuery({
|
|
173
198
|
queryKey,
|
|
174
199
|
queryFn: () => clientMethod(queryData),
|
|
@@ -238,4 +263,4 @@ export {
|
|
|
238
263
|
createTanstackQueryApi
|
|
239
264
|
};
|
|
240
265
|
|
|
241
|
-
//# debugId=
|
|
266
|
+
//# debugId=D668182572705E3164756E2164756E21
|
package/dist/mjs/index.mjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
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"
|
|
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<\n EndpointResponse<T>,\n Error\n > & {\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: (options?: TsrMutationOptions<T>) => UseMutationResult<\n EndpointResponse<T>,\n Error,\n EndpointRequestOptions<T>\n > & {\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<\n ExtractChunk<T>[],\n Error\n > & {\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// 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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\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[\n name as keyof T\n ] 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) =>\n queryClient.setQueryData(queryKey, updater),\n getQueryState: (queryKey: QueryKey) => queryClient.getQueryState(queryKey),\n fetchQuery: ({\n queryKey,\n queryData,\n ...rest\n }: TsrQueryOptions<StandardEndpointDefinition>) =>\n queryClient.fetchQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n }),\n prefetchQuery: ({\n queryKey,\n queryData,\n ...rest\n }: TsrQueryOptions<StandardEndpointDefinition>) =>\n queryClient.prefetchQuery({\n queryKey,\n queryFn: () => clientMethod(queryData),\n ...rest,\n }),\n ensureQueryData: ({\n queryKey,\n queryData,\n ...rest\n }: 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[\n name as keyof T\n ] as unknown as StreamingClientMethod<StreamingEndpointDefinition>;\n\n endpoints[name] = {\n stream: streamMethod,\n useStreamQuery: ({\n queryKey,\n queryData,\n refetchMode,\n ...rest\n }: 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[\n name as keyof T\n ] 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[\n name as keyof T\n ] 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[\n name as keyof T\n ] as unknown as ClientMethod<StandardEndpointDefinition>;\n\n if (method === 'GET' || method === 'HEAD') {\n // Query endpoint\n endpoints[name] = {\n useQuery: ({\n queryKey,\n queryData,\n ...rest\n }: 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: ({\n queryKey,\n queryData,\n ...rest\n }: 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>) =>\n 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>) =>\n clientMethod(data),\n ...options,\n });\n return {\n ...result,\n contractEndpoint: endpoint,\n };\n },\n mutate: (options: EndpointRequestOptions<StandardEndpointDefinition>) =>\n clientMethod(options),\n } as MutationEndpointApi<StandardEndpointDefinition>;\n }\n }\n\n return endpoints as TanstackQueryApi<T>;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;AAkBA;AAAA,gCAeE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+GK,SAAS,YAAY,CAAC,OAAgC;AAAA,EAC3D,OAAO,iBAAiB,SAAS,EAAE,YAAY;AAAA;AAM1C,SAAS,sBAA4D,CAC1E,OACA,UAC4C;AAAA,EAC5C,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,EAAE,YAAY,QAAQ;AAAA,IAC/D,OAAO;AAAA,EACT;AAAA,EACA,MAAM,SAAU,MAA6B;AAAA,EAC7C,OAAO,EAAE,UAAU,SAAS;AAAA;AAMvB,SAAS,uBAA6D,CAC3E,OACA,UACoD;AAAA,EACpD,OAAO,aAAa,KAAK,KAAK,uBAAuB,OAAO,QAAQ;AAAA;AAO/D,SAAS,eAAe,CAAC,QAAsB;AAAA,EACpD,MAAM,IAAI,MAAM,mBAAmB,KAAK,UAAU,MAAM,GAAG;AAAA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D668182572705E3164756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@richie-rpc/react-query",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@richie-rpc/client": "^1.2.
|
|
7
|
-
"@richie-rpc/core": "^1.2.
|
|
6
|
+
"@richie-rpc/client": "^1.2.7",
|
|
7
|
+
"@richie-rpc/core": "^1.2.5",
|
|
8
8
|
"@tanstack/react-query": "^5.0.0",
|
|
9
9
|
"react": "^18.0.0 || ^19.0.0",
|
|
10
10
|
"typescript": "^5",
|