routesync 1.0.20 → 1.0.22
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/dist/cli.js +34 -15
- package/dist/react.d.mts +62 -4
- package/dist/react.d.ts +62 -4
- package/dist/react.js +94 -2
- package/dist/react.mjs +90 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8461,9 +8461,9 @@ foreach ($routes as $route) {
|
|
|
8461
8461
|
$resourceName = null;
|
|
8462
8462
|
$collection = false;
|
|
8463
8463
|
|
|
8464
|
-
if (preg_match('/
|
|
8464
|
+
if (preg_match('/return\\s+new\\s+([a-zA-Z0-9_]+Resource)/', $methodSource, $matches)) {
|
|
8465
8465
|
$resourceName = $matches[1];
|
|
8466
|
-
} elseif (preg_match('/
|
|
8466
|
+
} elseif (preg_match('/return\\s+([a-zA-Z0-9_]+Resource)::collection/', $methodSource, $matches)) {
|
|
8467
8467
|
$resourceName = $matches[1];
|
|
8468
8468
|
$collection = true;
|
|
8469
8469
|
}
|
|
@@ -8489,7 +8489,7 @@ foreach ($routes as $route) {
|
|
|
8489
8489
|
|
|
8490
8490
|
if (!$responseMetadata) {
|
|
8491
8491
|
$docComment = $resReflector->getDocComment();
|
|
8492
|
-
if ($docComment && preg_match('/@
|
|
8492
|
+
if ($docComment && preg_match('/@mixin\\s+([\\\\\\\\a-zA-Z0-9_]+)/', $docComment, $mixinMatches)) {
|
|
8493
8493
|
$responseMetadata = [
|
|
8494
8494
|
'type' => class_basename($mixinMatches[1]),
|
|
8495
8495
|
'collection' => $collection
|
|
@@ -8755,6 +8755,7 @@ var SDKGenerator = class {
|
|
|
8755
8755
|
if (usesZod) {
|
|
8756
8756
|
lines.push(`import { z } from 'zod'`);
|
|
8757
8757
|
lines.push(`import * as Schemas from './schemas'`);
|
|
8758
|
+
lines.push(`export * as Schemas from './schemas'`);
|
|
8758
8759
|
}
|
|
8759
8760
|
if (usesTypes) {
|
|
8760
8761
|
lines.push(`import * as Types from './types'`);
|
|
@@ -8766,7 +8767,7 @@ var SDKGenerator = class {
|
|
|
8766
8767
|
const TitleCaseAction = route.actionName.charAt(0).toUpperCase() + route.actionName.slice(1);
|
|
8767
8768
|
const ContractName = `${TitleCaseGroup}${TitleCaseAction}Contract`;
|
|
8768
8769
|
const pathParams = Array.from(route.runtimePath.matchAll(/:([a-zA-Z0-9_]+)/g)).map((m) => m[1]);
|
|
8769
|
-
const paramsType = pathParams.length > 0 ? `{ ${pathParams.map((p) => `${p}: string`).join(", ")} }` : `
|
|
8770
|
+
const paramsType = pathParams.length > 0 ? `{ ${pathParams.map((p) => `${p}: string`).join(", ")} }` : `unknown`;
|
|
8770
8771
|
const methodActionName = TitleCaseGroup + TitleCaseAction;
|
|
8771
8772
|
const bodyType = route.schema?.rules && usesZod ? `z.infer<typeof Schemas.${methodActionName}Schema>` : `unknown`;
|
|
8772
8773
|
const responseType = route.response ? `Types.${route.response.type}${route.response.collection ? "[]" : ""}` : `unknown`;
|
|
@@ -8797,7 +8798,7 @@ var SDKGenerator = class {
|
|
|
8797
8798
|
lines.push(` body: Schemas.${ContractName.replace("Contract", "Schema")}`);
|
|
8798
8799
|
lines.push(` },`);
|
|
8799
8800
|
}
|
|
8800
|
-
if (options.zod && route.response) {
|
|
8801
|
+
if (options.zod && route.response && options.models) {
|
|
8801
8802
|
lines.push(` responseSchema: Schemas.${route.response.type}Schema${route.response.collection ? ".array()" : ""},`);
|
|
8802
8803
|
}
|
|
8803
8804
|
lines.push(` }),`);
|
|
@@ -9041,7 +9042,7 @@ var HookGenerator = class _HookGenerator {
|
|
|
9041
9042
|
const lines = [];
|
|
9042
9043
|
lines.push(`// Auto-generated by routesync. Do not edit manually.`);
|
|
9043
9044
|
lines.push(``);
|
|
9044
|
-
lines.push(`import { useApiQuery, useApiMutation } from '
|
|
9045
|
+
lines.push(`import { useApiQuery, useApiMutation } from 'routesync/react'`);
|
|
9045
9046
|
lines.push(`import { api } from './api'`);
|
|
9046
9047
|
lines.push(``);
|
|
9047
9048
|
const grouped = buildGeneratedRoutes(manifest.routes);
|
|
@@ -9053,7 +9054,8 @@ var HookGenerator = class _HookGenerator {
|
|
|
9053
9054
|
const queryKey = `['${group}', '${route.actionName}']`;
|
|
9054
9055
|
if (method === "GET") {
|
|
9055
9056
|
lines.push(`/**`);
|
|
9056
|
-
lines.push(` * @deprecated
|
|
9057
|
+
lines.push(` * @deprecated Generated hooks will become optional in v2.`);
|
|
9058
|
+
lines.push(` * Prefer \`useApiQuery(api.${group}.${route.actionName}, ...args)\` for future compatibility.`);
|
|
9057
9059
|
lines.push(` */`);
|
|
9058
9060
|
lines.push(`export function ${hookName}(...args: Parameters<typeof api.${group}.${route.actionName}>) {`);
|
|
9059
9061
|
lines.push(` return useApiQuery(api.${group}.${route.actionName}, ...args as any)`);
|
|
@@ -9061,7 +9063,8 @@ var HookGenerator = class _HookGenerator {
|
|
|
9061
9063
|
lines.push(``);
|
|
9062
9064
|
} else {
|
|
9063
9065
|
lines.push(`/**`);
|
|
9064
|
-
lines.push(` * @deprecated
|
|
9066
|
+
lines.push(` * @deprecated Generated hooks will become optional in v2.`);
|
|
9067
|
+
lines.push(` * Prefer \`useApiMutation(api.${group}.${route.actionName}, options)\` for future compatibility.`);
|
|
9065
9068
|
lines.push(` */`);
|
|
9066
9069
|
lines.push(`export function ${hookName}() {`);
|
|
9067
9070
|
lines.push(` return useApiMutation(api.${group}.${route.actionName})`);
|
|
@@ -9100,23 +9103,35 @@ var NextActionGenerator = class {
|
|
|
9100
9103
|
for (const [groupName, routes] of Object.entries(grouped)) {
|
|
9101
9104
|
for (const route of routes) {
|
|
9102
9105
|
const actionName = `${groupName}${toTypeName(route.actionName)}`;
|
|
9103
|
-
|
|
9106
|
+
const TitleCaseGroup = groupName.charAt(0).toUpperCase() + groupName.slice(1);
|
|
9107
|
+
const TitleCaseAction = route.actionName.charAt(0).toUpperCase() + route.actionName.slice(1);
|
|
9108
|
+
const ContractName = `${TitleCaseGroup}${TitleCaseAction}Contract`;
|
|
9109
|
+
const hasBody = route.schema && route.schema.rules && Object.keys(route.schema.rules).length > 0;
|
|
9110
|
+
const hasQuery = route.method === "GET";
|
|
9111
|
+
const hasParams = route.path.includes(":");
|
|
9112
|
+
let payloadType = "never";
|
|
9113
|
+
if (hasBody) payloadType = `${ContractName}['request']['body']`;
|
|
9114
|
+
else if (hasQuery) payloadType = `${ContractName}['request']['query']`;
|
|
9115
|
+
else if (hasParams) payloadType = `${ContractName}['request']['params']`;
|
|
9116
|
+
const payloadParam = hasBody || hasQuery || hasParams ? `payload: ${payloadType}` : `payload?: unknown`;
|
|
9117
|
+
lines.push(`export async function ${actionName}Action(${payloadParam}) {`);
|
|
9104
9118
|
const args = [];
|
|
9105
|
-
if (
|
|
9106
|
-
args.push(`params: payload
|
|
9119
|
+
if (hasParams) {
|
|
9120
|
+
args.push(`params: payload`);
|
|
9107
9121
|
}
|
|
9108
|
-
if (
|
|
9122
|
+
if (hasQuery) {
|
|
9109
9123
|
args.push(`query: payload`);
|
|
9110
|
-
} else {
|
|
9124
|
+
} else if (hasBody) {
|
|
9111
9125
|
args.push(`body: payload`);
|
|
9112
9126
|
}
|
|
9113
9127
|
if (route.auth) {
|
|
9114
9128
|
args.push(`headers: await getAuthHeaders()`);
|
|
9115
9129
|
}
|
|
9116
|
-
const
|
|
9130
|
+
const argsString = args.length > 0 ? `{ ${args.join(", ")} }` : "";
|
|
9131
|
+
const apiCall = `await api.${groupName}.${route.actionName}(${argsString})`;
|
|
9117
9132
|
lines.push(` try {`);
|
|
9118
9133
|
lines.push(` const response = ${apiCall}`);
|
|
9119
|
-
lines.push(` return { success: true, data: response
|
|
9134
|
+
lines.push(` return { success: true, data: response }`);
|
|
9120
9135
|
lines.push(` } catch (error: unknown) {`);
|
|
9121
9136
|
lines.push(` return { success: false, error: error instanceof Error ? error.message : String(error) }`);
|
|
9122
9137
|
lines.push(` }`);
|
|
@@ -9124,6 +9139,10 @@ var NextActionGenerator = class {
|
|
|
9124
9139
|
lines.push(``);
|
|
9125
9140
|
}
|
|
9126
9141
|
}
|
|
9142
|
+
lines.splice(3, 1, `import { api, type ${Object.values(grouped).flatMap((routes) => routes.map((r) => {
|
|
9143
|
+
const g = r.groupName || Object.keys(grouped).find((k) => grouped[k] === routes) || "";
|
|
9144
|
+
return g.charAt(0).toUpperCase() + g.slice(1) + r.actionName.charAt(0).toUpperCase() + r.actionName.slice(1) + "Contract";
|
|
9145
|
+
})).join(", type ")} } from './api'`);
|
|
9127
9146
|
await import_fs_extra7.default.writeFile(import_path6.default.join(outputDir, "actions.ts"), lines.join("\n"));
|
|
9128
9147
|
}
|
|
9129
9148
|
};
|
package/dist/react.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
|
2
|
+
import { UseQueryOptions, InfiniteData, UseInfiniteQueryOptions, UseSuspenseQueryOptions, UseMutationOptions, QueryClient } from '@tanstack/react-query';
|
|
3
3
|
|
|
4
4
|
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
5
5
|
type RouteTransform = (value: unknown) => unknown;
|
|
@@ -80,6 +80,20 @@ declare function useApiQuery<TResponse = unknown, TParams = unknown, TBody = unk
|
|
|
80
80
|
...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
|
|
81
81
|
queryOptions?: Omit<UseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>
|
|
82
82
|
]): _tanstack_react_query.UseQueryResult<NoInfer<TData>, TError>;
|
|
83
|
+
/**
|
|
84
|
+
* useApiSuspenseQuery — generic wrapper for Suspense-enabled data fetching (TanStack v5).
|
|
85
|
+
*/
|
|
86
|
+
declare function useApiSuspenseQuery<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TData = TResponse>(endpoint: EndpointCallable<TResponse, TParams, TBody>, ...args: [
|
|
87
|
+
...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
|
|
88
|
+
queryOptions?: Omit<UseSuspenseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>
|
|
89
|
+
]): _tanstack_react_query.UseSuspenseQueryResult<TData, TError>;
|
|
90
|
+
/**
|
|
91
|
+
* useApiInfiniteQuery — framework agnostic paginated fetching.
|
|
92
|
+
*/
|
|
93
|
+
declare function useApiInfiniteQuery<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TData = InfiniteData<TResponse>, TPageParam = unknown>(endpoint: EndpointCallable<TResponse, TParams, TBody>, ...args: [
|
|
94
|
+
...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
|
|
95
|
+
queryOptions: Omit<UseInfiniteQueryOptions<TResponse, TError, TData, any, TPageParam>, 'queryKey' | 'queryFn'>
|
|
96
|
+
]): _tanstack_react_query.UseInfiniteQueryResult<TData, TError>;
|
|
83
97
|
|
|
84
98
|
/**
|
|
85
99
|
* useApiMutation — accepts an endpoint callable directly.
|
|
@@ -94,11 +108,40 @@ declare function useApiQuery<TResponse = unknown, TParams = unknown, TBody = unk
|
|
|
94
108
|
* invalidate: [api.cart.list, api.orders.index],
|
|
95
109
|
* })
|
|
96
110
|
*/
|
|
97
|
-
interface ApiMutationOptions<TData, TError, TVariables> extends Omit<UseMutationOptions<TData, TError, TVariables>, 'mutationFn'> {
|
|
111
|
+
interface ApiMutationOptions<TData, TError, TVariables, TContext = unknown> extends Omit<UseMutationOptions<TData, TError, TVariables, TContext>, 'mutationFn'> {
|
|
98
112
|
/** Extra endpoints to invalidate on success (in addition to the auto group invalidation). */
|
|
99
113
|
invalidate?: EndpointCallable<any, any, any>[];
|
|
100
114
|
}
|
|
101
|
-
declare function useApiMutation<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: ApiMutationOptions<TResponse, TError, EndpointCallableOptions<TParams, TBody
|
|
115
|
+
declare function useApiMutation<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TContext = unknown>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: ApiMutationOptions<TResponse, TError, EndpointCallableOptions<TParams, TBody>, TContext>): _tanstack_react_query.UseMutationResult<TResponse, TError, EndpointCallableOptions<TParams, TBody>, TContext>;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* useApiQueryClient — a typed wrapper over TanStack's QueryClient
|
|
119
|
+
*
|
|
120
|
+
* Provides ergonomic helpers like invalidateEndpoint and prefetchEndpoint
|
|
121
|
+
* with strong typing based on the RouteSync SDK contracts.
|
|
122
|
+
*/
|
|
123
|
+
declare function useApiQueryClient(): {
|
|
124
|
+
queryClient: QueryClient;
|
|
125
|
+
/**
|
|
126
|
+
* Invalidate specific queries related to an endpoint.
|
|
127
|
+
* Optionally pass parameters to invalidate a more specific cache entry.
|
|
128
|
+
*/
|
|
129
|
+
invalidateEndpoint: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: EndpointCallableOptions<TParams, TBody>) => Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Prefetch data for an endpoint into the cache.
|
|
132
|
+
* Useful for SSR, RSC hydration, or proactive prefetching.
|
|
133
|
+
*/
|
|
134
|
+
prefetchEndpoint: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: EndpointCallableOptions<TParams, TBody>) => Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Manually update the cache for a specific endpoint.
|
|
137
|
+
* Often used for optimistic updates in mutations.
|
|
138
|
+
*/
|
|
139
|
+
setEndpointData: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options: EndpointCallableOptions<TParams, TBody> | undefined, updater: TResponse | ((oldData: TResponse | undefined) => TResponse)) => unknown;
|
|
140
|
+
/**
|
|
141
|
+
* Retrieve the current cached data for a specific endpoint.
|
|
142
|
+
*/
|
|
143
|
+
getEndpointData: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: EndpointCallableOptions<TParams, TBody>) => TResponse | undefined;
|
|
144
|
+
};
|
|
102
145
|
|
|
103
146
|
/**
|
|
104
147
|
* createHooks — generate typed hooks from an api group.
|
|
@@ -113,4 +156,19 @@ declare function useApiMutation<TResponse = unknown, TParams = unknown, TBody =
|
|
|
113
156
|
*/
|
|
114
157
|
declare function createHooks<T extends Record<string, EndpointCallable>>(group: T): { [K in keyof T as `use${Capitalize<string & K>}`]: T[K] extends EndpointCallable ? T[K]["$def"]["method"] extends "GET" | "DELETE" ? (options?: any, queryOptions?: any) => ReturnType<typeof useApiQuery> : (options?: any) => ReturnType<typeof useApiMutation> : never; };
|
|
115
158
|
|
|
116
|
-
|
|
159
|
+
interface UseFormSetError<TFieldValues extends Record<string, any>> {
|
|
160
|
+
(name: keyof TFieldValues | string, error: {
|
|
161
|
+
type: string;
|
|
162
|
+
message?: string;
|
|
163
|
+
}): void;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Parses an unknown error and safely pipes 422 Unprocessable Entity
|
|
167
|
+
* validation messages to react-hook-form's setError.
|
|
168
|
+
*
|
|
169
|
+
* @param error - The error thrown by a RouteSync mutation
|
|
170
|
+
* @param setError - The `setError` function destructured from `useForm()`
|
|
171
|
+
*/
|
|
172
|
+
declare function setFormErrors<TFieldValues extends Record<string, any>>(error: unknown, setError: UseFormSetError<TFieldValues>): void;
|
|
173
|
+
|
|
174
|
+
export { type ApiMutationOptions, createHooks, setFormErrors, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiQueryClient, useApiSuspenseQuery };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
|
-
import { UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
|
2
|
+
import { UseQueryOptions, InfiniteData, UseInfiniteQueryOptions, UseSuspenseQueryOptions, UseMutationOptions, QueryClient } from '@tanstack/react-query';
|
|
3
3
|
|
|
4
4
|
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
5
5
|
type RouteTransform = (value: unknown) => unknown;
|
|
@@ -80,6 +80,20 @@ declare function useApiQuery<TResponse = unknown, TParams = unknown, TBody = unk
|
|
|
80
80
|
...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
|
|
81
81
|
queryOptions?: Omit<UseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>
|
|
82
82
|
]): _tanstack_react_query.UseQueryResult<NoInfer<TData>, TError>;
|
|
83
|
+
/**
|
|
84
|
+
* useApiSuspenseQuery — generic wrapper for Suspense-enabled data fetching (TanStack v5).
|
|
85
|
+
*/
|
|
86
|
+
declare function useApiSuspenseQuery<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TData = TResponse>(endpoint: EndpointCallable<TResponse, TParams, TBody>, ...args: [
|
|
87
|
+
...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
|
|
88
|
+
queryOptions?: Omit<UseSuspenseQueryOptions<TResponse, TError, TData>, 'queryKey' | 'queryFn'>
|
|
89
|
+
]): _tanstack_react_query.UseSuspenseQueryResult<TData, TError>;
|
|
90
|
+
/**
|
|
91
|
+
* useApiInfiniteQuery — framework agnostic paginated fetching.
|
|
92
|
+
*/
|
|
93
|
+
declare function useApiInfiniteQuery<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TData = InfiniteData<TResponse>, TPageParam = unknown>(endpoint: EndpointCallable<TResponse, TParams, TBody>, ...args: [
|
|
94
|
+
...OptionalIfEmpty<EndpointCallableOptions<TParams, TBody>>,
|
|
95
|
+
queryOptions: Omit<UseInfiniteQueryOptions<TResponse, TError, TData, any, TPageParam>, 'queryKey' | 'queryFn'>
|
|
96
|
+
]): _tanstack_react_query.UseInfiniteQueryResult<TData, TError>;
|
|
83
97
|
|
|
84
98
|
/**
|
|
85
99
|
* useApiMutation — accepts an endpoint callable directly.
|
|
@@ -94,11 +108,40 @@ declare function useApiQuery<TResponse = unknown, TParams = unknown, TBody = unk
|
|
|
94
108
|
* invalidate: [api.cart.list, api.orders.index],
|
|
95
109
|
* })
|
|
96
110
|
*/
|
|
97
|
-
interface ApiMutationOptions<TData, TError, TVariables> extends Omit<UseMutationOptions<TData, TError, TVariables>, 'mutationFn'> {
|
|
111
|
+
interface ApiMutationOptions<TData, TError, TVariables, TContext = unknown> extends Omit<UseMutationOptions<TData, TError, TVariables, TContext>, 'mutationFn'> {
|
|
98
112
|
/** Extra endpoints to invalidate on success (in addition to the auto group invalidation). */
|
|
99
113
|
invalidate?: EndpointCallable<any, any, any>[];
|
|
100
114
|
}
|
|
101
|
-
declare function useApiMutation<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: ApiMutationOptions<TResponse, TError, EndpointCallableOptions<TParams, TBody
|
|
115
|
+
declare function useApiMutation<TResponse = unknown, TParams = unknown, TBody = unknown, TError = ApiError, TContext = unknown>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: ApiMutationOptions<TResponse, TError, EndpointCallableOptions<TParams, TBody>, TContext>): _tanstack_react_query.UseMutationResult<TResponse, TError, EndpointCallableOptions<TParams, TBody>, TContext>;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* useApiQueryClient — a typed wrapper over TanStack's QueryClient
|
|
119
|
+
*
|
|
120
|
+
* Provides ergonomic helpers like invalidateEndpoint and prefetchEndpoint
|
|
121
|
+
* with strong typing based on the RouteSync SDK contracts.
|
|
122
|
+
*/
|
|
123
|
+
declare function useApiQueryClient(): {
|
|
124
|
+
queryClient: QueryClient;
|
|
125
|
+
/**
|
|
126
|
+
* Invalidate specific queries related to an endpoint.
|
|
127
|
+
* Optionally pass parameters to invalidate a more specific cache entry.
|
|
128
|
+
*/
|
|
129
|
+
invalidateEndpoint: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: EndpointCallableOptions<TParams, TBody>) => Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Prefetch data for an endpoint into the cache.
|
|
132
|
+
* Useful for SSR, RSC hydration, or proactive prefetching.
|
|
133
|
+
*/
|
|
134
|
+
prefetchEndpoint: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: EndpointCallableOptions<TParams, TBody>) => Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Manually update the cache for a specific endpoint.
|
|
137
|
+
* Often used for optimistic updates in mutations.
|
|
138
|
+
*/
|
|
139
|
+
setEndpointData: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options: EndpointCallableOptions<TParams, TBody> | undefined, updater: TResponse | ((oldData: TResponse | undefined) => TResponse)) => unknown;
|
|
140
|
+
/**
|
|
141
|
+
* Retrieve the current cached data for a specific endpoint.
|
|
142
|
+
*/
|
|
143
|
+
getEndpointData: <TResponse, TParams, TBody>(endpoint: EndpointCallable<TResponse, TParams, TBody>, options?: EndpointCallableOptions<TParams, TBody>) => TResponse | undefined;
|
|
144
|
+
};
|
|
102
145
|
|
|
103
146
|
/**
|
|
104
147
|
* createHooks — generate typed hooks from an api group.
|
|
@@ -113,4 +156,19 @@ declare function useApiMutation<TResponse = unknown, TParams = unknown, TBody =
|
|
|
113
156
|
*/
|
|
114
157
|
declare function createHooks<T extends Record<string, EndpointCallable>>(group: T): { [K in keyof T as `use${Capitalize<string & K>}`]: T[K] extends EndpointCallable ? T[K]["$def"]["method"] extends "GET" | "DELETE" ? (options?: any, queryOptions?: any) => ReturnType<typeof useApiQuery> : (options?: any) => ReturnType<typeof useApiMutation> : never; };
|
|
115
158
|
|
|
116
|
-
|
|
159
|
+
interface UseFormSetError<TFieldValues extends Record<string, any>> {
|
|
160
|
+
(name: keyof TFieldValues | string, error: {
|
|
161
|
+
type: string;
|
|
162
|
+
message?: string;
|
|
163
|
+
}): void;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Parses an unknown error and safely pipes 422 Unprocessable Entity
|
|
167
|
+
* validation messages to react-hook-form's setError.
|
|
168
|
+
*
|
|
169
|
+
* @param error - The error thrown by a RouteSync mutation
|
|
170
|
+
* @param setError - The `setError` function destructured from `useForm()`
|
|
171
|
+
*/
|
|
172
|
+
declare function setFormErrors<TFieldValues extends Record<string, any>>(error: unknown, setError: UseFormSetError<TFieldValues>): void;
|
|
173
|
+
|
|
174
|
+
export { type ApiMutationOptions, createHooks, setFormErrors, useApiInfiniteQuery, useApiMutation, useApiQuery, useApiQueryClient, useApiSuspenseQuery };
|
package/dist/react.js
CHANGED
|
@@ -21,8 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
createHooks: () => createHooks,
|
|
24
|
+
setFormErrors: () => setFormErrors,
|
|
25
|
+
useApiInfiniteQuery: () => useApiInfiniteQuery,
|
|
24
26
|
useApiMutation: () => useApiMutation,
|
|
25
|
-
useApiQuery: () => useApiQuery
|
|
27
|
+
useApiQuery: () => useApiQuery,
|
|
28
|
+
useApiQueryClient: () => useApiQueryClient,
|
|
29
|
+
useApiSuspenseQuery: () => useApiSuspenseQuery
|
|
26
30
|
});
|
|
27
31
|
module.exports = __toCommonJS(src_exports);
|
|
28
32
|
|
|
@@ -38,6 +42,32 @@ function useApiQuery(endpoint, ...args) {
|
|
|
38
42
|
...queryOptions
|
|
39
43
|
});
|
|
40
44
|
}
|
|
45
|
+
function useApiSuspenseQuery(endpoint, ...args) {
|
|
46
|
+
const options = args[0];
|
|
47
|
+
const queryOptions = args[1];
|
|
48
|
+
const queryKey = endpoint.$queryKey(options);
|
|
49
|
+
return (0, import_react_query.useSuspenseQuery)({
|
|
50
|
+
queryKey,
|
|
51
|
+
queryFn: () => endpoint(options),
|
|
52
|
+
...queryOptions
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function useApiInfiniteQuery(endpoint, ...args) {
|
|
56
|
+
const options = args[0];
|
|
57
|
+
const queryOptions = args.length > 1 ? args[1] : args[0];
|
|
58
|
+
const queryKey = endpoint.$queryKey(options);
|
|
59
|
+
return (0, import_react_query.useInfiniteQuery)({
|
|
60
|
+
queryKey,
|
|
61
|
+
queryFn: ({ pageParam }) => {
|
|
62
|
+
const fetchOptions = { ...options };
|
|
63
|
+
if (pageParam !== void 0) {
|
|
64
|
+
fetchOptions.query = { ...fetchOptions.query || {}, page: pageParam };
|
|
65
|
+
}
|
|
66
|
+
return endpoint(fetchOptions);
|
|
67
|
+
},
|
|
68
|
+
...queryOptions
|
|
69
|
+
});
|
|
70
|
+
}
|
|
41
71
|
|
|
42
72
|
// packages/react/src/hooks/useMutation.ts
|
|
43
73
|
var import_react_query2 = require("@tanstack/react-query");
|
|
@@ -57,6 +87,47 @@ function useApiMutation(endpoint, options) {
|
|
|
57
87
|
});
|
|
58
88
|
}
|
|
59
89
|
|
|
90
|
+
// packages/react/src/hooks/useQueryClient.ts
|
|
91
|
+
var import_react_query3 = require("@tanstack/react-query");
|
|
92
|
+
function useApiQueryClient() {
|
|
93
|
+
const queryClient = (0, import_react_query3.useQueryClient)();
|
|
94
|
+
return {
|
|
95
|
+
queryClient,
|
|
96
|
+
/**
|
|
97
|
+
* Invalidate specific queries related to an endpoint.
|
|
98
|
+
* Optionally pass parameters to invalidate a more specific cache entry.
|
|
99
|
+
*/
|
|
100
|
+
invalidateEndpoint: (endpoint, options) => {
|
|
101
|
+
return queryClient.invalidateQueries({
|
|
102
|
+
queryKey: endpoint.$queryKey(options)
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* Prefetch data for an endpoint into the cache.
|
|
107
|
+
* Useful for SSR, RSC hydration, or proactive prefetching.
|
|
108
|
+
*/
|
|
109
|
+
prefetchEndpoint: (endpoint, options) => {
|
|
110
|
+
return queryClient.prefetchQuery({
|
|
111
|
+
queryKey: endpoint.$queryKey(options),
|
|
112
|
+
queryFn: () => endpoint(options)
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* Manually update the cache for a specific endpoint.
|
|
117
|
+
* Often used for optimistic updates in mutations.
|
|
118
|
+
*/
|
|
119
|
+
setEndpointData: (endpoint, options, updater) => {
|
|
120
|
+
return queryClient.setQueryData(endpoint.$queryKey(options), updater);
|
|
121
|
+
},
|
|
122
|
+
/**
|
|
123
|
+
* Retrieve the current cached data for a specific endpoint.
|
|
124
|
+
*/
|
|
125
|
+
getEndpointData: (endpoint, options) => {
|
|
126
|
+
return queryClient.getQueryData(endpoint.$queryKey(options));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
60
131
|
// packages/react/src/hooks/createHooks.ts
|
|
61
132
|
function createHooks(group) {
|
|
62
133
|
const hooks = {};
|
|
@@ -71,9 +142,30 @@ function createHooks(group) {
|
|
|
71
142
|
}
|
|
72
143
|
return hooks;
|
|
73
144
|
}
|
|
145
|
+
|
|
146
|
+
// packages/react/src/utils/form.ts
|
|
147
|
+
function setFormErrors(error, setError) {
|
|
148
|
+
if (error && typeof error === "object" && "status" in error && error.status === 422) {
|
|
149
|
+
const apiError = error;
|
|
150
|
+
const errors = apiError.errors;
|
|
151
|
+
if (errors && typeof errors === "object") {
|
|
152
|
+
for (const [field, messages] of Object.entries(errors)) {
|
|
153
|
+
if (Array.isArray(messages) && messages.length > 0) {
|
|
154
|
+
setError(field, { type: "server", message: String(messages[0]) });
|
|
155
|
+
} else if (typeof messages === "string") {
|
|
156
|
+
setError(field, { type: "server", message: messages });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
74
162
|
// Annotate the CommonJS export names for ESM import in node:
|
|
75
163
|
0 && (module.exports = {
|
|
76
164
|
createHooks,
|
|
165
|
+
setFormErrors,
|
|
166
|
+
useApiInfiniteQuery,
|
|
77
167
|
useApiMutation,
|
|
78
|
-
useApiQuery
|
|
168
|
+
useApiQuery,
|
|
169
|
+
useApiQueryClient,
|
|
170
|
+
useApiSuspenseQuery
|
|
79
171
|
});
|
package/dist/react.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// packages/react/src/hooks/useQuery.ts
|
|
2
|
-
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { useQuery, useSuspenseQuery, useInfiniteQuery } from "@tanstack/react-query";
|
|
3
3
|
function useApiQuery(endpoint, ...args) {
|
|
4
4
|
const options = args[0];
|
|
5
5
|
const queryOptions = args[1];
|
|
@@ -10,6 +10,32 @@ function useApiQuery(endpoint, ...args) {
|
|
|
10
10
|
...queryOptions
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
+
function useApiSuspenseQuery(endpoint, ...args) {
|
|
14
|
+
const options = args[0];
|
|
15
|
+
const queryOptions = args[1];
|
|
16
|
+
const queryKey = endpoint.$queryKey(options);
|
|
17
|
+
return useSuspenseQuery({
|
|
18
|
+
queryKey,
|
|
19
|
+
queryFn: () => endpoint(options),
|
|
20
|
+
...queryOptions
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function useApiInfiniteQuery(endpoint, ...args) {
|
|
24
|
+
const options = args[0];
|
|
25
|
+
const queryOptions = args.length > 1 ? args[1] : args[0];
|
|
26
|
+
const queryKey = endpoint.$queryKey(options);
|
|
27
|
+
return useInfiniteQuery({
|
|
28
|
+
queryKey,
|
|
29
|
+
queryFn: ({ pageParam }) => {
|
|
30
|
+
const fetchOptions = { ...options };
|
|
31
|
+
if (pageParam !== void 0) {
|
|
32
|
+
fetchOptions.query = { ...fetchOptions.query || {}, page: pageParam };
|
|
33
|
+
}
|
|
34
|
+
return endpoint(fetchOptions);
|
|
35
|
+
},
|
|
36
|
+
...queryOptions
|
|
37
|
+
});
|
|
38
|
+
}
|
|
13
39
|
|
|
14
40
|
// packages/react/src/hooks/useMutation.ts
|
|
15
41
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
@@ -29,6 +55,47 @@ function useApiMutation(endpoint, options) {
|
|
|
29
55
|
});
|
|
30
56
|
}
|
|
31
57
|
|
|
58
|
+
// packages/react/src/hooks/useQueryClient.ts
|
|
59
|
+
import { useQueryClient as useQueryClient2 } from "@tanstack/react-query";
|
|
60
|
+
function useApiQueryClient() {
|
|
61
|
+
const queryClient = useQueryClient2();
|
|
62
|
+
return {
|
|
63
|
+
queryClient,
|
|
64
|
+
/**
|
|
65
|
+
* Invalidate specific queries related to an endpoint.
|
|
66
|
+
* Optionally pass parameters to invalidate a more specific cache entry.
|
|
67
|
+
*/
|
|
68
|
+
invalidateEndpoint: (endpoint, options) => {
|
|
69
|
+
return queryClient.invalidateQueries({
|
|
70
|
+
queryKey: endpoint.$queryKey(options)
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
/**
|
|
74
|
+
* Prefetch data for an endpoint into the cache.
|
|
75
|
+
* Useful for SSR, RSC hydration, or proactive prefetching.
|
|
76
|
+
*/
|
|
77
|
+
prefetchEndpoint: (endpoint, options) => {
|
|
78
|
+
return queryClient.prefetchQuery({
|
|
79
|
+
queryKey: endpoint.$queryKey(options),
|
|
80
|
+
queryFn: () => endpoint(options)
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* Manually update the cache for a specific endpoint.
|
|
85
|
+
* Often used for optimistic updates in mutations.
|
|
86
|
+
*/
|
|
87
|
+
setEndpointData: (endpoint, options, updater) => {
|
|
88
|
+
return queryClient.setQueryData(endpoint.$queryKey(options), updater);
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Retrieve the current cached data for a specific endpoint.
|
|
92
|
+
*/
|
|
93
|
+
getEndpointData: (endpoint, options) => {
|
|
94
|
+
return queryClient.getQueryData(endpoint.$queryKey(options));
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
32
99
|
// packages/react/src/hooks/createHooks.ts
|
|
33
100
|
function createHooks(group) {
|
|
34
101
|
const hooks = {};
|
|
@@ -43,8 +110,29 @@ function createHooks(group) {
|
|
|
43
110
|
}
|
|
44
111
|
return hooks;
|
|
45
112
|
}
|
|
113
|
+
|
|
114
|
+
// packages/react/src/utils/form.ts
|
|
115
|
+
function setFormErrors(error, setError) {
|
|
116
|
+
if (error && typeof error === "object" && "status" in error && error.status === 422) {
|
|
117
|
+
const apiError = error;
|
|
118
|
+
const errors = apiError.errors;
|
|
119
|
+
if (errors && typeof errors === "object") {
|
|
120
|
+
for (const [field, messages] of Object.entries(errors)) {
|
|
121
|
+
if (Array.isArray(messages) && messages.length > 0) {
|
|
122
|
+
setError(field, { type: "server", message: String(messages[0]) });
|
|
123
|
+
} else if (typeof messages === "string") {
|
|
124
|
+
setError(field, { type: "server", message: messages });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
46
130
|
export {
|
|
47
131
|
createHooks,
|
|
132
|
+
setFormErrors,
|
|
133
|
+
useApiInfiniteQuery,
|
|
48
134
|
useApiMutation,
|
|
49
|
-
useApiQuery
|
|
135
|
+
useApiQuery,
|
|
136
|
+
useApiQueryClient,
|
|
137
|
+
useApiSuspenseQuery
|
|
50
138
|
};
|