@classytic/arc-next 0.1.3 → 0.2.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 +118 -13
- package/dist/api.d.ts +39 -8
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +13 -4
- package/dist/api.js.map +1 -1
- package/dist/hooks.d.ts +58 -5
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +141 -19
- package/dist/hooks.js.map +1 -1
- package/dist/mutation.d.ts +22 -16
- package/dist/mutation.d.ts.map +1 -1
- package/dist/mutation.js +9 -5
- package/dist/mutation.js.map +1 -1
- package/dist/query.d.ts +12 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +11 -6
- package/dist/query.js.map +1 -1
- package/package.json +1 -1
package/dist/mutation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation.js","names":[],"sources":["../src/mutation.ts"],"sourcesContent":["\"use client\";\n\nimport { useMutation, useQueryClient, type QueryClient, type QueryKey } from \"@tanstack/react-query\";\nimport { useTransition } from \"react\";\nimport { isArcApiError } from \"./client.js\";\nimport type { ToastHandler } from \"./client.js\";\n\n// Re-export for backward compatibility\nexport type { ToastHandler } from \"./client.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface MutationMessages {\n success?: string | ((data: unknown, variables: unknown) => string);\n error?: string | ((error: Error, variables: unknown) => string);\n}\n\nexport interface MutationCallbacks<TData, TVariables, TContext = unknown> {\n onMutate?: (variables: TVariables) => TContext | Promise<TContext>;\n onSuccess?: (data: TData, variables: TVariables, context: TContext) => void | Promise<void>;\n onError?: (error: Error, variables: TVariables, context: TContext) => void | Promise<void>;\n onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables, context: TContext) => void | Promise<void>;\n}\n\n// ============================================================================\n// Toast Configuration\n// ============================================================================\n\nlet toastHandler: ToastHandler = {\n success: (msg) => console.log(\"[Success]\", msg),\n error: (msg) => console.error(\"[Error]\", msg),\n};\n\n/**\n * Configure toast handler. Call once at app init.\n *\n * @example\n * import { toast } from \"sonner\";\n * configureToast({ success: toast.success, error: toast.error });\n */\nexport function configureToast(handler: ToastHandler): void {\n toastHandler = handler;\n}\n\nfunction showToast(\n type: \"success\" | \"error\",\n messages: MutationMessages | undefined,\n data: unknown,\n variables: unknown,\n error?: Error,\n handler?: ToastHandler,\n) {\n const activeHandler = handler ?? toastHandler;\n\n if (type === \"success\") {\n const msg = messages?.success;\n if (!msg) return;\n\n const text = typeof msg === \"function\" ? msg(data, variables) : msg;\n activeHandler.success(text);\n } else {\n const msg = messages?.error;\n let defaultMsg = error?.message || \"An error occurred\";\n if (isArcApiError(error) && error.fieldErrors) {\n const fields = Object.entries(error.fieldErrors);\n if (fields.length > 0) {\n defaultMsg = fields.map(([k, v]) => `${k}: ${v}`).join(', ');\n }\n }\n const text = typeof msg === \"function\"\n ? msg(error as Error, variables)\n : msg || defaultMsg;\n\n activeHandler.error(text);\n }\n}\n\n// ============================================================================\n// Mutation with React 19 Transitions\n// ============================================================================\n\nexport interface TransitionMutationConfig<TData, TVariables> {\n mutationFn: (variables: TVariables) => Promise<TData>;\n invalidateQueries?: QueryKey[];\n onSuccess?: (data: TData, variables: TVariables) => void;\n onError?: (error: Error, variables: TVariables) => void;\n onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;\n messages?: MutationMessages;\n useTransition?: boolean;\n showToast?: boolean;\n toastHandler?: ToastHandler;\n}\n\nexport function useMutationWithTransition<TData, TVariables>(config: TransitionMutationConfig<TData, TVariables>) {\n const {\n mutationFn,\n invalidateQueries = [],\n onSuccess,\n onError,\n onSettled,\n messages,\n useTransition: withTransition = true,\n showToast: toast = true,\n toastHandler: instanceToast,\n } = config;\n\n const queryClient = useQueryClient();\n const [isTransitioning, startTransition] = useTransition();\n\n const mutation = useMutation({\n mutationFn,\n\n onSuccess: (data, variables) => {\n const invalidate = () => {\n invalidateQueries.forEach((key) => queryClient.invalidateQueries({ queryKey: key }));\n };\n\n if (withTransition && invalidateQueries.length > 0) {\n startTransition(invalidate);\n } else {\n invalidate();\n }\n\n if (toast) showToast(\"success\", messages, data, variables, undefined, instanceToast);\n onSuccess?.(data, variables);\n },\n\n onError: (error, variables) => {\n if (toast) showToast(\"error\", messages, null, variables, error as Error, instanceToast);\n onError?.(error as Error, variables);\n },\n\n onSettled: (data, error, variables) => {\n onSettled?.(data, error as Error | null, variables);\n },\n });\n\n return {\n mutate: mutation.mutate,\n mutateAsync: mutation.mutateAsync,\n isPending: mutation.isPending || isTransitioning,\n isSuccess: mutation.isSuccess,\n isError: mutation.isError,\n error: mutation.error as Error | null,\n data: mutation.data,\n reset: mutation.reset,\n };\n}\n\n// ============================================================================\n// Mutation with Optimistic Updates\n// ============================================================================\n\nexport interface OptimisticMutationConfig<TData, TVariables> {\n mutationFn: (variables: TVariables) => Promise<TData>;\n queryKeys?: QueryKey[];\n optimisticUpdate?: (oldData: unknown, variables: TVariables) => unknown;\n onSuccess?: (data: TData, variables: TVariables) => void;\n onError?: (error: Error, variables: TVariables) => void;\n onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;\n messages?: MutationMessages;\n showToast?: boolean;\n toastHandler?: ToastHandler;\n}\n\nexport function useMutationWithOptimistic<TData, TVariables>(config: OptimisticMutationConfig<TData, TVariables>) {\n const {\n mutationFn,\n queryKeys = [],\n optimisticUpdate,\n onSuccess,\n onError,\n onSettled,\n messages,\n showToast: toast = true,\n toastHandler: instanceToast,\n } = config;\n\n const queryClient = useQueryClient();\n\n const mutation = useMutation({\n mutationFn,\n\n onMutate: async (variables) => {\n await Promise.all(queryKeys.map((key) => queryClient.cancelQueries({ queryKey: key })));\n\n const previous = queryKeys.map((key) => ({\n key,\n data: queryClient.getQueryData(key),\n }));\n\n if (optimisticUpdate) {\n queryKeys.forEach((key) => {\n queryClient.setQueryData(key, (old: unknown) => optimisticUpdate(old, variables));\n });\n }\n\n return { previous };\n },\n\n onSuccess: (data, variables) => {\n queryKeys.forEach((key) => queryClient.invalidateQueries({ queryKey: key }));\n if (toast) showToast(\"success\", messages, data, variables, undefined, instanceToast);\n onSuccess?.(data, variables);\n },\n\n onError: (error, variables, context) => {\n const ctx = context as { previous?: Array<{ key: QueryKey; data: unknown }> };\n ctx?.previous?.forEach(({ key, data }) => queryClient.setQueryData(key, data));\n\n if (toast) showToast(\"error\", messages, null, variables, error as Error, instanceToast);\n onError?.(error as Error, variables);\n },\n\n onSettled: (data, error, variables) => {\n onSettled?.(data, error as Error | null, variables);\n },\n });\n\n return {\n mutate: mutation.mutate,\n mutateAsync: mutation.mutateAsync,\n isPending: mutation.isPending,\n isSuccess: mutation.isSuccess,\n isError: mutation.isError,\n error: mutation.error as Error | null,\n data: mutation.data,\n reset: mutation.reset,\n };\n}\n\n// ============================================================================\n// Optimistic Mutation for CRUD Factory (handles multiple matching queries)\n// ============================================================================\n\nexport interface CreateOptimisticMutationConfig<TData, TVariables> {\n mutationFn: (variables: TVariables) => Promise<TData>;\n queryClient: QueryClient;\n queryKeys: QueryKey[];\n optimisticUpdate?: (oldData: unknown, variables: TVariables) => unknown;\n onSuccess?: (data: TData, variables: TVariables) => void;\n onError?: (error: Error, variables: TVariables) => void;\n messages?: MutationMessages;\n toastHandler?: ToastHandler;\n}\n\nexport function createOptimisticMutation<TData, TVariables>({\n mutationFn,\n queryClient,\n queryKeys,\n optimisticUpdate,\n onSuccess,\n onError,\n messages,\n toastHandler: instanceToast,\n}: CreateOptimisticMutationConfig<TData, TVariables>) {\n return useMutation({\n mutationFn,\n\n onMutate: async (variables) => {\n await Promise.all(queryKeys.map((key) => queryClient.cancelQueries({ queryKey: key, exact: false })));\n\n const previous = queryKeys.map((key) => ({\n key,\n data: queryClient.getQueriesData({ queryKey: key }),\n }));\n\n if (optimisticUpdate) {\n queryKeys.forEach((key) => {\n queryClient.getQueriesData({ queryKey: key }).forEach(([qKey, qData]) => {\n queryClient.setQueryData(qKey, optimisticUpdate(qData, variables));\n });\n });\n }\n\n return { previous };\n },\n\n onSuccess: (data, variables) => {\n if (messages?.success) showToast(\"success\", messages, data, variables, undefined, instanceToast);\n queryKeys.forEach((key) => queryClient.invalidateQueries({ queryKey: key }));\n onSuccess?.(data, variables);\n },\n\n onError: (error, variables, context) => {\n const ctx = context as { previous?: Array<{ key: QueryKey; data: Array<[QueryKey, unknown]> }> };\n ctx?.previous?.forEach(({ data }) => {\n data.forEach(([qKey, qData]) => queryClient.setQueryData(qKey, qData));\n });\n\n showToast(\"error\", messages, null, variables, error as Error, instanceToast);\n onError?.(error as Error, variables);\n },\n });\n}\n\n// ============================================================================\n// Query Config Presets\n// ============================================================================\n\nexport const QUERY_CONFIGS = {\n realtime: { staleTime: 20_000, refetchInterval: 30_000 },\n frequent: { staleTime: 60_000 },\n stable: { staleTime: 300_000 },\n static: { staleTime: 600_000 },\n} as const;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mutation.js","names":[],"sources":["../src/mutation.ts"],"sourcesContent":["\"use client\";\n\nimport { useMutation, useQueryClient, type QueryClient, type QueryKey, type UseMutateFunction, type UseMutateAsyncFunction } from \"@tanstack/react-query\";\nimport { useTransition } from \"react\";\nimport { isArcApiError } from \"./client.js\";\nimport type { ToastHandler } from \"./client.js\";\n\n// Re-export for backward compatibility\nexport type { ToastHandler } from \"./client.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface MutationMessages {\n success?: string | ((data: unknown, variables: unknown) => string);\n error?: string | ((error: Error, variables: unknown) => string);\n}\n\nexport interface MutationCallbacks<TData, TVariables, TContext = unknown> {\n onMutate?: (variables: TVariables) => TContext | Promise<TContext>;\n onSuccess?: (data: TData, variables: TVariables, context: TContext) => void | Promise<void>;\n onError?: (error: Error, variables: TVariables, context: TContext) => void | Promise<void>;\n onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables, context: TContext) => void | Promise<void>;\n}\n\nexport interface TransitionMutationReturn<TData, TVariables> {\n mutate: UseMutateFunction<TData, Error, TVariables>;\n mutateAsync: UseMutateAsyncFunction<TData, Error, TVariables>;\n isPending: boolean;\n isSuccess: boolean;\n isError: boolean;\n error: Error | null;\n data: TData | undefined;\n reset: () => void;\n}\n\n// ============================================================================\n// Toast Configuration\n// ============================================================================\n\nlet toastHandler: ToastHandler = {\n success: (msg) => console.log(\"[Success]\", msg),\n error: (msg) => console.error(\"[Error]\", msg),\n};\n\n/**\n * Configure toast handler. Call once at app init.\n *\n * @example\n * import { toast } from \"sonner\";\n * configureToast({ success: toast.success, error: toast.error });\n */\nexport function configureToast(handler: ToastHandler): void {\n toastHandler = handler;\n}\n\nfunction showToast(\n type: \"success\" | \"error\",\n messages: MutationMessages | undefined,\n data: unknown,\n variables: unknown,\n error?: Error,\n handler?: ToastHandler,\n) {\n const activeHandler = handler ?? toastHandler;\n\n if (type === \"success\") {\n const msg = messages?.success;\n if (!msg) return;\n\n const text = typeof msg === \"function\" ? msg(data, variables) : msg;\n activeHandler.success(text);\n } else {\n const msg = messages?.error;\n let defaultMsg = error?.message || \"An error occurred\";\n if (isArcApiError(error) && error.fieldErrors) {\n const fields = Object.entries(error.fieldErrors);\n if (fields.length > 0) {\n defaultMsg = fields.map(([k, v]) => `${k}: ${v}`).join(', ');\n }\n }\n const text = typeof msg === \"function\"\n ? msg(error as Error, variables)\n : msg || defaultMsg;\n\n activeHandler.error(text);\n }\n}\n\n// ============================================================================\n// Mutation with React 19 Transitions\n// ============================================================================\n\nexport interface TransitionMutationConfig<TData, TVariables> {\n mutationFn: (variables: TVariables) => Promise<TData>;\n invalidateQueries?: QueryKey[];\n onSuccess?: (data: TData, variables: TVariables) => void;\n onError?: (error: Error, variables: TVariables) => void;\n onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;\n messages?: MutationMessages;\n useTransition?: boolean;\n showToast?: boolean;\n /** Per-call toast guard — return false to suppress toast for this invocation */\n shouldToast?: () => boolean;\n toastHandler?: ToastHandler;\n}\n\nexport function useMutationWithTransition<TData, TVariables>(config: TransitionMutationConfig<TData, TVariables>) {\n const {\n mutationFn,\n invalidateQueries = [],\n onSuccess,\n onError,\n onSettled,\n messages,\n useTransition: withTransition = true,\n showToast: toast = true,\n toastHandler: instanceToast,\n } = config;\n\n const queryClient = useQueryClient();\n const [isTransitioning, startTransition] = useTransition();\n\n const mutation = useMutation({\n mutationFn,\n\n onSuccess: (data, variables) => {\n const invalidate = () => {\n invalidateQueries.forEach((key) => queryClient.invalidateQueries({ queryKey: key }));\n };\n\n if (withTransition && invalidateQueries.length > 0) {\n startTransition(invalidate);\n } else {\n invalidate();\n }\n\n const doToast = toast && (config.shouldToast?.() ?? true);\n if (doToast) showToast(\"success\", messages, data, variables, undefined, instanceToast);\n onSuccess?.(data, variables);\n },\n\n onError: (error, variables) => {\n const doToast = toast && (config.shouldToast?.() ?? true);\n if (doToast) showToast(\"error\", messages, null, variables, error as Error, instanceToast);\n onError?.(error as Error, variables);\n },\n\n onSettled: (data, error, variables) => {\n onSettled?.(data, error as Error | null, variables);\n },\n });\n\n return {\n mutate: mutation.mutate,\n mutateAsync: mutation.mutateAsync,\n isPending: mutation.isPending || isTransitioning,\n isSuccess: mutation.isSuccess,\n isError: mutation.isError,\n error: mutation.error as Error | null,\n data: mutation.data,\n reset: mutation.reset,\n };\n}\n\n// ============================================================================\n// Mutation with Optimistic Updates\n// ============================================================================\n\nexport interface OptimisticMutationConfig<TData, TVariables> {\n mutationFn: (variables: TVariables) => Promise<TData>;\n queryKeys?: QueryKey[];\n optimisticUpdate?: (oldData: unknown, variables: TVariables) => unknown;\n onSuccess?: (data: TData, variables: TVariables) => void;\n onError?: (error: Error, variables: TVariables) => void;\n onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;\n messages?: MutationMessages;\n showToast?: boolean;\n toastHandler?: ToastHandler;\n}\n\nexport function useMutationWithOptimistic<TData, TVariables>(config: OptimisticMutationConfig<TData, TVariables>) {\n const {\n mutationFn,\n queryKeys = [],\n optimisticUpdate,\n onSuccess,\n onError,\n onSettled,\n messages,\n showToast: toast = true,\n toastHandler: instanceToast,\n } = config;\n\n const queryClient = useQueryClient();\n\n const mutation = useMutation({\n mutationFn,\n\n onMutate: async (variables) => {\n await Promise.all(queryKeys.map((key) => queryClient.cancelQueries({ queryKey: key })));\n\n const previous = queryKeys.map((key) => ({\n key,\n data: queryClient.getQueryData(key),\n }));\n\n if (optimisticUpdate) {\n queryKeys.forEach((key) => {\n queryClient.setQueryData(key, (old: unknown) => optimisticUpdate(old, variables));\n });\n }\n\n return { previous };\n },\n\n onSuccess: (data, variables) => {\n queryKeys.forEach((key) => queryClient.invalidateQueries({ queryKey: key }));\n if (toast) showToast(\"success\", messages, data, variables, undefined, instanceToast);\n onSuccess?.(data, variables);\n },\n\n onError: (error, variables, context) => {\n const ctx = context as { previous?: Array<{ key: QueryKey; data: unknown }> };\n ctx?.previous?.forEach(({ key, data }) => queryClient.setQueryData(key, data));\n\n if (toast) showToast(\"error\", messages, null, variables, error as Error, instanceToast);\n onError?.(error as Error, variables);\n },\n\n onSettled: (data, error, variables) => {\n onSettled?.(data, error as Error | null, variables);\n },\n });\n\n return {\n mutate: mutation.mutate,\n mutateAsync: mutation.mutateAsync,\n isPending: mutation.isPending,\n isSuccess: mutation.isSuccess,\n isError: mutation.isError,\n error: mutation.error as Error | null,\n data: mutation.data,\n reset: mutation.reset,\n };\n}\n\n// ============================================================================\n// Optimistic Mutation for CRUD Factory (handles multiple matching queries)\n// ============================================================================\n\nexport interface CreateOptimisticMutationConfig<TData, TVariables> {\n mutationFn: (variables: TVariables) => Promise<TData>;\n queryClient: QueryClient;\n queryKeys: QueryKey[];\n optimisticUpdate?: (oldData: unknown, variables: TVariables) => unknown;\n onSuccess?: (data: TData, variables: TVariables) => void;\n onError?: (error: Error, variables: TVariables) => void;\n onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;\n messages?: MutationMessages;\n /** Per-call toast guard — return false to suppress toast for this invocation */\n shouldToast?: () => boolean;\n toastHandler?: ToastHandler;\n}\n\nexport function createOptimisticMutation<TData, TVariables>(config: CreateOptimisticMutationConfig<TData, TVariables>) {\n const {\n mutationFn,\n queryClient,\n queryKeys,\n optimisticUpdate,\n onSuccess,\n onError,\n onSettled,\n messages,\n toastHandler: instanceToast,\n } = config;\n return useMutation({\n mutationFn,\n\n onMutate: async (variables) => {\n await Promise.all(queryKeys.map((key) => queryClient.cancelQueries({ queryKey: key, exact: false })));\n\n const previous = queryKeys.map((key) => ({\n key,\n data: queryClient.getQueriesData({ queryKey: key }),\n }));\n\n if (optimisticUpdate) {\n queryKeys.forEach((key) => {\n queryClient.getQueriesData({ queryKey: key }).forEach(([qKey, qData]) => {\n queryClient.setQueryData(qKey, optimisticUpdate(qData, variables));\n });\n });\n }\n\n return { previous };\n },\n\n onSuccess: (data, variables) => {\n const doToast = config.shouldToast?.() ?? true;\n if (doToast && messages?.success) showToast(\"success\", messages, data, variables, undefined, instanceToast);\n queryKeys.forEach((key) => queryClient.invalidateQueries({ queryKey: key }));\n onSuccess?.(data, variables);\n },\n\n onError: (error, variables, context) => {\n const ctx = context as { previous?: Array<{ key: QueryKey; data: Array<[QueryKey, unknown]> }> };\n ctx?.previous?.forEach(({ data }) => {\n data.forEach(([qKey, qData]) => queryClient.setQueryData(qKey, qData));\n });\n\n const doToast = config.shouldToast?.() ?? true;\n if (doToast) showToast(\"error\", messages, null, variables, error as Error, instanceToast);\n onError?.(error as Error, variables);\n },\n\n onSettled: (data, error, variables) => {\n onSettled?.(data, error as Error | null, variables);\n },\n });\n}\n\n// ============================================================================\n// Query Config Presets\n// ============================================================================\n\nexport const QUERY_CONFIGS = {\n realtime: { staleTime: 20_000, refetchInterval: 30_000 },\n frequent: { staleTime: 60_000 },\n stable: { staleTime: 300_000 },\n static: { staleTime: 600_000 },\n} as const;\n"],"mappings":"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"}
|
package/dist/query.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ interface RequestPassthrough {
|
|
|
17
17
|
headerOptions?: Record<string, string>;
|
|
18
18
|
signal?: AbortSignal;
|
|
19
19
|
}
|
|
20
|
-
interface ListQueryOptions {
|
|
20
|
+
interface ListQueryOptions<TData = unknown> {
|
|
21
21
|
public?: boolean;
|
|
22
22
|
enabled?: boolean;
|
|
23
23
|
staleTime?: number;
|
|
@@ -28,10 +28,12 @@ interface ListQueryOptions {
|
|
|
28
28
|
refetchInterval?: number | false;
|
|
29
29
|
refetchIntervalInBackground?: boolean;
|
|
30
30
|
_scope?: string;
|
|
31
|
+
/** Transform raw API response before returning. Runs on each render, receives raw data. */
|
|
32
|
+
select?: (data: unknown) => TData;
|
|
31
33
|
/** Pass-through options for the underlying fetch request (cache, revalidate, tags, headers) */
|
|
32
34
|
request?: RequestPassthrough;
|
|
33
35
|
}
|
|
34
|
-
interface DetailQueryOptions {
|
|
36
|
+
interface DetailQueryOptions<TData = unknown> {
|
|
35
37
|
public?: boolean;
|
|
36
38
|
organizationId?: string | null;
|
|
37
39
|
enabled?: boolean;
|
|
@@ -45,6 +47,8 @@ interface DetailQueryOptions {
|
|
|
45
47
|
select?: string;
|
|
46
48
|
populate?: string | string[];
|
|
47
49
|
};
|
|
50
|
+
/** Transform raw API response before returning. Runs on each render, receives raw data. */
|
|
51
|
+
select?: (data: unknown) => TData;
|
|
48
52
|
/** Pass-through options for the underlying fetch request (cache, revalidate, tags, headers) */
|
|
49
53
|
request?: RequestPassthrough;
|
|
50
54
|
}
|
|
@@ -107,6 +111,7 @@ interface CreateListQueryConfig {
|
|
|
107
111
|
options?: Record<string, unknown>;
|
|
108
112
|
prefillDetailCache?: boolean;
|
|
109
113
|
detailKeyBuilder?: (id: string) => QueryKey;
|
|
114
|
+
select?: (data: unknown) => unknown;
|
|
110
115
|
}
|
|
111
116
|
declare function createListQuery<T>({
|
|
112
117
|
queryKey,
|
|
@@ -114,7 +119,8 @@ declare function createListQuery<T>({
|
|
|
114
119
|
enabled,
|
|
115
120
|
options,
|
|
116
121
|
prefillDetailCache,
|
|
117
|
-
detailKeyBuilder
|
|
122
|
+
detailKeyBuilder,
|
|
123
|
+
select
|
|
118
124
|
}: CreateListQueryConfig): ListQueryResult<T>;
|
|
119
125
|
interface CreateDetailQueryConfig {
|
|
120
126
|
queryKey: QueryKey;
|
|
@@ -123,12 +129,14 @@ interface CreateDetailQueryConfig {
|
|
|
123
129
|
}) => Promise<unknown>;
|
|
124
130
|
enabled?: boolean;
|
|
125
131
|
options?: Record<string, unknown>;
|
|
132
|
+
select?: (data: unknown) => unknown;
|
|
126
133
|
}
|
|
127
134
|
declare function createDetailQuery<T>({
|
|
128
135
|
queryKey,
|
|
129
136
|
queryFn,
|
|
130
137
|
enabled,
|
|
131
|
-
options
|
|
138
|
+
options,
|
|
139
|
+
select
|
|
132
140
|
}: CreateDetailQueryConfig): DetailQueryResult<T>;
|
|
133
141
|
interface InfiniteListQueryOptions {
|
|
134
142
|
public?: boolean;
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","names":[],"sources":["../src/query.ts"],"mappings":";;;UAUiB,cAAA;EACf,KAAA;EACA,KAAA;EACA,IAAA;EACA,KAAA;EACA,OAAA;EACA,OAAA;AAAA;;UAIe,kBAAA;EACf,KAAA,GAAQ,YAAA;EACR,UAAA;EACA,IAAA;EACA,aAAA,GAAgB,MAAA;EAChB,MAAA,GAAS,WAAA;AAAA;AAAA,UAGM,gBAAA;EACf,MAAA;EACA,OAAA;EACA,SAAA;EACA,MAAA;EACA,oBAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,2BAAA;EACA,MAAA;EAbA;EAeA,OAAA,GAAU,kBAAA;AAAA;AAAA,UAGK,kBAAA;EACf,MAAA;EACA,cAAA;EACA,OAAA;EACA,SAAA;EACA,MAAA;EACA,iBAAA;EACA,eAAA;EACA,2BAAA;
|
|
1
|
+
{"version":3,"file":"query.d.ts","names":[],"sources":["../src/query.ts"],"mappings":"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"}
|
package/dist/query.js
CHANGED
|
@@ -107,14 +107,15 @@ function createCacheUtils(KEYS) {
|
|
|
107
107
|
removeDetail: (client, id) => client.removeQueries({ queryKey: KEYS.detail(id) })
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
-
function createListQuery({ queryKey, queryFn, enabled = true, options = {}, prefillDetailCache = true, detailKeyBuilder }) {
|
|
110
|
+
function createListQuery({ queryKey, queryFn, enabled = true, options = {}, prefillDetailCache = true, detailKeyBuilder, select }) {
|
|
111
111
|
const queryClient = useQueryClient();
|
|
112
112
|
const query = useQuery({
|
|
113
113
|
queryKey,
|
|
114
|
-
queryFn: () => queryFn({}),
|
|
114
|
+
queryFn: ({ signal }) => queryFn({ signal }),
|
|
115
115
|
enabled,
|
|
116
116
|
...DEFAULT_QUERY_CONFIG,
|
|
117
117
|
...options,
|
|
118
|
+
...select ? { select } : {},
|
|
118
119
|
placeholderData: keepPreviousData
|
|
119
120
|
});
|
|
120
121
|
const items = extractItems(query.data);
|
|
@@ -144,13 +145,14 @@ function createListQuery({ queryKey, queryFn, enabled = true, options = {}, pref
|
|
|
144
145
|
data: query.data
|
|
145
146
|
};
|
|
146
147
|
}
|
|
147
|
-
function createDetailQuery({ queryKey, queryFn, enabled = true, options = {} }) {
|
|
148
|
+
function createDetailQuery({ queryKey, queryFn, enabled = true, options = {}, select }) {
|
|
148
149
|
const query = useQuery({
|
|
149
150
|
queryKey,
|
|
150
|
-
queryFn: () => queryFn({}),
|
|
151
|
+
queryFn: ({ signal }) => queryFn({ signal }),
|
|
151
152
|
enabled,
|
|
152
153
|
...DEFAULT_QUERY_CONFIG,
|
|
153
|
-
...options
|
|
154
|
+
...options,
|
|
155
|
+
...select ? { select } : {}
|
|
154
156
|
});
|
|
155
157
|
return {
|
|
156
158
|
item: extractItem(query.data),
|
|
@@ -167,7 +169,10 @@ function createDetailQuery({ queryKey, queryFn, enabled = true, options = {} })
|
|
|
167
169
|
function createInfiniteListQuery({ queryKey, queryFn, enabled = true, options = {}, initialPageParam = 1, getNextPageParam, getPreviousPageParam }) {
|
|
168
170
|
const query = useInfiniteQuery({
|
|
169
171
|
queryKey,
|
|
170
|
-
queryFn: ({ pageParam }) => queryFn({
|
|
172
|
+
queryFn: ({ pageParam, signal }) => queryFn({
|
|
173
|
+
pageParam,
|
|
174
|
+
signal
|
|
175
|
+
}),
|
|
171
176
|
enabled,
|
|
172
177
|
initialPageParam,
|
|
173
178
|
getNextPageParam,
|
package/dist/query.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","names":[],"sources":["../src/query.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useInfiniteQuery, useQueryClient, keepPreviousData, type QueryKey, type InfiniteData } from \"@tanstack/react-query\";\nimport { useEffect } from \"react\";\nimport type { QueryClient } from \"@tanstack/react-query\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PaginationData {\n total: number;\n pages: number;\n page: number;\n limit: number;\n hasNext: boolean;\n hasPrev: boolean;\n}\n\n/** Request-level options passed through to the fetch call */\nexport interface RequestPassthrough {\n cache?: RequestCache;\n revalidate?: number;\n tags?: string[];\n headerOptions?: Record<string, string>;\n signal?: AbortSignal;\n}\n\nexport interface ListQueryOptions {\n public?: boolean;\n enabled?: boolean;\n staleTime?: number;\n gcTime?: number;\n refetchOnWindowFocus?: boolean;\n structuralSharing?: boolean;\n prefillDetailCache?: boolean;\n refetchInterval?: number | false;\n refetchIntervalInBackground?: boolean;\n _scope?: string;\n /** Pass-through options for the underlying fetch request (cache, revalidate, tags, headers) */\n request?: RequestPassthrough;\n}\n\nexport interface DetailQueryOptions {\n public?: boolean;\n organizationId?: string | null;\n enabled?: boolean;\n staleTime?: number;\n gcTime?: number;\n structuralSharing?: boolean;\n refetchInterval?: number | false;\n refetchIntervalInBackground?: boolean;\n /** Query params passed to getById (e.g. select, populate) */\n params?: { select?: string; populate?: string | string[] };\n /** Pass-through options for the underlying fetch request (cache, revalidate, tags, headers) */\n request?: RequestPassthrough;\n}\n\nexport interface ListQueryResult<T> {\n items: T[];\n pagination: PaginationData | null;\n isLoading: boolean;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n isStale: boolean;\n error: Error | null;\n refetch: () => Promise<unknown>;\n data: unknown;\n}\n\nexport interface DetailQueryResult<T> {\n item: T | null;\n isLoading: boolean;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n isStale: boolean;\n error: Error | null;\n refetch: () => Promise<unknown>;\n data: unknown;\n}\n\nexport interface QueryKeys {\n all: string[];\n lists: () => QueryKey;\n list: (params?: unknown) => QueryKey;\n details: () => QueryKey;\n detail: (id: string) => QueryKey;\n custom: (key: string, ...args: unknown[]) => QueryKey;\n scopedList: (scope: string, params?: unknown) => QueryKey;\n}\n\nexport interface CacheUtils<T> {\n invalidateAll: (client: QueryClient) => Promise<void>;\n invalidateLists: (client: QueryClient) => Promise<void>;\n invalidateDetail: (client: QueryClient, id: string) => Promise<void>;\n setDetail: (client: QueryClient, id: string, data: T) => void;\n getDetail: (client: QueryClient, id: string) => T | undefined;\n removeDetail: (client: QueryClient, id: string) => void;\n}\n\n// ============================================================================\n// Default Config\n// ============================================================================\n\nexport const DEFAULT_QUERY_CONFIG = {\n staleTime: 5 * 60 * 1000,\n gcTime: 30 * 60 * 1000,\n refetchOnWindowFocus: false,\n retry: 0,\n} as const;\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\nexport function getItemId(item: unknown): string | null {\n if (!item || typeof item !== \"object\") return null;\n const obj = item as Record<string, unknown>;\n const id = obj._id ?? obj.id;\n return typeof id === \"string\" ? id : id ? String(id) : null;\n}\n\nfunction normalizePagination(data: unknown): PaginationData | null {\n if (!data || typeof data !== \"object\") return null;\n const d = data as Record<string, unknown>;\n\n const hasTotal = d.total != null || d.totalDocs != null;\n const hasPages = d.pages != null || d.totalPages != null;\n if (!hasTotal && !hasPages) return null;\n\n return {\n total: Number(d.total ?? d.totalDocs ?? 0),\n pages: Number(d.pages ?? d.totalPages ?? 1),\n page: Number(d.page ?? d.currentPage ?? 1),\n limit: Number(d.limit ?? 10),\n hasNext: Boolean(d.hasNext ?? d.hasNextPage ?? d.hasMore ?? false),\n hasPrev: Boolean(d.hasPrev ?? d.hasPrevPage ?? false),\n };\n}\n\nfunction extractItems<T>(data: unknown): T[] {\n if (!data) return [];\n if (Array.isArray(data)) return data as T[];\n if (typeof data !== \"object\") return [];\n\n const d = data as Record<string, unknown>;\n const items = d.docs ?? d.data ?? d.items ?? d.results;\n return Array.isArray(items) ? (items as T[]) : [];\n}\n\nfunction extractItem<T>(data: unknown): T | null {\n if (!data) return null;\n if (typeof data !== \"object\") return null;\n\n const d = data as Record<string, unknown>;\n return (d.data ?? d) as T | null;\n}\n\nexport function updateListCache<T>(listData: unknown, updater: (items: T[]) => T[]): unknown {\n if (!listData) return listData;\n\n if (Array.isArray(listData)) {\n return updater(listData as T[]);\n }\n\n if (typeof listData !== \"object\") return listData;\n const d = listData as Record<string, unknown>;\n\n // Detect the items array field: docs > data > items > results (same order as extractItems)\n const arrayField = \"docs\" in d ? \"docs\"\n : (Array.isArray(d.data) ? \"data\"\n : (\"items\" in d && Array.isArray(d.items) ? \"items\"\n : (\"results\" in d && Array.isArray(d.results) ? \"results\" : null)));\n\n if (!arrayField) return listData;\n\n const updated = updater(d[arrayField] as T[]);\n const original = d[arrayField] as T[];\n const delta = updated.length - original.length;\n\n // Update total/totalDocs when item count changes (optimistic add/delete)\n const result: Record<string, unknown> = { ...d, [arrayField]: updated };\n if (delta !== 0) {\n if (d.total != null) result.total = Math.max(0, Number(d.total) + delta);\n if (d.totalDocs != null) result.totalDocs = Math.max(0, Number(d.totalDocs) + delta);\n }\n\n return result;\n}\n\n// ============================================================================\n// Query Keys Factory\n// ============================================================================\n\nexport function createQueryKeys(entityKey: string): QueryKeys {\n return {\n all: [entityKey],\n lists: () => [entityKey, \"list\"],\n list: (params) => [entityKey, \"list\", params],\n details: () => [entityKey, \"detail\"],\n detail: (id) => [entityKey, \"detail\", id],\n custom: (key, ...args) => [entityKey, key, ...args],\n scopedList: (scope, params) => [entityKey, \"list\", { _scope: scope, ...(params as object) }],\n };\n}\n\n// ============================================================================\n// Cache Utilities Factory\n// ============================================================================\n\nexport function createCacheUtils<T>(KEYS: QueryKeys): CacheUtils<T> {\n return {\n invalidateAll: (client) => client.invalidateQueries({ queryKey: KEYS.all }),\n invalidateLists: (client) => client.invalidateQueries({ queryKey: KEYS.lists() }),\n invalidateDetail: (client, id) => client.invalidateQueries({ queryKey: KEYS.detail(id) }),\n setDetail: (client, id, data) => client.setQueryData(KEYS.detail(id), { data }),\n getDetail: (client, id) => {\n const cached = client.getQueryData(KEYS.detail(id)) as { data?: T } | undefined;\n return cached?.data;\n },\n removeDetail: (client, id) => client.removeQueries({ queryKey: KEYS.detail(id) }),\n };\n}\n\n// ============================================================================\n// List Query Hook\n// ============================================================================\n\nexport interface CreateListQueryConfig {\n queryKey: QueryKey;\n queryFn: (context: { signal?: AbortSignal }) => Promise<unknown>;\n enabled?: boolean;\n options?: Record<string, unknown>;\n prefillDetailCache?: boolean;\n detailKeyBuilder?: (id: string) => QueryKey;\n}\n\nexport function createListQuery<T>({\n queryKey,\n queryFn,\n enabled = true,\n options = {},\n prefillDetailCache = true,\n detailKeyBuilder,\n}: CreateListQueryConfig): ListQueryResult<T> {\n const queryClient = useQueryClient();\n\n const query = useQuery({\n queryKey,\n queryFn: () => queryFn({}),\n enabled,\n ...DEFAULT_QUERY_CONFIG,\n ...options,\n placeholderData: keepPreviousData,\n });\n\n const items = extractItems<T>(query.data);\n const pagination = normalizePagination(query.data);\n\n useEffect(() => {\n if (!prefillDetailCache || !detailKeyBuilder || items.length === 0) return;\n\n items.forEach((item) => {\n const id = getItemId(item);\n if (id) queryClient.setQueryData(detailKeyBuilder(id), { data: item });\n });\n }, [items, prefillDetailCache, detailKeyBuilder, queryClient]);\n\n return {\n items,\n pagination,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n isStale: query.isStale,\n error: query.error,\n refetch: query.refetch,\n data: query.data,\n };\n}\n\n// ============================================================================\n// Detail Query Hook\n// ============================================================================\n\nexport interface CreateDetailQueryConfig {\n queryKey: QueryKey;\n queryFn: (context: { signal?: AbortSignal }) => Promise<unknown>;\n enabled?: boolean;\n options?: Record<string, unknown>;\n}\n\nexport function createDetailQuery<T>({\n queryKey,\n queryFn,\n enabled = true,\n options = {},\n}: CreateDetailQueryConfig): DetailQueryResult<T> {\n const query = useQuery({\n queryKey,\n queryFn: () => queryFn({}),\n enabled,\n ...DEFAULT_QUERY_CONFIG,\n ...options,\n });\n\n const item = extractItem<T>(query.data);\n\n return {\n item,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n isStale: query.isStale,\n error: query.error,\n refetch: query.refetch,\n data: query.data,\n };\n}\n\n// ============================================================================\n// Infinite List Query Hook\n// ============================================================================\n\nexport interface InfiniteListQueryOptions {\n public?: boolean;\n enabled?: boolean;\n staleTime?: number;\n gcTime?: number;\n refetchOnWindowFocus?: boolean;\n structuralSharing?: boolean;\n _scope?: string;\n request?: RequestPassthrough;\n}\n\nexport interface InfiniteListQueryResult<T> {\n items: T[];\n hasNextPage: boolean;\n hasPreviousPage: boolean;\n isFetchingNextPage: boolean;\n isFetchingPreviousPage: boolean;\n fetchNextPage: () => void;\n fetchPreviousPage: () => void;\n isLoading: boolean;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n error: Error | null;\n refetch: () => Promise<unknown>;\n data: InfiniteData<unknown> | undefined;\n}\n\nexport interface CreateInfiniteListQueryConfig {\n queryKey: QueryKey;\n queryFn: (context: { signal?: AbortSignal; pageParam: unknown }) => Promise<unknown>;\n enabled?: boolean;\n options?: Record<string, unknown>;\n initialPageParam?: unknown;\n getNextPageParam: (lastPage: unknown) => unknown;\n getPreviousPageParam?: (firstPage: unknown) => unknown;\n}\n\nexport function createInfiniteListQuery<T>({\n queryKey,\n queryFn,\n enabled = true,\n options = {},\n initialPageParam = 1,\n getNextPageParam,\n getPreviousPageParam,\n}: CreateInfiniteListQueryConfig): InfiniteListQueryResult<T> {\n const query = useInfiniteQuery({\n queryKey,\n queryFn: ({ pageParam }) => queryFn({ pageParam }),\n enabled,\n initialPageParam,\n getNextPageParam,\n getPreviousPageParam,\n ...DEFAULT_QUERY_CONFIG,\n ...options,\n });\n\n const items: T[] = query.data?.pages.flatMap((page) => extractItems<T>(page)) ?? [];\n\n return {\n items,\n hasNextPage: query.hasNextPage,\n hasPreviousPage: query.hasPreviousPage,\n isFetchingNextPage: query.isFetchingNextPage,\n isFetchingPreviousPage: query.isFetchingPreviousPage,\n fetchNextPage: query.fetchNextPage,\n fetchPreviousPage: query.fetchPreviousPage,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n error: query.error,\n refetch: query.refetch,\n data: query.data,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query.js","names":[],"sources":["../src/query.ts"],"sourcesContent":["\"use client\";\n\nimport { useQuery, useInfiniteQuery, useQueryClient, keepPreviousData, type QueryKey, type InfiniteData } from \"@tanstack/react-query\";\nimport { useEffect } from \"react\";\nimport type { QueryClient } from \"@tanstack/react-query\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PaginationData {\n total: number;\n pages: number;\n page: number;\n limit: number;\n hasNext: boolean;\n hasPrev: boolean;\n}\n\n/** Request-level options passed through to the fetch call */\nexport interface RequestPassthrough {\n cache?: RequestCache;\n revalidate?: number;\n tags?: string[];\n headerOptions?: Record<string, string>;\n signal?: AbortSignal;\n}\n\nexport interface ListQueryOptions<TData = unknown> {\n public?: boolean;\n enabled?: boolean;\n staleTime?: number;\n gcTime?: number;\n refetchOnWindowFocus?: boolean;\n structuralSharing?: boolean;\n prefillDetailCache?: boolean;\n refetchInterval?: number | false;\n refetchIntervalInBackground?: boolean;\n _scope?: string;\n /** Transform raw API response before returning. Runs on each render, receives raw data. */\n select?: (data: unknown) => TData;\n /** Pass-through options for the underlying fetch request (cache, revalidate, tags, headers) */\n request?: RequestPassthrough;\n}\n\nexport interface DetailQueryOptions<TData = unknown> {\n public?: boolean;\n organizationId?: string | null;\n enabled?: boolean;\n staleTime?: number;\n gcTime?: number;\n structuralSharing?: boolean;\n refetchInterval?: number | false;\n refetchIntervalInBackground?: boolean;\n /** Query params passed to getById (e.g. select, populate) */\n params?: { select?: string; populate?: string | string[] };\n /** Transform raw API response before returning. Runs on each render, receives raw data. */\n select?: (data: unknown) => TData;\n /** Pass-through options for the underlying fetch request (cache, revalidate, tags, headers) */\n request?: RequestPassthrough;\n}\n\nexport interface ListQueryResult<T> {\n items: T[];\n pagination: PaginationData | null;\n isLoading: boolean;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n isStale: boolean;\n error: Error | null;\n refetch: () => Promise<unknown>;\n data: unknown;\n}\n\nexport interface DetailQueryResult<T> {\n item: T | null;\n isLoading: boolean;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n isStale: boolean;\n error: Error | null;\n refetch: () => Promise<unknown>;\n data: unknown;\n}\n\nexport interface QueryKeys {\n all: string[];\n lists: () => QueryKey;\n list: (params?: unknown) => QueryKey;\n details: () => QueryKey;\n detail: (id: string) => QueryKey;\n custom: (key: string, ...args: unknown[]) => QueryKey;\n scopedList: (scope: string, params?: unknown) => QueryKey;\n}\n\nexport interface CacheUtils<T> {\n invalidateAll: (client: QueryClient) => Promise<void>;\n invalidateLists: (client: QueryClient) => Promise<void>;\n invalidateDetail: (client: QueryClient, id: string) => Promise<void>;\n setDetail: (client: QueryClient, id: string, data: T) => void;\n getDetail: (client: QueryClient, id: string) => T | undefined;\n removeDetail: (client: QueryClient, id: string) => void;\n}\n\n// ============================================================================\n// Default Config\n// ============================================================================\n\nexport const DEFAULT_QUERY_CONFIG = {\n staleTime: 5 * 60 * 1000,\n gcTime: 30 * 60 * 1000,\n refetchOnWindowFocus: false,\n retry: 0,\n} as const;\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\nexport function getItemId(item: unknown): string | null {\n if (!item || typeof item !== \"object\") return null;\n const obj = item as Record<string, unknown>;\n const id = obj._id ?? obj.id;\n return typeof id === \"string\" ? id : id ? String(id) : null;\n}\n\nfunction normalizePagination(data: unknown): PaginationData | null {\n if (!data || typeof data !== \"object\") return null;\n const d = data as Record<string, unknown>;\n\n const hasTotal = d.total != null || d.totalDocs != null;\n const hasPages = d.pages != null || d.totalPages != null;\n if (!hasTotal && !hasPages) return null;\n\n return {\n total: Number(d.total ?? d.totalDocs ?? 0),\n pages: Number(d.pages ?? d.totalPages ?? 1),\n page: Number(d.page ?? d.currentPage ?? 1),\n limit: Number(d.limit ?? 10),\n hasNext: Boolean(d.hasNext ?? d.hasNextPage ?? d.hasMore ?? false),\n hasPrev: Boolean(d.hasPrev ?? d.hasPrevPage ?? false),\n };\n}\n\nfunction extractItems<T>(data: unknown): T[] {\n if (!data) return [];\n if (Array.isArray(data)) return data as T[];\n if (typeof data !== \"object\") return [];\n\n const d = data as Record<string, unknown>;\n const items = d.docs ?? d.data ?? d.items ?? d.results;\n return Array.isArray(items) ? (items as T[]) : [];\n}\n\nfunction extractItem<T>(data: unknown): T | null {\n if (!data) return null;\n if (typeof data !== \"object\") return null;\n\n const d = data as Record<string, unknown>;\n return (d.data ?? d) as T | null;\n}\n\nexport function updateListCache<T>(listData: unknown, updater: (items: T[]) => T[]): unknown {\n if (!listData) return listData;\n\n if (Array.isArray(listData)) {\n return updater(listData as T[]);\n }\n\n if (typeof listData !== \"object\") return listData;\n const d = listData as Record<string, unknown>;\n\n // Detect the items array field: docs > data > items > results (same order as extractItems)\n const arrayField = \"docs\" in d ? \"docs\"\n : (Array.isArray(d.data) ? \"data\"\n : (\"items\" in d && Array.isArray(d.items) ? \"items\"\n : (\"results\" in d && Array.isArray(d.results) ? \"results\" : null)));\n\n if (!arrayField) return listData;\n\n const updated = updater(d[arrayField] as T[]);\n const original = d[arrayField] as T[];\n const delta = updated.length - original.length;\n\n // Update total/totalDocs when item count changes (optimistic add/delete)\n const result: Record<string, unknown> = { ...d, [arrayField]: updated };\n if (delta !== 0) {\n if (d.total != null) result.total = Math.max(0, Number(d.total) + delta);\n if (d.totalDocs != null) result.totalDocs = Math.max(0, Number(d.totalDocs) + delta);\n }\n\n return result;\n}\n\n// ============================================================================\n// Query Keys Factory\n// ============================================================================\n\nexport function createQueryKeys(entityKey: string): QueryKeys {\n return {\n all: [entityKey],\n lists: () => [entityKey, \"list\"],\n list: (params) => [entityKey, \"list\", params],\n details: () => [entityKey, \"detail\"],\n detail: (id) => [entityKey, \"detail\", id],\n custom: (key, ...args) => [entityKey, key, ...args],\n scopedList: (scope, params) => [entityKey, \"list\", { _scope: scope, ...(params as object) }],\n };\n}\n\n// ============================================================================\n// Cache Utilities Factory\n// ============================================================================\n\nexport function createCacheUtils<T>(KEYS: QueryKeys): CacheUtils<T> {\n return {\n invalidateAll: (client) => client.invalidateQueries({ queryKey: KEYS.all }),\n invalidateLists: (client) => client.invalidateQueries({ queryKey: KEYS.lists() }),\n invalidateDetail: (client, id) => client.invalidateQueries({ queryKey: KEYS.detail(id) }),\n setDetail: (client, id, data) => client.setQueryData(KEYS.detail(id), { data }),\n getDetail: (client, id) => {\n const cached = client.getQueryData(KEYS.detail(id)) as { data?: T } | undefined;\n return cached?.data;\n },\n removeDetail: (client, id) => client.removeQueries({ queryKey: KEYS.detail(id) }),\n };\n}\n\n// ============================================================================\n// List Query Hook\n// ============================================================================\n\nexport interface CreateListQueryConfig {\n queryKey: QueryKey;\n queryFn: (context: { signal?: AbortSignal }) => Promise<unknown>;\n enabled?: boolean;\n options?: Record<string, unknown>;\n prefillDetailCache?: boolean;\n detailKeyBuilder?: (id: string) => QueryKey;\n select?: (data: unknown) => unknown;\n}\n\nexport function createListQuery<T>({\n queryKey,\n queryFn,\n enabled = true,\n options = {},\n prefillDetailCache = true,\n detailKeyBuilder,\n select,\n}: CreateListQueryConfig): ListQueryResult<T> {\n const queryClient = useQueryClient();\n\n const query = useQuery({\n queryKey,\n queryFn: ({ signal }) => queryFn({ signal }),\n enabled,\n ...DEFAULT_QUERY_CONFIG,\n ...options,\n ...(select ? { select } : {}),\n placeholderData: keepPreviousData,\n });\n\n const items = extractItems<T>(query.data);\n const pagination = normalizePagination(query.data);\n\n useEffect(() => {\n if (!prefillDetailCache || !detailKeyBuilder || items.length === 0) return;\n\n items.forEach((item) => {\n const id = getItemId(item);\n if (id) queryClient.setQueryData(detailKeyBuilder(id), { data: item });\n });\n }, [items, prefillDetailCache, detailKeyBuilder, queryClient]);\n\n return {\n items,\n pagination,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n isStale: query.isStale,\n error: query.error,\n refetch: query.refetch,\n data: query.data,\n };\n}\n\n// ============================================================================\n// Detail Query Hook\n// ============================================================================\n\nexport interface CreateDetailQueryConfig {\n queryKey: QueryKey;\n queryFn: (context: { signal?: AbortSignal }) => Promise<unknown>;\n enabled?: boolean;\n options?: Record<string, unknown>;\n select?: (data: unknown) => unknown;\n}\n\nexport function createDetailQuery<T>({\n queryKey,\n queryFn,\n enabled = true,\n options = {},\n select,\n}: CreateDetailQueryConfig): DetailQueryResult<T> {\n const query = useQuery({\n queryKey,\n queryFn: ({ signal }) => queryFn({ signal }),\n enabled,\n ...DEFAULT_QUERY_CONFIG,\n ...options,\n ...(select ? { select } : {}),\n });\n\n const item = extractItem<T>(query.data);\n\n return {\n item,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n isStale: query.isStale,\n error: query.error,\n refetch: query.refetch,\n data: query.data,\n };\n}\n\n// ============================================================================\n// Infinite List Query Hook\n// ============================================================================\n\nexport interface InfiniteListQueryOptions {\n public?: boolean;\n enabled?: boolean;\n staleTime?: number;\n gcTime?: number;\n refetchOnWindowFocus?: boolean;\n structuralSharing?: boolean;\n _scope?: string;\n request?: RequestPassthrough;\n}\n\nexport interface InfiniteListQueryResult<T> {\n items: T[];\n hasNextPage: boolean;\n hasPreviousPage: boolean;\n isFetchingNextPage: boolean;\n isFetchingPreviousPage: boolean;\n fetchNextPage: () => void;\n fetchPreviousPage: () => void;\n isLoading: boolean;\n isFetching: boolean;\n isError: boolean;\n isSuccess: boolean;\n error: Error | null;\n refetch: () => Promise<unknown>;\n data: InfiniteData<unknown> | undefined;\n}\n\nexport interface CreateInfiniteListQueryConfig {\n queryKey: QueryKey;\n queryFn: (context: { signal?: AbortSignal; pageParam: unknown }) => Promise<unknown>;\n enabled?: boolean;\n options?: Record<string, unknown>;\n initialPageParam?: unknown;\n getNextPageParam: (lastPage: unknown) => unknown;\n getPreviousPageParam?: (firstPage: unknown) => unknown;\n}\n\nexport function createInfiniteListQuery<T>({\n queryKey,\n queryFn,\n enabled = true,\n options = {},\n initialPageParam = 1,\n getNextPageParam,\n getPreviousPageParam,\n}: CreateInfiniteListQueryConfig): InfiniteListQueryResult<T> {\n const query = useInfiniteQuery({\n queryKey,\n queryFn: ({ pageParam, signal }) => queryFn({ pageParam, signal }),\n enabled,\n initialPageParam,\n getNextPageParam,\n getPreviousPageParam,\n ...DEFAULT_QUERY_CONFIG,\n ...options,\n });\n\n const items: T[] = query.data?.pages.flatMap((page) => extractItems<T>(page)) ?? [];\n\n return {\n items,\n hasNextPage: query.hasNextPage,\n hasPreviousPage: query.hasPreviousPage,\n isFetchingNextPage: query.isFetchingNextPage,\n isFetchingPreviousPage: query.isFetchingPreviousPage,\n fetchNextPage: query.fetchNextPage,\n fetchPreviousPage: query.fetchPreviousPage,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n isSuccess: query.isSuccess,\n error: query.error,\n refetch: query.refetch,\n data: query.data,\n };\n}\n"],"mappings":"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"}
|