@hypequery/react 0.5.1 → 0.6.1
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 +51 -0
- package/dist/analyticsHooks.d.ts +4 -4
- package/dist/analyticsHooks.d.ts.map +1 -1
- package/dist/client.d.ts +41 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +128 -0
- package/dist/createHooks.d.ts +5 -49
- package/dist/createHooks.d.ts.map +1 -1
- package/dist/createHooks.js +19 -134
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/provider.d.ts +13 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +18 -0
- package/dist/provider.test.d.ts +2 -0
- package/dist/provider.test.d.ts.map +1 -0
- package/dist/provider.test.js +53 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -21,6 +21,57 @@ npx hypequery generate:manifest analytics/api.ts \
|
|
|
21
21
|
--output src/generated/hypequery-manifest.json
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
## Configure Once With `HypequeryProvider`
|
|
25
|
+
|
|
26
|
+
For hosted APIs and generated clients, create hooks without transport
|
|
27
|
+
configuration. A provider supplies one client to the application and includes a
|
|
28
|
+
TanStack Query provider by default.
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
// client/hypequery.ts
|
|
32
|
+
import { createHooks } from '@hypequery/react';
|
|
33
|
+
import type { AnalyticsApi } from '../server/api.js';
|
|
34
|
+
|
|
35
|
+
export const { useQuery, useMutation } = createHooks<AnalyticsApi>();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// app/providers.tsx
|
|
40
|
+
'use client';
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
createHypequeryClient,
|
|
44
|
+
HypequeryProvider,
|
|
45
|
+
} from '@hypequery/react';
|
|
46
|
+
import type { ReactNode } from 'react';
|
|
47
|
+
import type { AnalyticsApi } from '../server/api.js';
|
|
48
|
+
import manifest from '../generated/hypequery-manifest.json';
|
|
49
|
+
|
|
50
|
+
const client = createHypequeryClient<AnalyticsApi>({
|
|
51
|
+
baseUrl: 'https://acme.hypequery.cloud/v1/analytics/production',
|
|
52
|
+
manifest,
|
|
53
|
+
// Resolve a short-lived browser token. Never expose a server API key here.
|
|
54
|
+
token: async () => tokenStore.get(),
|
|
55
|
+
onUnauthorized: () => tokenStore.refresh(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
59
|
+
return <HypequeryProvider client={client}>{children}</HypequeryProvider>;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Applications that already own a TanStack Query client can pass it through the
|
|
64
|
+
`queryClient` prop so Hypequery shares the existing cache.
|
|
65
|
+
|
|
66
|
+
Each client instance receives an isolated cache scope. Create a new client when
|
|
67
|
+
the deployment, tenant, or authenticated user changes. A deterministic
|
|
68
|
+
`cacheKey` may be supplied when the same security context must retain cache data
|
|
69
|
+
across equivalent client instances.
|
|
70
|
+
|
|
71
|
+
## Create Dataset And Metric Hooks
|
|
72
|
+
|
|
73
|
+
`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, ...)`.
|
|
74
|
+
|
|
24
75
|
```tsx
|
|
25
76
|
import { createAnalyticsHooks } from '@hypequery/react';
|
|
26
77
|
import type { AnalyticsApi } from '../server/api.js';
|
package/dist/analyticsHooks.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { HttpError } from './errors.js';
|
|
|
4
4
|
import type { ExtractNames, QueryInput, QueryOutput, QueryOutputForInput } from './types.js';
|
|
5
5
|
type DatasetKey<Name extends string> = `dataset:${Name}`;
|
|
6
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];
|
|
7
|
+
type QueryKey<Name extends string, Input> = Input extends never ? ['hypequery', string, Name] : ['hypequery', string, Name, Input];
|
|
8
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
9
|
/** Infinite-query options parameterized by the projected page type. */
|
|
10
10
|
type InfiniteQueryOptions<Api, Key extends ExtractNames<Api>, TPage> = Omit<TanstackUseInfiniteQueryOptions<TPage, HttpError, InfiniteData<TPage, number>, QueryKey<Key, QueryInput<Api, Key>>, number>, 'queryKey' | 'queryFn' | 'getNextPageParam' | 'initialPageParam'>;
|
|
@@ -17,7 +17,7 @@ export interface CreateAnalyticsHooksConfig<Api extends Record<string, {
|
|
|
17
17
|
export declare function createAnalyticsHooks<Api extends Record<string, {
|
|
18
18
|
input: any;
|
|
19
19
|
output: any;
|
|
20
|
-
}>, TMetrics extends readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[] = readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[]>(config
|
|
20
|
+
}>, TMetrics extends readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[] = readonly Exclude<ExtractNames<Api>, `dataset:${string}`>[]>(config?: CreateAnalyticsHooksConfig<Api, TMetrics>): {
|
|
21
21
|
readonly useMetric: {
|
|
22
22
|
<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>;
|
|
23
23
|
<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>;
|
|
@@ -28,9 +28,9 @@ export declare function createAnalyticsHooks<Api extends Record<string, {
|
|
|
28
28
|
};
|
|
29
29
|
readonly useInfiniteMetric: <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?: InfiniteQueryOptions<Api, Name, QueryOutputForInput<Api, Name, TInput>>) => UseInfiniteQueryResult<InfiniteData<QueryOutputForInput<Api, Name, TInput>, number>, HttpError>;
|
|
30
30
|
readonly useInfiniteDataset: <Name extends DatasetNamesFromApi<Api>, const TInput extends QueryInput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>>>(name: Name, input: TInput, options?: InfiniteQueryOptions<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>, QueryOutputForInput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>, TInput>>) => UseInfiniteQueryResult<InfiniteData<QueryOutputForInput<Api, Extract<ExtractNames<Api>, DatasetKey<Name>>, TInput>, number>, HttpError>;
|
|
31
|
-
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>;
|
|
31
|
+
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", string, Name] : ["hypequery", string, 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", string, Name] : ["hypequery", string, Name, QueryInput<Api, Name>]>, "queryKey" | "queryFn"> | undefined]) => UseQueryResult<QueryOutput<Api, Name>, HttpError>;
|
|
32
32
|
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>>;
|
|
33
|
-
readonly useInfiniteQuery: <Name extends ExtractNames<Api>>(name: Name, input: QueryInput<Api, Name>, options?: Omit<TanstackUseInfiniteQueryOptions<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>;
|
|
33
|
+
readonly useInfiniteQuery: <Name extends ExtractNames<Api>>(name: Name, input: QueryInput<Api, Name>, options?: Omit<TanstackUseInfiniteQueryOptions<QueryOutput<Api, Name>, HttpError, InfiniteData<QueryOutput<Api, Name>, number>, QueryInput<Api, Name> extends never ? ["hypequery", string, Name] : ["hypequery", string, Name, QueryInput<Api, Name>], number>, "queryKey" | "queryFn" | "getNextPageParam" | "initialPageParam"> | undefined) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Name>, number>, HttpError>;
|
|
34
34
|
};
|
|
35
35
|
export {};
|
|
36
36
|
//# sourceMappingURL=analyticsHooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyticsHooks.d.ts","sourceRoot":"","sources":["../src/analyticsHooks.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,IAAI,uBAAuB,EAC1C,cAAc,EACd,uBAAuB,IAAI,+BAA+B,EAC1D,sBAAsB,EACtB,YAAY,EACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAe,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE7F,KAAK,UAAU,CAAC,IAAI,SAAS,MAAM,IAAI,WAAW,IAAI,EAAE,CAAC;AACzD,KAAK,mBAAmB,CAAC,GAAG,IAC1B,YAAY,CAAC,GAAG,CAAC,SAAS,MAAM,GAAG,GAC/B,GAAG,SAAS,WAAW,MAAM,IAAI,EAAE,GACjC,IAAI,GACJ,KAAK,GACP,KAAK,CAAC;AAEZ,KAAK,QAAQ,CAAC,IAAI,SAAS,MAAM,EAAE,KAAK,IAAI,KAAK,SAAS,KAAK,GAC3D,CAAC,WAAW,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"analyticsHooks.d.ts","sourceRoot":"","sources":["../src/analyticsHooks.tsx"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type ApiContract = Record<string, {
|
|
2
|
+
input: any;
|
|
3
|
+
output: any;
|
|
4
|
+
}>;
|
|
5
|
+
export interface QueryMethodConfig {
|
|
6
|
+
method?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface RouteManifestEntry {
|
|
10
|
+
method?: string;
|
|
11
|
+
path?: string;
|
|
12
|
+
}
|
|
13
|
+
export type RouteManifest = Record<string, RouteManifestEntry>;
|
|
14
|
+
type HeaderMap = Record<string, string | undefined>;
|
|
15
|
+
export type HeadersInput = HeaderMap | (() => HeaderMap | Promise<HeaderMap>);
|
|
16
|
+
export type TokenInput = string | (() => string | undefined | Promise<string | undefined>);
|
|
17
|
+
export interface HypequeryClientConfig<Api extends ApiContract = ApiContract> {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
/**
|
|
20
|
+
* Stable cache scope for this deployment and authentication context. Omit it
|
|
21
|
+
* to create an isolated scope for this client instance.
|
|
22
|
+
*/
|
|
23
|
+
cacheKey?: string;
|
|
24
|
+
fetchFn?: typeof fetch;
|
|
25
|
+
headers?: HeadersInput;
|
|
26
|
+
/** A bearer token or per-request token resolver. Never put a server API key in browser code. */
|
|
27
|
+
token?: TokenInput;
|
|
28
|
+
config?: Record<string, QueryMethodConfig>;
|
|
29
|
+
manifest?: RouteManifest;
|
|
30
|
+
/** Refresh authentication state after a 401. The request is retried once. */
|
|
31
|
+
onUnauthorized?: () => void | Promise<void>;
|
|
32
|
+
api?: Api;
|
|
33
|
+
}
|
|
34
|
+
export interface HypequeryClient<Api extends ApiContract = ApiContract> {
|
|
35
|
+
readonly config: Readonly<HypequeryClientConfig<Api>>;
|
|
36
|
+
readonly cacheKey: string;
|
|
37
|
+
request(name: string, input?: unknown, defaultMethod?: string, extraHeaders?: Record<string, string>): Promise<unknown>;
|
|
38
|
+
}
|
|
39
|
+
export declare function createHypequeryClient<Api extends ApiContract = ApiContract>(config: HypequeryClientConfig<Api>): HypequeryClient<Api>;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AAE/D,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AACpD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;AAC9E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAE3F,MAAM,WAAW,qBAAqB,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,gGAAgG;IAChG,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAqED,MAAM,WAAW,eAAe,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW;IACpE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzH;AAID,wBAAgB,qBAAqB,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW,EACzE,MAAM,EAAE,qBAAqB,CAAC,GAAG,CAAC,GACjC,eAAe,CAAC,GAAG,CAAC,CAwFtB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { HttpError } from './errors.js';
|
|
2
|
+
const normalizeMethodConfig = (source) => {
|
|
3
|
+
if (!source)
|
|
4
|
+
return {};
|
|
5
|
+
return Object.fromEntries(Object.entries(source).map(([key, value]) => [key, { method: value.method ?? 'GET', path: value.path }]));
|
|
6
|
+
};
|
|
7
|
+
const deriveMethodConfig = (api) => {
|
|
8
|
+
if (typeof api !== 'object' || api === null)
|
|
9
|
+
return {};
|
|
10
|
+
if (typeof api.manifest === 'function') {
|
|
11
|
+
const manifest = api.manifest();
|
|
12
|
+
if (manifest && typeof manifest === 'object')
|
|
13
|
+
return normalizeMethodConfig(manifest);
|
|
14
|
+
}
|
|
15
|
+
if ('_routeConfig' in api &&
|
|
16
|
+
typeof api._routeConfig === 'object' &&
|
|
17
|
+
api._routeConfig !== null) {
|
|
18
|
+
return normalizeMethodConfig(api._routeConfig);
|
|
19
|
+
}
|
|
20
|
+
if ('queries' in api &&
|
|
21
|
+
typeof api.queries === 'object' &&
|
|
22
|
+
api.queries !== null) {
|
|
23
|
+
return normalizeMethodConfig(api.queries);
|
|
24
|
+
}
|
|
25
|
+
return {};
|
|
26
|
+
};
|
|
27
|
+
const isAbsoluteHttpUrl = (value) => /^https?:\/\//.test(value);
|
|
28
|
+
const ensureTrailingSlash = (value) => value.endsWith('/') ? value : `${value}/`;
|
|
29
|
+
const buildUrl = (baseUrl, name, path) => {
|
|
30
|
+
if (path) {
|
|
31
|
+
if (isAbsoluteHttpUrl(path))
|
|
32
|
+
return path;
|
|
33
|
+
if (isAbsoluteHttpUrl(baseUrl))
|
|
34
|
+
return new URL(path, ensureTrailingSlash(baseUrl)).toString();
|
|
35
|
+
if (path.startsWith('/'))
|
|
36
|
+
return path;
|
|
37
|
+
if (!baseUrl)
|
|
38
|
+
throw new Error('baseUrl is required');
|
|
39
|
+
return `${ensureTrailingSlash(baseUrl)}${path}`;
|
|
40
|
+
}
|
|
41
|
+
if (!baseUrl)
|
|
42
|
+
throw new Error('baseUrl is required');
|
|
43
|
+
return `${ensureTrailingSlash(baseUrl)}${name}`;
|
|
44
|
+
};
|
|
45
|
+
const parseResponse = async (response) => {
|
|
46
|
+
const text = await response.text();
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return text;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const resolveHeaders = async (headers) => {
|
|
55
|
+
if (!headers)
|
|
56
|
+
return {};
|
|
57
|
+
const raw = typeof headers === 'function' ? await headers() : headers;
|
|
58
|
+
return Object.fromEntries(Object.entries(raw).filter(([, value]) => value !== undefined));
|
|
59
|
+
};
|
|
60
|
+
const resolveToken = async (token) => typeof token === 'function' ? token() : token;
|
|
61
|
+
let clientSequence = 0;
|
|
62
|
+
export function createHypequeryClient(config) {
|
|
63
|
+
const { baseUrl, fetchFn = fetch, headers, token, config: explicitConfig = {}, manifest, onUnauthorized, api, } = config;
|
|
64
|
+
const cacheKey = config.cacheKey ?? `client:${++clientSequence}`;
|
|
65
|
+
const finalConfig = {
|
|
66
|
+
...deriveMethodConfig(api),
|
|
67
|
+
...(manifest ? normalizeMethodConfig(manifest) : {}),
|
|
68
|
+
...explicitConfig,
|
|
69
|
+
};
|
|
70
|
+
const request = async (name, input, defaultMethod = 'GET', extraHeaders) => {
|
|
71
|
+
const methodConfig = finalConfig[name];
|
|
72
|
+
if (name.includes(':') && !methodConfig?.path) {
|
|
73
|
+
throw new Error(`No route configured for "${name}". Pass \`manifest\` (from the serve ` +
|
|
74
|
+
`api.manifest()) or an explicit \`config\` entry to createHypequeryClient().`);
|
|
75
|
+
}
|
|
76
|
+
const url = buildUrl(baseUrl, name, methodConfig?.path);
|
|
77
|
+
const method = methodConfig?.method ?? defaultMethod;
|
|
78
|
+
let finalUrl = url;
|
|
79
|
+
let body;
|
|
80
|
+
if (method === 'GET' && input && typeof input === 'object') {
|
|
81
|
+
const params = new URLSearchParams();
|
|
82
|
+
for (const [key, value] of Object.entries(input)) {
|
|
83
|
+
if (value === undefined || value === null)
|
|
84
|
+
continue;
|
|
85
|
+
if (Array.isArray(value))
|
|
86
|
+
value.forEach((item) => params.append(key, String(item)));
|
|
87
|
+
else
|
|
88
|
+
params.append(key, String(value));
|
|
89
|
+
}
|
|
90
|
+
const queryString = params.toString();
|
|
91
|
+
finalUrl = queryString ? `${url}?${queryString}` : url;
|
|
92
|
+
}
|
|
93
|
+
else if (input !== undefined) {
|
|
94
|
+
body = JSON.stringify(input);
|
|
95
|
+
}
|
|
96
|
+
const attempt = async () => {
|
|
97
|
+
const [resolvedHeaders, resolvedToken] = await Promise.all([
|
|
98
|
+
resolveHeaders(headers),
|
|
99
|
+
resolveToken(token),
|
|
100
|
+
]);
|
|
101
|
+
return fetchFn(finalUrl, {
|
|
102
|
+
method,
|
|
103
|
+
headers: {
|
|
104
|
+
...resolvedHeaders,
|
|
105
|
+
...(resolvedToken ? { authorization: `Bearer ${resolvedToken}` } : {}),
|
|
106
|
+
...(extraHeaders ?? {}),
|
|
107
|
+
...(body ? { 'content-type': 'application/json' } : {}),
|
|
108
|
+
},
|
|
109
|
+
body,
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
let response = await attempt();
|
|
113
|
+
if (response.status === 401 && onUnauthorized) {
|
|
114
|
+
await onUnauthorized();
|
|
115
|
+
response = await attempt();
|
|
116
|
+
}
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
const errorBody = await parseResponse(response);
|
|
119
|
+
throw new HttpError(`${method} request to ${finalUrl} failed with status ${response.status}`, response.status, errorBody);
|
|
120
|
+
}
|
|
121
|
+
return response.json();
|
|
122
|
+
};
|
|
123
|
+
return Object.freeze({
|
|
124
|
+
cacheKey,
|
|
125
|
+
config: Object.freeze({ ...config }),
|
|
126
|
+
request,
|
|
127
|
+
});
|
|
128
|
+
}
|
package/dist/createHooks.d.ts
CHANGED
|
@@ -1,60 +1,16 @@
|
|
|
1
1
|
import { type UseQueryOptions as TanstackUseQueryOptions, type UseMutationOptions as TanstackUseMutationOptions, type UseInfiniteQueryOptions as TanstackUseInfiniteQueryOptions, type UseMutationResult, type UseQueryResult, type UseInfiniteQueryResult, type InfiniteData } from '@tanstack/react-query';
|
|
2
2
|
import type { ExtractNames, QueryInput, QueryOutput } from './types.js';
|
|
3
3
|
import { HttpError } from './errors.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
path?: string;
|
|
7
|
-
}
|
|
8
|
-
/** A single route as produced by `@hypequery/serve`'s `api.manifest()`. */
|
|
9
|
-
export interface RouteManifestEntry {
|
|
10
|
-
method?: string;
|
|
11
|
-
path?: string;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Map of query/metric/dataset keys to their HTTP method and full path. Pair
|
|
15
|
-
* with `InferAPIType` and `api.manifest()` from `@hypequery/serve` to resolve
|
|
16
|
-
* routes on the client without importing server code into the bundle.
|
|
17
|
-
*/
|
|
18
|
-
export type RouteManifest = Record<string, RouteManifestEntry>;
|
|
19
|
-
type HeaderMap = Record<string, string | undefined>;
|
|
20
|
-
type HeadersInput = HeaderMap | (() => HeaderMap | Promise<HeaderMap>);
|
|
21
|
-
export interface CreateHooksConfig<TApi = Record<string, {
|
|
22
|
-
input: unknown;
|
|
23
|
-
output: unknown;
|
|
24
|
-
}>> {
|
|
25
|
-
baseUrl: string;
|
|
26
|
-
fetchFn?: typeof fetch;
|
|
27
|
-
/**
|
|
28
|
-
* Static headers, or a (optionally async) function returning headers. The
|
|
29
|
-
* function is invoked per request, so it can supply a fresh/short-lived token.
|
|
30
|
-
*/
|
|
31
|
-
headers?: HeadersInput;
|
|
32
|
-
config?: Record<string, QueryMethodConfig>;
|
|
33
|
-
/**
|
|
34
|
-
* Route manifest from `@hypequery/serve`'s `api.manifest()`. Resolves the
|
|
35
|
-
* method and full path for each key — required for metric/dataset endpoints,
|
|
36
|
-
* which are POST routes whose paths differ from their map keys.
|
|
37
|
-
*/
|
|
38
|
-
manifest?: RouteManifest;
|
|
39
|
-
/**
|
|
40
|
-
* Called when a request returns 401. Use it to refresh credentials (e.g. a
|
|
41
|
-
* token). If it resolves without throwing, the request is retried once with
|
|
42
|
-
* freshly resolved headers.
|
|
43
|
-
*/
|
|
44
|
-
onUnauthorized?: () => void | Promise<void>;
|
|
45
|
-
api?: TApi;
|
|
46
|
-
}
|
|
4
|
+
import { type ApiContract, type HypequeryClientConfig } from './client.js';
|
|
5
|
+
export type CreateHooksConfig<TApi extends ApiContract = ApiContract> = HypequeryClientConfig<TApi>;
|
|
47
6
|
declare const OPTIONS_SYMBOL: unique symbol;
|
|
48
7
|
export declare function queryOptions<T extends object>(opts: T): T & {
|
|
49
8
|
[OPTIONS_SYMBOL]: true;
|
|
50
9
|
};
|
|
51
|
-
export declare function createHooks<Api extends
|
|
52
|
-
input:
|
|
53
|
-
output: any;
|
|
54
|
-
}>>(config: CreateHooksConfig<Api>): {
|
|
55
|
-
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>;
|
|
10
|
+
export declare function createHooks<Api extends ApiContract>(config?: CreateHooksConfig<Api>): {
|
|
11
|
+
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", string, Name] : ["hypequery", string, 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", string, Name] : ["hypequery", string, Name, QueryInput<Api, Name>]>, "queryKey" | "queryFn"> | undefined]) => UseQueryResult<QueryOutput<Api, Name>, HttpError>;
|
|
56
12
|
readonly useMutation: <Name extends ExtractNames<Api>>(name: Name, options?: Omit<TanstackUseMutationOptions<QueryOutput<Api, Name>, HttpError, QueryInput<Api, Name>, unknown>, "mutationFn">) => UseMutationResult<QueryOutput<Api, Name>, HttpError, QueryInput<Api, Name>>;
|
|
57
|
-
readonly useInfiniteQuery: <Name extends ExtractNames<Api>>(name: Name, input: QueryInput<Api, Name>, options?: Omit<TanstackUseInfiniteQueryOptions<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">) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Name>, number>, HttpError>;
|
|
13
|
+
readonly useInfiniteQuery: <Name extends ExtractNames<Api>>(name: Name, input: QueryInput<Api, Name>, options?: Omit<TanstackUseInfiniteQueryOptions<QueryOutput<Api, Name>, HttpError, InfiniteData<QueryOutput<Api, Name>, number>, QueryInput<Api, Name> extends never ? ["hypequery", string, Name] : ["hypequery", string, Name, QueryInput<Api, Name>], number>, "queryKey" | "queryFn" | "getNextPageParam" | "initialPageParam">) => UseInfiniteQueryResult<InfiniteData<QueryOutput<Api, Name>, number>, HttpError>;
|
|
58
14
|
};
|
|
59
15
|
export {};
|
|
60
16
|
//# sourceMappingURL=createHooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createHooks.d.ts","sourceRoot":"","sources":["../src/createHooks.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,eAAe,IAAI,uBAAuB,EAC/C,KAAK,kBAAkB,IAAI,0BAA0B,EACrD,KAAK,uBAAuB,IAAI,+BAA+B,EAC/D,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"createHooks.d.ts","sourceRoot":"","sources":["../src/createHooks.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,eAAe,IAAI,uBAAuB,EAC/C,KAAK,kBAAkB,IAAI,0BAA0B,EACrD,KAAK,uBAAuB,IAAI,+BAA+B,EAC/D,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAC;AAWrB,MAAM,MAAM,iBAAiB,CAAC,IAAI,SAAS,WAAW,GAAG,WAAW,IAClE,qBAAqB,CAAC,IAAI,CAAC,CAAC;AAE9B,QAAA,MAAM,cAAc,eAAkC,CAAC;AAEvD,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG;IAAE,CAAC,cAAc,CAAC,EAAE,IAAI,CAAA;CAAE,CAEtF;AAcD,wBAAgB,WAAW,CAAC,GAAG,SAAS,WAAW,EACjD,MAAM,CAAC,EAAE,iBAAiB,CAAC,GAAG,CAAC;wBA0Bb,IAAI,SAAS,YAAY,CAAC,GAAG,CAAC,+mBAE7C,cAAc,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,SAAS,CAAC;2BAyC/B,IAAI,SAAS,YAAY,CAAC,GAAG,CAAC,QAC3C,IAAI,kIAET,iBAAiB,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gCAyBpD,IAAI,SAAS,YAAY,CAAC,GAAG,CAAC,QAChD,IAAI,SACH,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,yUAE3B,sBAAsB,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC;EA6BnF"}
|
package/dist/createHooks.js
CHANGED
|
@@ -1,69 +1,10 @@
|
|
|
1
1
|
import { useQuery as useTanstackQuery, useMutation as useTanstackMutation, useInfiniteQuery as useTanstackInfiniteQuery, } from '@tanstack/react-query';
|
|
2
|
-
import {
|
|
2
|
+
import { createHypequeryClient, } from './client.js';
|
|
3
|
+
import { useOptionalHypequeryClient } from './provider.js';
|
|
3
4
|
const OPTIONS_SYMBOL = Symbol.for('hypequery-options');
|
|
4
5
|
export function queryOptions(opts) {
|
|
5
6
|
return { ...opts, [OPTIONS_SYMBOL]: true };
|
|
6
7
|
}
|
|
7
|
-
const normalizeMethodConfig = (source) => {
|
|
8
|
-
if (!source)
|
|
9
|
-
return {};
|
|
10
|
-
return Object.fromEntries(Object.entries(source).map(([key, value]) => [key, { method: value.method ?? 'GET', path: value.path }]));
|
|
11
|
-
};
|
|
12
|
-
const deriveMethodConfig = (api) => {
|
|
13
|
-
if (typeof api !== 'object' || api === null) {
|
|
14
|
-
return {};
|
|
15
|
-
}
|
|
16
|
-
// Preferred: the serve API exposes a manifest() with full method + path.
|
|
17
|
-
if (typeof api.manifest === 'function') {
|
|
18
|
-
const manifest = api.manifest();
|
|
19
|
-
if (manifest && typeof manifest === 'object') {
|
|
20
|
-
return normalizeMethodConfig(manifest);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
if ('_routeConfig' in api &&
|
|
24
|
-
typeof api._routeConfig === 'object' &&
|
|
25
|
-
api._routeConfig !== null) {
|
|
26
|
-
return normalizeMethodConfig(api._routeConfig);
|
|
27
|
-
}
|
|
28
|
-
if ('queries' in api &&
|
|
29
|
-
typeof api.queries === 'object' &&
|
|
30
|
-
api.queries !== null) {
|
|
31
|
-
return normalizeMethodConfig(api.queries);
|
|
32
|
-
}
|
|
33
|
-
return {};
|
|
34
|
-
};
|
|
35
|
-
const isAbsoluteHttpUrl = (value) => /^https?:\/\//.test(value);
|
|
36
|
-
const ensureTrailingSlash = (value) => value.endsWith('/') ? value : `${value}/`;
|
|
37
|
-
const buildUrl = (baseUrl, name, path) => {
|
|
38
|
-
if (path) {
|
|
39
|
-
if (isAbsoluteHttpUrl(path)) {
|
|
40
|
-
return path;
|
|
41
|
-
}
|
|
42
|
-
if (isAbsoluteHttpUrl(baseUrl)) {
|
|
43
|
-
return new URL(path, ensureTrailingSlash(baseUrl)).toString();
|
|
44
|
-
}
|
|
45
|
-
if (path.startsWith('/')) {
|
|
46
|
-
return path;
|
|
47
|
-
}
|
|
48
|
-
if (!baseUrl) {
|
|
49
|
-
throw new Error('baseUrl is required');
|
|
50
|
-
}
|
|
51
|
-
return `${ensureTrailingSlash(baseUrl)}${path}`;
|
|
52
|
-
}
|
|
53
|
-
if (!baseUrl) {
|
|
54
|
-
throw new Error('baseUrl is required');
|
|
55
|
-
}
|
|
56
|
-
return `${ensureTrailingSlash(baseUrl)}${name}`;
|
|
57
|
-
};
|
|
58
|
-
const parseResponse = async (res) => {
|
|
59
|
-
const text = await res.text();
|
|
60
|
-
try {
|
|
61
|
-
return JSON.parse(text);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return text;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
8
|
const isOptionsBag = (value) => {
|
|
68
9
|
return Boolean(value && typeof value === 'object' && OPTIONS_SYMBOL in value);
|
|
69
10
|
};
|
|
@@ -76,76 +17,18 @@ const looksLikeQueryOptions = (value) => {
|
|
|
76
17
|
const matches = optionKeys.filter((key) => key in value).length;
|
|
77
18
|
return matches >= 2;
|
|
78
19
|
};
|
|
79
|
-
const resolveHeaders = async (headers) => {
|
|
80
|
-
if (!headers)
|
|
81
|
-
return {};
|
|
82
|
-
const raw = typeof headers === 'function' ? await headers() : headers;
|
|
83
|
-
return Object.fromEntries(Object.entries(raw).filter(([, value]) => value !== undefined));
|
|
84
|
-
};
|
|
85
20
|
export function createHooks(config) {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
const fetchQuery = async (name, input, defaultMethod = 'GET', extraHeaders) => {
|
|
94
|
-
const methodConfig = finalConfig[name];
|
|
95
|
-
// Semantic endpoints (dataset:<name>) live at paths that differ from their
|
|
96
|
-
// map key, so without a resolved path we'd call the wrong URL. Fail loudly
|
|
97
|
-
// instead of silently requesting `${baseUrl}/dataset:<name>`.
|
|
98
|
-
if (name.includes(':') && !methodConfig?.path) {
|
|
99
|
-
throw new Error(`No route configured for "${name}". Pass \`manifest\` (from the serve ` +
|
|
100
|
-
`api.manifest()) or an explicit \`config\` entry to createHooks().`);
|
|
21
|
+
const configuredClient = config ? createHypequeryClient(config) : null;
|
|
22
|
+
function useResolvedClient() {
|
|
23
|
+
const providerClient = useOptionalHypequeryClient();
|
|
24
|
+
const client = configuredClient ?? providerClient;
|
|
25
|
+
if (!client) {
|
|
26
|
+
throw new Error('Configure createHooks() directly or render inside <HypequeryProvider>.');
|
|
101
27
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
let finalUrl = url;
|
|
105
|
-
let body;
|
|
106
|
-
if (method === 'GET' && input && typeof input === 'object') {
|
|
107
|
-
const params = new URLSearchParams();
|
|
108
|
-
for (const [key, value] of Object.entries(input)) {
|
|
109
|
-
if (value === undefined || value === null)
|
|
110
|
-
continue;
|
|
111
|
-
if (Array.isArray(value)) {
|
|
112
|
-
value.forEach((item) => params.append(key, String(item)));
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
params.append(key, String(value));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const queryString = params.toString();
|
|
119
|
-
finalUrl = queryString ? `${url}?${queryString}` : url;
|
|
120
|
-
}
|
|
121
|
-
else if (input !== undefined) {
|
|
122
|
-
body = JSON.stringify(input);
|
|
123
|
-
}
|
|
124
|
-
const attempt = async () => {
|
|
125
|
-
const resolvedHeaders = await resolveHeaders(headers);
|
|
126
|
-
return fetchFn(finalUrl, {
|
|
127
|
-
method,
|
|
128
|
-
headers: {
|
|
129
|
-
...resolvedHeaders,
|
|
130
|
-
...(extraHeaders ?? {}),
|
|
131
|
-
...(body ? { 'content-type': 'application/json' } : {}),
|
|
132
|
-
},
|
|
133
|
-
body,
|
|
134
|
-
});
|
|
135
|
-
};
|
|
136
|
-
let res = await attempt();
|
|
137
|
-
// On 401, give the caller a chance to refresh credentials and retry once.
|
|
138
|
-
if (res.status === 401 && onUnauthorized) {
|
|
139
|
-
await onUnauthorized();
|
|
140
|
-
res = await attempt();
|
|
141
|
-
}
|
|
142
|
-
if (!res.ok) {
|
|
143
|
-
const errorBody = await parseResponse(res);
|
|
144
|
-
throw new HttpError(`${method} request to ${finalUrl} failed with status ${res.status}`, res.status, errorBody);
|
|
145
|
-
}
|
|
146
|
-
return res.json();
|
|
147
|
-
};
|
|
28
|
+
return client;
|
|
29
|
+
}
|
|
148
30
|
function useQuery(...args) {
|
|
31
|
+
const client = useResolvedClient();
|
|
149
32
|
const [name, potentialInputOrOptions, maybeOptions] = args;
|
|
150
33
|
let input;
|
|
151
34
|
let options;
|
|
@@ -163,19 +46,20 @@ export function createHooks(config) {
|
|
|
163
46
|
}
|
|
164
47
|
const queryKey = (() => {
|
|
165
48
|
if (input === undefined) {
|
|
166
|
-
return ['hypequery', name];
|
|
49
|
+
return ['hypequery', client.cacheKey, name];
|
|
167
50
|
}
|
|
168
|
-
return ['hypequery', name, input];
|
|
51
|
+
return ['hypequery', client.cacheKey, name, input];
|
|
169
52
|
})();
|
|
170
53
|
return useTanstackQuery({
|
|
171
54
|
queryKey,
|
|
172
|
-
queryFn: () =>
|
|
55
|
+
queryFn: () => client.request(name, input),
|
|
173
56
|
...(options ?? {}),
|
|
174
57
|
});
|
|
175
58
|
}
|
|
176
59
|
function useMutation(name, options) {
|
|
60
|
+
const client = useResolvedClient();
|
|
177
61
|
return useTanstackMutation({
|
|
178
|
-
mutationFn: (input) =>
|
|
62
|
+
mutationFn: (input) => client.request(name, input, 'POST'),
|
|
179
63
|
...options,
|
|
180
64
|
});
|
|
181
65
|
}
|
|
@@ -186,12 +70,13 @@ export function createHooks(config) {
|
|
|
186
70
|
* provided, is the starting offset.
|
|
187
71
|
*/
|
|
188
72
|
function useInfiniteQuery(name, input, options) {
|
|
73
|
+
const client = useResolvedClient();
|
|
189
74
|
const initialOffset = input?.offset ?? 0;
|
|
190
|
-
const queryKey = ['hypequery', name, input];
|
|
75
|
+
const queryKey = ['hypequery', client.cacheKey, name, input];
|
|
191
76
|
return useTanstackInfiniteQuery({
|
|
192
77
|
queryKey,
|
|
193
78
|
initialPageParam: initialOffset,
|
|
194
|
-
queryFn: ({ pageParam }) =>
|
|
79
|
+
queryFn: ({ pageParam }) => client.request(name, { ...input, offset: pageParam }, 'POST', { 'x-include-meta': 'true' }),
|
|
195
80
|
getNextPageParam: (lastPage) => {
|
|
196
81
|
const pagination = lastPage.meta?.pagination;
|
|
197
82
|
if (!pagination || !pagination.hasMore)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export { createHooks, queryOptions } from './createHooks.js';
|
|
2
2
|
export { createAnalyticsHooks } from './analyticsHooks.js';
|
|
3
|
+
export { createHypequeryClient } from './client.js';
|
|
4
|
+
export { HypequeryProvider, useHypequeryClient } from './provider.js';
|
|
3
5
|
export { HttpError } from './errors.js';
|
|
4
6
|
export type { QueryInput, QueryOutput, HttpMethod } from './types.js';
|
|
5
|
-
export type { CreateHooksConfig
|
|
7
|
+
export type { CreateHooksConfig } from './createHooks.js';
|
|
8
|
+
export type { ApiContract, HeadersInput, HypequeryClient, HypequeryClientConfig, QueryMethodConfig, RouteManifest, RouteManifestEntry, TokenInput, } from './client.js';
|
|
9
|
+
export type { HypequeryProviderProps } from './provider.js';
|
|
6
10
|
export type { CreateAnalyticsHooksConfig } from './analyticsHooks.js';
|
|
7
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,YAAY,EACV,WAAW,EACX,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,YAAY,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { createHooks, queryOptions } from './createHooks.js';
|
|
2
2
|
export { createAnalyticsHooks } from './analyticsHooks.js';
|
|
3
|
+
export { createHypequeryClient } from './client.js';
|
|
4
|
+
export { HypequeryProvider, useHypequeryClient } from './provider.js';
|
|
3
5
|
export { HttpError } from './errors.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import type { ApiContract, HypequeryClient } from './client.js';
|
|
4
|
+
export interface HypequeryProviderProps<Api extends ApiContract = ApiContract> {
|
|
5
|
+
client: HypequeryClient<Api>;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
/** Reuse an application's existing TanStack Query client when supplied. */
|
|
8
|
+
queryClient?: QueryClient;
|
|
9
|
+
}
|
|
10
|
+
export declare function HypequeryProvider<Api extends ApiContract>({ client, children, queryClient, }: HypequeryProviderProps<Api>): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function useHypequeryClient<Api extends ApiContract = ApiContract>(): HypequeryClient<Api>;
|
|
12
|
+
export declare function useOptionalHypequeryClient<Api extends ApiContract = ApiContract>(): HypequeryClient<Api> | null;
|
|
13
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAIhE,MAAM,WAAW,sBAAsB,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW;IAC3E,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC;IAC7B,QAAQ,EAAE,SAAS,CAAC;IACpB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,GAAG,SAAS,WAAW,EAAE,EACzD,MAAM,EACN,QAAQ,EACR,WAAW,GACZ,EAAE,sBAAsB,CAAC,GAAG,CAAC,2CAS7B;AAED,wBAAgB,kBAAkB,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW,0BAMvE;AAED,wBAAgB,0BAA0B,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW,KACvC,eAAe,CAAC,GAAG,CAAC,GAAG,IAAI,CACnE"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
3
|
+
import { createContext, useContext, useState } from 'react';
|
|
4
|
+
const HypequeryContext = createContext(null);
|
|
5
|
+
export function HypequeryProvider({ client, children, queryClient, }) {
|
|
6
|
+
const [defaultQueryClient] = useState(() => new QueryClient());
|
|
7
|
+
return (_jsx(HypequeryContext.Provider, { value: client, children: _jsx(QueryClientProvider, { client: queryClient ?? defaultQueryClient, children: children }) }));
|
|
8
|
+
}
|
|
9
|
+
export function useHypequeryClient() {
|
|
10
|
+
const client = useOptionalHypequeryClient();
|
|
11
|
+
if (!client) {
|
|
12
|
+
throw new Error('Hypequery hooks must be rendered inside <HypequeryProvider>.');
|
|
13
|
+
}
|
|
14
|
+
return client;
|
|
15
|
+
}
|
|
16
|
+
export function useOptionalHypequeryClient() {
|
|
17
|
+
return useContext(HypequeryContext);
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.test.d.ts","sourceRoot":"","sources":["../src/provider.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { renderHook, waitFor } from '@testing-library/react';
|
|
3
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { createHypequeryClient } from './client.js';
|
|
6
|
+
import { createHooks } from './createHooks.js';
|
|
7
|
+
import { HypequeryProvider } from './provider.js';
|
|
8
|
+
function success(data) {
|
|
9
|
+
return { ok: true, status: 200, json: async () => data };
|
|
10
|
+
}
|
|
11
|
+
describe('HypequeryProvider', () => {
|
|
12
|
+
it('configures generated hooks once and supplies their TanStack Query client', async () => {
|
|
13
|
+
const fetchFn = vi.fn().mockResolvedValue(success({ message: 'Hello Luke' }));
|
|
14
|
+
const client = createHypequeryClient({
|
|
15
|
+
baseUrl: 'https://acme.hypequery.cloud/v1/analytics/production',
|
|
16
|
+
token: async () => 'browser-token',
|
|
17
|
+
fetchFn: fetchFn,
|
|
18
|
+
manifest: { greeting: { method: 'GET', path: 'queries/greeting' } },
|
|
19
|
+
});
|
|
20
|
+
const { useQuery } = createHooks();
|
|
21
|
+
const wrapper = ({ children }) => (_jsx(HypequeryProvider, { client: client, children: children }));
|
|
22
|
+
const { result } = renderHook(() => useQuery('greeting', { name: 'Luke' }), { wrapper });
|
|
23
|
+
await waitFor(() => expect(result.current.data).toEqual({ message: 'Hello Luke' }));
|
|
24
|
+
expect(fetchFn).toHaveBeenCalledWith('https://acme.hypequery.cloud/v1/analytics/production/queries/greeting?name=Luke', expect.objectContaining({
|
|
25
|
+
headers: expect.objectContaining({ authorization: 'Bearer browser-token' }),
|
|
26
|
+
}));
|
|
27
|
+
});
|
|
28
|
+
it('isolates cached responses when the provider client changes', async () => {
|
|
29
|
+
const firstFetch = vi.fn().mockResolvedValue(success({ message: 'First user' }));
|
|
30
|
+
const secondFetch = vi.fn().mockResolvedValue(success({ message: 'Second user' }));
|
|
31
|
+
const createClient = (fetchFn) => createHypequeryClient({
|
|
32
|
+
baseUrl: 'https://acme.hypequery.cloud/v1/analytics/production',
|
|
33
|
+
fetchFn: fetchFn,
|
|
34
|
+
manifest: { greeting: { method: 'GET', path: 'queries/greeting' } },
|
|
35
|
+
});
|
|
36
|
+
const firstClient = createClient(firstFetch);
|
|
37
|
+
const secondClient = createClient(secondFetch);
|
|
38
|
+
const queryClient = new QueryClient({
|
|
39
|
+
defaultOptions: { queries: { retry: false } },
|
|
40
|
+
});
|
|
41
|
+
const { useQuery } = createHooks();
|
|
42
|
+
let activeClient = firstClient;
|
|
43
|
+
const wrapper = ({ children }) => (_jsx(HypequeryProvider, { client: activeClient, queryClient: queryClient, children: children }));
|
|
44
|
+
const view = renderHook(() => useQuery('greeting', { name: 'Luke' }), { wrapper });
|
|
45
|
+
await waitFor(() => expect(view.result.current.data).toEqual({ message: 'First user' }));
|
|
46
|
+
activeClient = secondClient;
|
|
47
|
+
view.rerender();
|
|
48
|
+
await waitFor(() => expect(view.result.current.data).toEqual({ message: 'Second user' }));
|
|
49
|
+
expect(firstClient.cacheKey).not.toBe(secondClient.cacheKey);
|
|
50
|
+
expect(firstFetch).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(secondFetch).toHaveBeenCalledTimes(1);
|
|
52
|
+
});
|
|
53
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ type DatasetOutputForInput<TInfo, TInput, TFallback> = TInfo extends {
|
|
|
42
42
|
} ? WithTypedData<TFallback, {
|
|
43
43
|
[K in SelectedDimensions<TDimensions, TInput>]?: DimensionValue<TDimensions[K]>;
|
|
44
44
|
} & {
|
|
45
|
-
[K in SelectedMeasures<TMeasures, TInput>]?: string;
|
|
45
|
+
[K in SelectedMeasures<TMeasures, TInput>]?: string | null;
|
|
46
46
|
} & PeriodSelection<TInput>> : TFallback;
|
|
47
47
|
type MetricOutputForInput<TInfo, TInput, TFallback> = TInfo extends {
|
|
48
48
|
kind: 'metric';
|
|
@@ -51,7 +51,7 @@ type MetricOutputForInput<TInfo, TInput, TFallback> = TInfo extends {
|
|
|
51
51
|
} ? WithTypedData<TFallback, {
|
|
52
52
|
[K in SelectedDimensions<TDimensions, TInput>]?: DimensionValue<TDimensions[K]>;
|
|
53
53
|
} & {
|
|
54
|
-
[K in TMetricName]?: string;
|
|
54
|
+
[K in TMetricName]?: string | null;
|
|
55
55
|
} & PeriodSelection<TInput>> : TFallback;
|
|
56
56
|
export type QueryOutputForInput<Api, Name extends ExtractNames<Api>, TInput> = SemanticInfo<Api, Name> extends infer Info ? Info extends {
|
|
57
57
|
kind: 'dataset';
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Type-safe React and TanStack Query hooks for ClickHouse analytics, metrics, and datasets",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"react-dom": "^18.2.0",
|
|
43
43
|
"typescript": "^5.7.3",
|
|
44
44
|
"vitest": "^3.2.6",
|
|
45
|
-
"@hypequery/
|
|
46
|
-
"@hypequery/
|
|
45
|
+
"@hypequery/datasets": "0.13.6",
|
|
46
|
+
"@hypequery/serve": "0.16.2"
|
|
47
47
|
},
|
|
48
48
|
"repository": {
|
|
49
49
|
"type": "git",
|