@hypequery/react 0.1.1 → 0.3.0

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 CHANGED
@@ -1,377 +1,308 @@
1
1
  # @hypequery/react
2
2
 
3
- Type-safe React hooks for hypequery APIs. Wraps your generated API definition in thin TanStack Query shims so you can call `useQuery('weeklyRevenue')` with end-to-end type safety.
3
+ React hooks for typed Hypequery HTTP endpoints, built on TanStack Query.
4
4
 
5
- ## Installation
5
+ Use `@hypequery/react` with APIs created by `@hypequery/serve`. The hooks call generated routes and infer input/output types from the API type you pass in.
6
+
7
+ ## Install
6
8
 
7
9
  ```bash
8
10
  npm install @hypequery/react @tanstack/react-query
11
+ # or
12
+ pnpm add @hypequery/react @tanstack/react-query
9
13
  ```
10
14
 
11
- Peer dependencies: `react@^18`, `@tanstack/react-query@^5`.
12
-
13
15
  ## Quick Start
14
16
 
15
- ```ts
16
- // lib/analytics.ts
17
- import { createHooks } from '@hypequery/react';
18
- import type { Api } from '@/analytics/queries';
17
+ Given a serve API:
19
18
 
20
- export const { useQuery, useMutation } = createHooks<Api>({
21
- baseUrl: '/api',
19
+ ```ts
20
+ // server/api.ts
21
+ import { initServe } from '@hypequery/serve';
22
+ import { z } from 'zod';
23
+ import { db } from './db.js';
24
+
25
+ const { query, serve } = initServe({
26
+ context: () => ({ db }),
27
+ basePath: '/api/analytics',
22
28
  });
23
- ```
24
29
 
25
- Wrap your app with TanStack Query's provider:
30
+ const weeklyRevenue = query({
31
+ description: 'Weekly revenue',
32
+ input: z.object({ startDate: z.string() }),
33
+ query: ({ ctx, input }) =>
34
+ ctx.db
35
+ .table('orders')
36
+ .where('created_at', 'gte', input.startDate)
37
+ .sum('total', 'revenue')
38
+ .execute(),
39
+ });
26
40
 
27
- ```tsx
28
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
41
+ export const api = serve({
42
+ queries: { weeklyRevenue },
43
+ });
29
44
 
30
- const queryClient = new QueryClient();
45
+ api.route('/weeklyRevenue', api.queries.weeklyRevenue);
31
46
 
32
- export function App({ children }: { children: React.ReactNode }) {
33
- return (
34
- <QueryClientProvider client={queryClient}>
35
- {children}
36
- </QueryClientProvider>
37
- );
38
- }
47
+ export type AnalyticsApi = typeof api;
39
48
  ```
40
49
 
41
- Use the hooks in your components:
50
+ Create hooks on the client:
42
51
 
43
52
  ```tsx
44
- // Query
45
- const { data, error, isLoading } = useQuery('weeklyRevenue', { startDate: '2025-01-01' });
53
+ // client/analytics-hooks.ts
54
+ import { createHooks } from '@hypequery/react';
55
+ import type { AnalyticsApi } from '../server/api.js';
46
56
 
47
- // Mutation
48
- const rebuild = useMutation('rebuildMetrics');
49
- rebuild.mutate({ force: true });
57
+ export const { useQuery, useMutation } = createHooks<AnalyticsApi>({
58
+ baseUrl: '/api/analytics',
59
+ api: {} as AnalyticsApi,
60
+ });
50
61
  ```
51
62
 
52
- ---
53
-
54
- ## API Reference
55
-
56
- ### `createHooks<Api>(config)`
63
+ Use them inside a component:
57
64
 
58
- Factory function that creates type-safe `useQuery` and `useMutation` hooks for your API.
59
-
60
- **Parameters:**
65
+ ```tsx
66
+ function RevenuePanel() {
67
+ const revenue = useQuery('weeklyRevenue', {
68
+ startDate: '2026-01-01',
69
+ });
61
70
 
62
- ```ts
63
- interface CreateHooksConfig<TApi> {
64
- baseUrl: string; // Required: API base URL (e.g., '/api' or 'https://api.example.com')
65
- fetchFn?: typeof fetch; // Optional: Custom fetch implementation (defaults to global fetch)
66
- headers?: Record<string, string | undefined> | (() => Record<string, string | undefined>); // Optional: Default headers or a resolver function
67
- config?: Record<string, QueryMethodConfig>; // Optional: Per-route HTTP method overrides
68
- api?: TApi; // Optional: API object to auto-extract HTTP methods
69
- }
71
+ if (revenue.isLoading) return <p>Loading...</p>;
72
+ if (revenue.error) return <p>{revenue.error.message}</p>;
70
73
 
71
- interface QueryMethodConfig {
72
- method?: string; // HTTP method: 'GET', 'POST', 'PUT', 'DELETE', etc.
74
+ return <pre>{JSON.stringify(revenue.data, null, 2)}</pre>;
73
75
  }
74
76
  ```
75
77
 
76
- **Returns:**
77
-
78
- ```ts
79
- {
80
- useQuery: <Name>(name, input?, options?) => UseQueryResult<Output, HttpError>,
81
- useMutation: <Name>(name, options?) => UseMutationResult<Output, HttpError, Input>
82
- }
83
- ```
78
+ ## Dataset And Metric Hooks
84
79
 
85
- **Example:**
80
+ `createAnalyticsHooks` adds convenience wrappers for semantic endpoint names. Metrics use their endpoint name directly. Dataset endpoints are addressed as `dataset:<name>` in the API type and exposed through `useDataset(name, ...)`.
86
81
 
87
- ```ts
88
- const { useQuery, useMutation } = createHooks<Api>({
89
- baseUrl: 'https://api.example.com',
90
- headers: {
91
- 'X-API-Key': process.env.API_KEY,
92
- },
93
- config: {
94
- // Override HTTP method for specific routes
95
- uploadFile: { method: 'POST' },
96
- },
97
- });
98
- ```
82
+ ```tsx
83
+ import { createAnalyticsHooks } from '@hypequery/react';
84
+ import type { InferApiType } from '@hypequery/serve';
85
+ import type { api } from '../server/api.js';
99
86
 
100
- ---
87
+ import manifest from './hypequery-manifest.json';
101
88
 
102
- ### `useQuery(name, input?, options?)`
89
+ type AnalyticsApi = InferApiType<typeof api>;
103
90
 
104
- Type-safe wrapper around TanStack Query's `useQuery`. Automatically infers input and output types from your API definition.
91
+ export const { useMetric, useDataset } = createAnalyticsHooks<AnalyticsApi>({
92
+ baseUrl: '/api/analytics',
93
+ manifest,
94
+ metrics: ['revenue', 'averageOrderValue'] as const,
95
+ });
105
96
 
106
- **Signatures:**
97
+ function Dashboard() {
98
+ const revenue = useMetric('revenue', {
99
+ dimensions: ['country'],
100
+ filters: [{ field: 'status', operator: 'eq', value: 'completed' }],
101
+ orderBy: [{ field: 'revenue', direction: 'desc' }],
102
+ limit: 10,
103
+ });
107
104
 
108
- ```ts
109
- // No input required
110
- useQuery(name: Name)
111
- useQuery(name: Name, options: QueryOptions)
105
+ const orders = useDataset('orders', {
106
+ dimensions: ['status'],
107
+ measures: ['revenue', 'orderCount'],
108
+ });
112
109
 
113
- // Input required
114
- useQuery(name: Name, input: Input)
115
- useQuery(name: Name, input: Input, options: QueryOptions)
110
+ return (
111
+ <pre>
112
+ {JSON.stringify({ revenue: revenue.data, orders: orders.data }, null, 2)}
113
+ </pre>
114
+ );
115
+ }
116
116
  ```
117
117
 
118
- **Parameters:**
118
+ ## Pagination
119
119
 
120
- - `name` - Query name from your API definition
121
- - `input` - Query input (typed from your API). Optional if query has no input schema
122
- - `options` - TanStack Query options (`enabled`, `staleTime`, `refetchInterval`, etc.)
123
-
124
- **Returns:** TanStack Query's `UseQueryResult<Output, HttpError>`
125
-
126
- **Examples:**
120
+ Semantic queries that set a `limit` return `meta.pagination = { limit, offset, hasMore }`
121
+ (`hasMore` is exact the server over-fetches one row rather than running a count query).
122
+ `useInfiniteQuery`, and the `useInfiniteMetric` / `useInfiniteDataset` wrappers, build on
123
+ this to page through results. They advance the offset automatically and request meta for you.
127
124
 
128
125
  ```tsx
129
- // Simple query with no input
130
- const { data } = useQuery('healthCheck');
131
-
132
- // Query with input
133
- const { data, error, isLoading } = useQuery('getUser', { id: '123' });
126
+ function OrdersTable() {
127
+ const orders = useInfiniteDataset('orders', {
128
+ dimensions: ['status'],
129
+ measures: ['revenue'],
130
+ limit: 50,
131
+ });
134
132
 
135
- // Query with options
136
- const { data } = useQuery('getUser', { id: '123' }, {
137
- enabled: isLoggedIn,
138
- staleTime: 60000,
139
- refetchOnWindowFocus: false,
140
- });
141
-
142
- // Query without input but with options
143
- const { data } = useQuery('healthCheck', { refetchInterval: 5000 });
133
+ return (
134
+ <>
135
+ {orders.data?.pages.flatMap((page) => page.data).map((row, i) => (
136
+ <Row key={i} row={row} />
137
+ ))}
138
+ <button
139
+ onClick={() => orders.fetchNextPage()}
140
+ disabled={!orders.hasNextPage || orders.isFetchingNextPage}
141
+ >
142
+ Load more
143
+ </button>
144
+ </>
145
+ );
146
+ }
144
147
  ```
145
148
 
146
- ---
147
-
148
- ### `useMutation(name, options?)`
149
+ `input.limit` is the page size; `input.offset`, if provided, is the starting offset.
149
150
 
150
- Type-safe wrapper around TanStack Query's `useMutation`.
151
+ ## Route Configuration
151
152
 
152
- **Parameters:**
153
+ Hooks need to know each endpoint's HTTP method and path. There are three ways to
154
+ supply that, in increasing precedence: a route manifest, a runtime `api` object,
155
+ or explicit `config`.
153
156
 
154
- - `name` - Mutation name from your API definition
155
- - `options` - TanStack Query mutation options (`onSuccess`, `onError`, `retry`, etc.)
157
+ ### Route manifest (recommended)
156
158
 
157
- **Returns:** TanStack Query's `UseMutationResult<Output, HttpError, Input>`
159
+ `@hypequery/serve`'s `api.manifest()` returns a serializable map of every
160
+ query/metric/dataset key to its `{ method, path }`. Export it from a server-only
161
+ module and pass it to the hooks — this avoids importing server code into the
162
+ browser bundle while keeping client routes in sync with the server.
158
163
 
159
- **Examples:**
164
+ ```ts
165
+ // server side (server-only module)
166
+ export const manifest = api.manifest();
160
167
 
161
- ```tsx
162
- // Basic mutation
163
- const createUser = useMutation('createUser');
164
- createUser.mutate({ name: 'Alice', email: 'alice@example.com' });
165
-
166
- // Mutation with callbacks
167
- const updateProfile = useMutation('updateProfile', {
168
- onSuccess: (data) => {
169
- console.log('Profile updated:', data);
170
- queryClient.invalidateQueries(['hypequery', 'getProfile']);
171
- },
172
- onError: (error) => {
173
- console.error('Failed to update profile:', error.message);
174
- },
168
+ // client side
169
+ const { useQuery } = createHooks<AnalyticsApi>({
170
+ baseUrl: '/api/analytics',
171
+ manifest,
175
172
  });
176
-
177
- updateProfile.mutate({ bio: 'New bio' });
178
173
  ```
179
174
 
180
- ---
181
-
182
- ### `queryOptions(options)`
183
-
184
- Helper function to explicitly mark an object as query options (not input). Useful when your query input and TanStack Query options have overlapping property names.
185
-
186
- **Parameters:**
187
-
188
- - `options` - TanStack Query options object
189
-
190
- **Returns:** The same object with an internal symbol marker
175
+ > Metric and dataset endpoints are POST routes whose paths differ from their map
176
+ > keys (e.g. `dataset:orders` → `POST /api/analytics/datasets/orders/query`). They
177
+ > require a `manifest` (or explicit `config`); calling them without one throws a
178
+ > clear error rather than hitting the wrong URL.
191
179
 
192
- **When to use:**
180
+ #### Generating the manifest at build time
193
181
 
194
- If your API input has properties like `enabled`, `staleTime`, etc., the library might misclassify input as options. Use `queryOptions()` to disambiguate:
195
-
196
- ```tsx
197
- // ❌ Ambiguous - could be input or options?
198
- useQuery('getConfig', { enabled: true, staleTime: 5000 });
199
-
200
- // ✅ Explicit - this is input
201
- useQuery('getConfig', { enabled: true, staleTime: 5000 });
202
-
203
- // ✅ Explicit - these are options
204
- import { queryOptions } from '@hypequery/react';
205
- useQuery('getConfig', queryOptions({ enabled: true, staleTime: 5000 }));
206
- ```
182
+ The cleanest way to keep server code out of the browser bundle is to generate a
183
+ JSON file at build time and import that on the client.
207
184
 
208
- ---
209
-
210
- ### `HttpError`
211
-
212
- Custom error class thrown by failed HTTP requests. Extends the standard `Error` class with additional properties.
213
-
214
- **Properties:**
215
-
216
- ```ts
217
- class HttpError extends Error {
218
- readonly status: number; // HTTP status code (404, 500, etc.)
219
- readonly body: unknown; // Parsed response body (JSON or text)
220
- }
185
+ ```bash
186
+ npx hypequery generate:manifest analytics/api.ts --output src/generated/hypequery-manifest.json
221
187
  ```
222
188
 
223
- **Usage:**
224
-
225
- ```tsx
226
- const { error } = useQuery('getUser', { id: '123' });
227
-
228
- if (error) {
229
- console.log(error.message); // "GET request to /api/getUser failed with status 404"
230
- console.log(error.status); // 404
231
- console.log(error.body); // { message: "User not found" }
232
-
233
- // Type-safe instanceof check
234
- if (error instanceof HttpError) {
235
- if (error.status === 404) {
236
- return <NotFound />;
237
- }
189
+ ```jsonc
190
+ // package.json
191
+ {
192
+ "scripts": {
193
+ "gen:manifest": "hypequery generate:manifest analytics/api.ts --output src/generated/hypequery-manifest.json",
194
+ "build": "npm run gen:manifest && <your client build>"
238
195
  }
239
196
  }
240
197
  ```
241
198
 
242
- **Error handling example:**
199
+ ```ts
200
+ // client side — imports plain JSON, no server code in the bundle
201
+ import { createAnalyticsHooks } from '@hypequery/react';
202
+ import type { InferApiType } from '@hypequery/serve';
203
+ import type { api } from '../analytics/api.js';
204
+ import manifest from './generated/hypequery-manifest.json';
243
205
 
244
- ```tsx
245
- import { HttpError } from '@hypequery/react';
246
-
247
- function UserProfile({ userId }: { userId: string }) {
248
- const { data, error, isLoading } = useQuery('getUser', { id: userId });
249
-
250
- if (isLoading) return <Spinner />;
251
-
252
- if (error instanceof HttpError) {
253
- switch (error.status) {
254
- case 404:
255
- return <NotFound message="User not found" />;
256
- case 403:
257
- return <Unauthorized />;
258
- case 500:
259
- return <ServerError details={error.body} />;
260
- default:
261
- return <Error message={error.message} />;
262
- }
263
- }
206
+ type AnalyticsApi = InferApiType<typeof api>;
264
207
 
265
- return <div>{data.name}</div>;
266
- }
208
+ const { useMetric, useDataset } = createAnalyticsHooks<AnalyticsApi>({
209
+ baseUrl: '/api/analytics',
210
+ manifest,
211
+ });
267
212
  ```
268
213
 
269
- ---
214
+ The manifest is derived entirely from your serve config (`basePath`, route keys,
215
+ and `semanticPaths`), so the generated file is deterministic and only changes when
216
+ your API shape does — commit it or regenerate it on every build.
270
217
 
271
- ## Advanced Usage
218
+ > `baseUrl` supplies the origin/host; the per-endpoint path comes from the
219
+ > manifest (it already includes the server's `basePath`), so there's no
220
+ > double-prefixing. Keep `baseUrl` aligned with where the API is mounted.
272
221
 
273
- ### Custom fetch implementation
222
+ ### Explicit config
223
+
224
+ If a manifest is not available at runtime, pass route config explicitly. This
225
+ overrides any manifest entry.
274
226
 
275
227
  ```ts
276
- const { useQuery } = createHooks<Api>({
277
- baseUrl: '/api',
278
- fetchFn: async (url, init) => {
279
- // Add authentication token
280
- const token = await getAuthToken();
281
- return fetch(url, {
282
- ...init,
283
- headers: {
284
- ...init?.headers,
285
- Authorization: `Bearer ${token}`,
286
- },
287
- });
228
+ const { useQuery } = createHooks<AnalyticsApi>({
229
+ baseUrl: '/api/analytics',
230
+ config: {
231
+ weeklyRevenue: { method: 'POST', path: '/weeklyRevenue' },
288
232
  },
289
233
  });
290
234
  ```
291
235
 
292
- ### Default headers
236
+ `path` may be relative to `baseUrl`, absolute within the same origin, or an absolute HTTP URL.
293
237
 
294
- ```ts
295
- const { useQuery } = createHooks<Api>({
296
- baseUrl: '/api',
297
- headers: {
298
- 'X-API-Key': process.env.API_KEY,
299
- 'X-Client-Version': '1.0.0',
300
- },
301
- });
302
- ```
238
+ ## Headers & auth
303
239
 
304
- ### Dynamic headers
240
+ Pass static headers, or a function (sync or async) invoked per request — handy for
241
+ attaching a fresh/short-lived token.
305
242
 
306
243
  ```ts
307
- const { useQuery } = createHooks<Api>({
308
- baseUrl: '/api',
309
- headers: () => {
310
- const key = getTenantKey();
311
- return key ? { 'x-tenant-key': key } : {};
312
- },
244
+ const hooks = createHooks<AnalyticsApi>({
245
+ baseUrl: '/api/analytics',
246
+ headers: async () => ({
247
+ authorization: `Bearer ${await getAccessToken()}`,
248
+ }),
313
249
  });
314
250
  ```
315
251
 
316
- ### HTTP method configuration
252
+ Provide `onUnauthorized` to refresh credentials on a `401` and retry the request once
253
+ with freshly resolved headers:
317
254
 
318
255
  ```ts
319
- const { useQuery } = createHooks<Api>({
320
- baseUrl: '/api',
321
- config: {
322
- // Override default GET for specific queries
323
- refreshCache: { method: 'POST' },
324
- deleteUser: { method: 'DELETE' },
256
+ const hooks = createHooks<AnalyticsApi>({
257
+ baseUrl: '/api/analytics',
258
+ headers: () => ({ authorization: `Bearer ${tokenStore.access}` }),
259
+ onUnauthorized: async () => {
260
+ await tokenStore.refresh(); // throw to abort; resolve to retry once
325
261
  },
326
262
  });
327
263
  ```
328
264
 
329
- ### Auto-extract methods from API object
330
-
331
- If your API definition includes HTTP method information, pass it to auto-configure:
265
+ ## Query Options
332
266
 
333
- ```ts
334
- import { api } from '@/analytics/queries';
267
+ TanStack Query options can be passed as the final argument.
335
268
 
336
- const { useQuery } = createHooks({
337
- baseUrl: '/api',
338
- api, // Automatically extracts method config from api object
339
- });
269
+ ```tsx
270
+ const result = useQuery(
271
+ 'weeklyRevenue',
272
+ { startDate: '2026-01-01' },
273
+ {
274
+ staleTime: 60_000,
275
+ refetchOnWindowFocus: false,
276
+ },
277
+ );
340
278
  ```
341
279
 
342
- ---
343
-
344
- ## TypeScript
280
+ For no-input queries, use `queryOptions()` when an options object might look like input.
345
281
 
346
- All hooks are fully typed based on your API definition:
282
+ ```tsx
283
+ import { queryOptions } from '@hypequery/react';
347
284
 
348
- ```ts
349
- interface Api {
350
- getUser: {
351
- input: { id: string };
352
- output: { name: string; email: string };
353
- };
354
- weeklyRevenue: {
355
- input: { startDate: string };
356
- output: { total: number };
357
- };
358
- }
285
+ const result = useQuery('health', queryOptions({
286
+ staleTime: 30_000,
287
+ }));
288
+ ```
359
289
 
360
- const { useQuery } = createHooks<Api>({ baseUrl: '/api' });
290
+ ## Mutations
361
291
 
362
- // TypeScript infers input and output types
363
- const { data } = useQuery('getUser', { id: '123' });
364
- // ^? { name: string; email: string } | undefined
292
+ `useMutation(name)` uses the same endpoint typing and sends requests as `POST` by default unless route config overrides it.
365
293
 
366
- // ❌ TypeScript error: missing required input
367
- const { data } = useQuery('getUser');
294
+ ```tsx
295
+ const refresh = useMutation('refreshReport');
368
296
 
369
- // ❌ TypeScript error: invalid property
370
- const { data } = useQuery('getUser', { userId: '123' });
297
+ refresh.mutate({ reportId: 'revenue' });
371
298
  ```
372
299
 
373
- ---
300
+ ## Notes
301
+
302
+ - This package does not define datasets. Use `@hypequery/datasets` for semantic definitions.
303
+ - This package does not generate SQL. It calls HTTP routes exposed by `@hypequery/serve`.
304
+ - Dataset relationship JOIN execution is not implied by the React hooks; hooks consume whatever endpoint behavior the server exposes.
374
305
 
375
306
  ## License
376
307
 
377
- MIT
308
+ Apache-2.0.
@@ -0,0 +1,34 @@
1
+ import type { UseQueryOptions as TanstackUseQueryOptions, UseQueryResult, UseInfiniteQueryResult, InfiniteData } from '@tanstack/react-query';
2
+ import { type CreateHooksConfig } from './createHooks.js';
3
+ import { HttpError } from './errors.js';
4
+ import type { ExtractNames, QueryInput, QueryOutput, QueryOutputForInput } from './types.js';
5
+ type DatasetKey<Name extends string> = `dataset:${Name}`;
6
+ type DatasetNamesFromApi<Api> = ExtractNames<Api> extends infer Key ? Key extends `dataset:${infer Name}` ? Name : never : never;
7
+ type QueryKey<Name extends string, Input> = Input extends never ? ['hypequery', Name] : ['hypequery', Name, Input];
8
+ type QueryOptions<Api, Key extends ExtractNames<Api>> = Omit<TanstackUseQueryOptions<QueryOutput<Api, Key>, HttpError, QueryOutput<Api, Key>, QueryKey<Key, QueryInput<Api, Key>>>, 'queryKey' | 'queryFn'>;
9
+ export interface CreateAnalyticsHooksConfig<Api extends Record<string, {
10
+ input: any;
11
+ output: any;
12
+ }>, TMetrics extends readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[] = readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[]> extends CreateHooksConfig<Api> {
13
+ metrics?: TMetrics;
14
+ }
15
+ export declare function createAnalyticsHooks<Api extends Record<string, {
16
+ input: any;
17
+ output: any;
18
+ }>, TMetrics extends readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[] = readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[]>(config: CreateAnalyticsHooksConfig<Api, TMetrics>): {
19
+ readonly useMetric: {
20
+ <Name extends TMetrics extends readonly (infer U)[] ? U extends string ? U : never : Exclude<ExtractNames<Api>, `dataset:${string}`>>(...args: QueryInput<Api, Name> extends never ? [name: Name, options?: QueryOptions<Api, Name>] : never): UseQueryResult<QueryOutput<Api, Name>, HttpError>;
21
+ <Name extends TMetrics extends readonly (infer U)[] ? U extends string ? U : never : Exclude<ExtractNames<Api>, `dataset:${string}`>, const TInput extends QueryInput<Api, Name>>(name: Name, input: TInput, options?: QueryOptions<Api, Name>): UseQueryResult<QueryOutputForInput<Api, Name, TInput>, HttpError>;
22
+ };
23
+ readonly useDataset: {
24
+ <Name extends DatasetNamesFromApi<Api>>(...args: QueryInput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>> extends never ? [name: Name, options?: QueryOptions<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>>] : never): UseQueryResult<QueryOutput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>>, HttpError>;
25
+ <Name extends DatasetNamesFromApi<Api>, const TInput extends QueryInput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>>>(name: Name, input: TInput, options?: QueryOptions<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>>): UseQueryResult<QueryOutputForInput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>, TInput>, HttpError>;
26
+ };
27
+ readonly useInfiniteMetric: <Name extends TMetrics extends readonly (infer U)[] ? U extends string ? U : never : Exclude<ExtractNames<Api>, `dataset:${string}`>>(name: Name, input: QueryInput<Api, Name>, options?: Parameters<(<Name_1 extends ExtractNames<Api>>(name: Name_1, input: QueryInput<Api, Name_1>, options?: Omit<import("@tanstack/react-query").UseInfiniteQueryOptions<QueryOutput<Api, Name_1>, HttpError, InfiniteData<QueryOutput<Api, Name_1>, number>, QueryInput<Api, Name_1> extends never ? ["hypequery", Name_1] : ["hypequery", Name_1, QueryInput<Api, Name_1>], number>, "queryKey" | "queryFn" | "getNextPageParam" | "initialPageParam"> | undefined) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Name_1>, number>, HttpError>)>[2]) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Name>, number>, HttpError>;
28
+ readonly useInfiniteDataset: <Name extends DatasetNamesFromApi<Api>>(name: Name, input: QueryInput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>>, options?: Parameters<(<Name_1 extends ExtractNames<Api>>(name: Name_1, input: QueryInput<Api, Name_1>, options?: Omit<import("@tanstack/react-query").UseInfiniteQueryOptions<QueryOutput<Api, Name_1>, HttpError, InfiniteData<QueryOutput<Api, Name_1>, number>, QueryInput<Api, Name_1> extends never ? ["hypequery", Name_1] : ["hypequery", Name_1, QueryInput<Api, Name_1>], number>, "queryKey" | "queryFn" | "getNextPageParam" | "initialPageParam"> | undefined) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Name_1>, number>, HttpError>)>[2]) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>>, number>, HttpError>;
29
+ readonly useQuery: <Name extends ExtractNames<Api>>(...args: QueryInput<Api, Name> extends never ? [name: Name, options?: Omit<TanstackUseQueryOptions<QueryOutput<Api, Name>, HttpError, QueryOutput<Api, Name>, QueryInput<Api, Name> extends never ? ["hypequery", Name] : ["hypequery", Name, QueryInput<Api, Name>]>, "queryKey" | "queryFn"> | undefined] : [name: Name, input: QueryInput<Api, Name>, options?: Omit<TanstackUseQueryOptions<QueryOutput<Api, Name>, HttpError, QueryOutput<Api, Name>, QueryInput<Api, Name> extends never ? ["hypequery", Name] : ["hypequery", Name, QueryInput<Api, Name>]>, "queryKey" | "queryFn"> | undefined]) => UseQueryResult<QueryOutput<Api, Name>, HttpError>;
30
+ readonly useMutation: <Name extends ExtractNames<Api>>(name: Name, options?: Omit<import("@tanstack/react-query").UseMutationOptions<QueryOutput<Api, Name>, HttpError, QueryInput<Api, Name>, unknown>, "mutationFn"> | undefined) => import("@tanstack/react-query").UseMutationResult<QueryOutput<Api, Name>, HttpError, QueryInput<Api, Name>>;
31
+ readonly useInfiniteQuery: <Name extends ExtractNames<Api>>(name: Name, input: QueryInput<Api, Name>, options?: Omit<import("@tanstack/react-query").UseInfiniteQueryOptions<QueryOutput<Api, Name>, HttpError, InfiniteData<QueryOutput<Api, Name>, number>, QueryInput<Api, Name> extends never ? ["hypequery", Name] : ["hypequery", Name, QueryInput<Api, Name>], number>, "queryKey" | "queryFn" | "getNextPageParam" | "initialPageParam"> | undefined) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Name>, number>, HttpError>;
32
+ };
33
+ export {};
34
+ //# sourceMappingURL=analyticsHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyticsHooks.d.ts","sourceRoot":"","sources":["../src/analyticsHooks.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { createHooks } from './createHooks.js';
2
+ export function createAnalyticsHooks(config) {
3
+ const hooks = createHooks(config);
4
+ function useMetric(...args) {
5
+ const [name, ...rest] = args;
6
+ return hooks.useQuery(name, ...rest);
7
+ }
8
+ function useDataset(...args) {
9
+ const [name, ...rest] = args;
10
+ const key = `dataset:${String(name)}`;
11
+ return hooks.useQuery(key, ...rest);
12
+ }
13
+ function useInfiniteMetric(name, input, options) {
14
+ return hooks.useInfiniteQuery(name, input, options);
15
+ }
16
+ function useInfiniteDataset(name, input, options) {
17
+ const key = `dataset:${String(name)}`;
18
+ return hooks.useInfiniteQuery(key, input, options);
19
+ }
20
+ return {
21
+ ...hooks,
22
+ useMetric,
23
+ useDataset,
24
+ useInfiniteMetric,
25
+ useInfiniteDataset,
26
+ };
27
+ }