@umituz/react-native-ai-generation-content 1.48.0 → 1.48.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.48.0",
3
+ "version": "1.48.2",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -5,8 +5,17 @@
5
5
 
6
6
  import type { Creation } from "../entities/Creation";
7
7
 
8
+ export type CreationsSubscriptionCallback = (creations: Creation[]) => void;
9
+ export type UnsubscribeFunction = () => void;
10
+
8
11
  export interface ICreationsRepository {
9
12
  getAll(userId: string): Promise<Creation[]>;
13
+ /** Realtime subscription to all creations */
14
+ subscribeToAll(
15
+ userId: string,
16
+ onData: CreationsSubscriptionCallback,
17
+ onError?: (error: Error) => void,
18
+ ): UnsubscribeFunction;
10
19
  getById(userId: string, id: string): Promise<Creation | null>;
11
20
  create(userId: string, creation: Creation): Promise<void>;
12
21
  update(
@@ -2,4 +2,8 @@
2
2
  * Domain Repository Interfaces
3
3
  */
4
4
 
5
- export type { ICreationsRepository } from "./ICreationsRepository";
5
+ export type {
6
+ ICreationsRepository,
7
+ CreationsSubscriptionCallback,
8
+ UnsubscribeFunction,
9
+ } from "./ICreationsRepository";
@@ -94,7 +94,11 @@ export { DEFAULT_TRANSLATIONS, DEFAULT_CONFIG } from "./domain/value-objects";
94
94
  // DOMAIN LAYER - Repository Interface
95
95
  // =============================================================================
96
96
 
97
- export type { ICreationsRepository } from "./domain/repositories";
97
+ export type {
98
+ ICreationsRepository,
99
+ CreationsSubscriptionCallback,
100
+ UnsubscribeFunction,
101
+ } from "./domain/repositories";
98
102
 
99
103
  // =============================================================================
100
104
  // INFRASTRUCTURE LAYER
@@ -1,7 +1,8 @@
1
- import { getDocs, getDoc, query, orderBy } from "firebase/firestore";
1
+ import { getDocs, getDoc, query, orderBy, onSnapshot } from "firebase/firestore";
2
2
  import { type FirestorePathResolver } from "@umituz/react-native-firebase";
3
3
  import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
4
4
  import type { Creation, CreationDocument } from "../../domain/entities/Creation";
5
+ import type { CreationsSubscriptionCallback, UnsubscribeFunction } from "../../domain/repositories/ICreationsRepository";
5
6
 
6
7
  declare const __DEV__: boolean;
7
8
 
@@ -85,10 +86,66 @@ export class CreationsFetcher {
85
86
  return this.documentMapper(docSnap.id, data);
86
87
  } catch (error) {
87
88
  if (__DEV__) {
88
-
89
+
89
90
  console.error("[CreationsRepository] getById() ERROR", error);
90
91
  }
91
92
  return null;
92
93
  }
93
94
  }
95
+
96
+ subscribeToAll(
97
+ userId: string,
98
+ onData: CreationsSubscriptionCallback,
99
+ onError?: (error: Error) => void,
100
+ ): UnsubscribeFunction {
101
+ if (__DEV__) {
102
+ console.log("[CreationsFetcher] subscribeToAll()", { userId });
103
+ }
104
+
105
+ const userCollection = this.pathResolver.getUserCollection(userId);
106
+ if (!userCollection) {
107
+ onData([]);
108
+ return () => {};
109
+ }
110
+
111
+ const q = query(userCollection, orderBy("createdAt", "desc"));
112
+
113
+ return onSnapshot(
114
+ q,
115
+ (snapshot) => {
116
+ const allCreations = snapshot.docs.map((docSnap) => {
117
+ const data = docSnap.data() as CreationDocument;
118
+ const creation = this.documentMapper(docSnap.id, data);
119
+
120
+ if (creation.deletedAt === undefined && data.deletedAt) {
121
+ const deletedAt =
122
+ data.deletedAt instanceof Date
123
+ ? data.deletedAt
124
+ : data.deletedAt &&
125
+ typeof data.deletedAt === "object" &&
126
+ "toDate" in data.deletedAt
127
+ ? (data.deletedAt as { toDate: () => Date }).toDate()
128
+ : undefined;
129
+ return { ...creation, deletedAt };
130
+ }
131
+
132
+ return creation;
133
+ });
134
+
135
+ const filtered = allCreations.filter((c) => !c.deletedAt);
136
+
137
+ if (__DEV__) {
138
+ console.log("[CreationsFetcher] Realtime update:", filtered.length);
139
+ }
140
+
141
+ onData(filtered);
142
+ },
143
+ (error) => {
144
+ if (__DEV__) {
145
+ console.error("[CreationsFetcher] subscribeToAll() ERROR", error);
146
+ }
147
+ onError?.(error);
148
+ },
149
+ );
150
+ }
94
151
  }
@@ -2,7 +2,11 @@
2
2
  if (typeof __DEV__ !== "undefined" && __DEV__) console.log("📍 [LIFECYCLE] CreationsRepository.ts - Module loading");
3
3
 
4
4
  import { BaseRepository, FirestorePathResolver } from "@umituz/react-native-firebase";
5
- import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
5
+ import type {
6
+ ICreationsRepository,
7
+ CreationsSubscriptionCallback,
8
+ UnsubscribeFunction,
9
+ } from "../../domain/repositories/ICreationsRepository";
6
10
  import type { Creation } from "../../domain/entities/Creation";
7
11
  import { mapDocumentToCreation } from "../../domain/entities/Creation";
8
12
  import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
@@ -66,6 +70,14 @@ export class CreationsRepository
66
70
  return this.fetcher.getById(userId, id);
67
71
  }
68
72
 
73
+ subscribeToAll(
74
+ userId: string,
75
+ onData: CreationsSubscriptionCallback,
76
+ onError?: (error: Error) => void,
77
+ ): UnsubscribeFunction {
78
+ return this.fetcher.subscribeToAll(userId, onData, onError);
79
+ }
80
+
69
81
  async create(userId: string, creation: Creation): Promise<void> {
70
82
  return this.writer.create(userId, creation);
71
83
  }
@@ -1,75 +1,44 @@
1
1
  /**
2
2
  * useCreationPersistence Hook
3
3
  * Encapsulates Firestore persistence logic for AI generation features
4
- * Eliminates boilerplate code in feature screens
4
+ * Realtime listener handles UI updates automatically
5
5
  */
6
6
 
7
7
  import { useCallback, useMemo } from "react";
8
- import { useQueryClient } from "@umituz/react-native-design-system";
9
8
  import { useAuth } from "@umituz/react-native-auth";
10
9
  import { createCreationsRepository } from "../../infrastructure/adapters";
11
10
  import type { Creation } from "../../domain/entities/Creation";
12
11
 
13
12
  declare const __DEV__: boolean;
14
13
 
15
- /**
16
- * Configuration for creation persistence
17
- */
18
14
  export interface UseCreationPersistenceConfig {
19
- /** Creation type identifier (e.g., "anime-selfie", "ai-kiss") */
20
15
  readonly type: string;
21
- /** Collection name in Firestore (defaults to "creations") */
22
16
  readonly collectionName?: string;
23
- /** Credit cost for this feature (passed to onCreditDeduct) */
24
17
  readonly creditCost?: number;
25
- /** Callback to deduct credits on successful processing */
26
18
  readonly onCreditDeduct?: (cost: number) => Promise<void | boolean>;
27
19
  }
28
20
 
29
- /**
30
- * Base processing start data - all features must have creationId
31
- */
32
21
  export interface BaseProcessingStartData {
33
22
  readonly creationId: string;
34
23
  }
35
24
 
36
- /**
37
- * Base processing result - all features should have creationId
38
- */
39
25
  export interface BaseProcessingResult {
40
26
  readonly creationId?: string;
41
27
  readonly imageUrl?: string;
42
28
  readonly videoUrl?: string;
43
29
  }
44
30
 
45
- /**
46
- * Return type for useCreationPersistence - uses generic callbacks
47
- */
48
31
  export interface UseCreationPersistenceReturn {
49
32
  readonly onProcessingStart: <T extends BaseProcessingStartData>(data: T) => void;
50
33
  readonly onProcessingComplete: <T extends BaseProcessingResult>(result: T) => void;
51
34
  readonly onError: (error: string, creationId?: string) => void;
52
35
  }
53
36
 
54
- /**
55
- * Hook that provides Firestore persistence callbacks for AI features
56
- *
57
- * @example
58
- * const { deductCredit } = useDeductCredit({ userId, onCreditsExhausted: openPaywall });
59
- * const persistence = useCreationPersistence({
60
- * type: "anime-selfie",
61
- * creditCost: AI_CREDIT_COST.ANIME_SELFIE,
62
- * onCreditDeduct: async (cost) => {
63
- * for (let i = 0; i < cost; i++) await deductCredit("image");
64
- * },
65
- * });
66
- */
67
37
  export function useCreationPersistence(
68
38
  config: UseCreationPersistenceConfig,
69
39
  ): UseCreationPersistenceReturn {
70
40
  const { type, collectionName = "creations", creditCost, onCreditDeduct } = config;
71
41
  const { userId } = useAuth();
72
- const queryClient = useQueryClient();
73
42
 
74
43
  const repository = useMemo(
75
44
  () => createCreationsRepository(collectionName),
@@ -78,12 +47,14 @@ export function useCreationPersistence(
78
47
 
79
48
  const onProcessingStart = useCallback(
80
49
  <T extends BaseProcessingStartData>(data: T) => {
81
- if (__DEV__) {
50
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
82
51
  console.log("[useCreationPersistence] onProcessingStart", { type, userId });
83
52
  }
84
53
 
85
54
  if (!userId) {
86
- if (__DEV__) console.log("[useCreationPersistence] No userId, skipping");
55
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
56
+ console.log("[useCreationPersistence] No userId, skipping");
57
+ }
87
58
  return;
88
59
  }
89
60
 
@@ -103,19 +74,18 @@ export function useCreationPersistence(
103
74
  metadata: cleanMetadata,
104
75
  };
105
76
 
106
- if (__DEV__) {
77
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
107
78
  console.log("[useCreationPersistence] Creating document", { creationId, type });
108
79
  }
109
80
 
110
81
  repository.create(userId, creation);
111
- queryClient.invalidateQueries({ queryKey: ["creations"] });
112
82
  },
113
- [userId, repository, queryClient, type],
83
+ [userId, repository, type],
114
84
  );
115
85
 
116
86
  const onProcessingComplete = useCallback(
117
87
  <T extends BaseProcessingResult>(result: T) => {
118
- if (__DEV__) {
88
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
119
89
  console.log("[useCreationPersistence] onProcessingComplete", {
120
90
  creationId: result.creationId,
121
91
  hasImageUrl: !!result.imageUrl,
@@ -124,7 +94,9 @@ export function useCreationPersistence(
124
94
  }
125
95
 
126
96
  if (!userId || !result.creationId) {
127
- if (__DEV__) console.log("[useCreationPersistence] Missing userId or creationId");
97
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
98
+ console.log("[useCreationPersistence] Missing userId or creationId");
99
+ }
128
100
  return;
129
101
  }
130
102
 
@@ -140,31 +112,31 @@ export function useCreationPersistence(
140
112
  status: "completed",
141
113
  output,
142
114
  });
143
- queryClient.invalidateQueries({ queryKey: ["creations"] });
144
115
 
145
- // Deduct credits via callback (app provides implementation)
146
116
  if (creditCost && creditCost > 0 && onCreditDeduct) {
147
- if (__DEV__) {
117
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
148
118
  console.log("[useCreationPersistence] Deducting credits", { cost: creditCost });
149
119
  }
150
120
  onCreditDeduct(creditCost).catch((err) => {
151
- if (__DEV__) {
121
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
152
122
  console.error("[useCreationPersistence] Credit deduction failed", err);
153
123
  }
154
124
  });
155
125
  }
156
126
  },
157
- [userId, repository, queryClient, creditCost, onCreditDeduct],
127
+ [userId, repository, creditCost, onCreditDeduct],
158
128
  );
159
129
 
160
130
  const onError = useCallback(
161
131
  (error: string, creationId?: string) => {
162
- if (__DEV__) {
132
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
163
133
  console.log("[useCreationPersistence] onError", { error, creationId });
164
134
  }
165
135
 
166
136
  if (!userId || !creationId) {
167
- if (__DEV__) console.log("[useCreationPersistence] Missing userId or creationId");
137
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
138
+ console.log("[useCreationPersistence] Missing userId or creationId");
139
+ }
168
140
  return;
169
141
  }
170
142
 
@@ -172,9 +144,8 @@ export function useCreationPersistence(
172
144
  status: "failed",
173
145
  metadata: { error },
174
146
  });
175
- queryClient.invalidateQueries({ queryKey: ["creations"] });
176
147
  },
177
- [userId, repository, queryClient],
148
+ [userId, repository],
178
149
  );
179
150
 
180
151
  return useMemo(
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * useCreationRating Hook
3
- * Handles rating of creations with optimistic update
3
+ * Handles rating of creations
4
+ * Realtime listener handles UI updates automatically
4
5
  */
5
6
 
6
- import { useMutation, useQueryClient } from "@umituz/react-native-design-system";
7
+ import { useState, useCallback } from "react";
7
8
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
8
- import type { Creation } from "../../domain/entities/Creation";
9
+
10
+ declare const __DEV__: boolean;
9
11
 
10
12
  interface UseCreationRatingProps {
11
13
  readonly userId: string | null;
@@ -15,41 +17,53 @@ interface UseCreationRatingProps {
15
17
  interface RatingVariables {
16
18
  readonly id: string;
17
19
  readonly rating: number;
20
+ readonly description?: string;
21
+ }
22
+
23
+ interface UseCreationRatingReturn {
24
+ readonly mutate: (variables: RatingVariables) => void;
25
+ readonly mutateAsync: (variables: RatingVariables) => Promise<boolean>;
26
+ readonly isPending: boolean;
18
27
  }
19
28
 
20
29
  export function useCreationRating({
21
30
  userId,
22
31
  repository,
23
- }: UseCreationRatingProps) {
24
- const queryClient = useQueryClient();
25
- const queryKey = ["creations", userId ?? ""];
32
+ }: UseCreationRatingProps): UseCreationRatingReturn {
33
+ const [isPending, setIsPending] = useState(false);
26
34
 
27
- return useMutation({
28
- mutationFn: async ({ id, rating }: RatingVariables) => {
35
+ const mutateAsync = useCallback(
36
+ async ({ id, rating, description }: RatingVariables): Promise<boolean> => {
29
37
  if (!userId) return false;
30
- return repository.rate(userId, id, rating);
31
- },
32
- onMutate: async ({ id, rating }: RatingVariables) => {
33
- await queryClient.cancelQueries({ queryKey });
34
- const previousData = queryClient.getQueryData<Creation[]>(queryKey);
35
-
36
- if (previousData) {
37
- queryClient.setQueryData<Creation[]>(queryKey, (old) =>
38
- old?.map((c) =>
39
- c.id === id ? { ...c, rating, ratedAt: new Date() } : c
40
- ) ?? []
41
- );
42
- }
43
38
 
44
- return { previousData };
45
- },
46
- onError: (_error, _variables, context) => {
47
- if (context?.previousData) {
48
- queryClient.setQueryData(queryKey, context.previousData);
39
+ setIsPending(true);
40
+ try {
41
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
42
+ console.log("[useCreationRating] Rating:", { id, rating });
43
+ }
44
+ const result = await repository.rate(userId, id, rating, description);
45
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
46
+ console.log("[useCreationRating] Rate result:", result);
47
+ }
48
+ return result;
49
+ } catch (error) {
50
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
51
+ console.error("[useCreationRating] Error:", error);
52
+ }
53
+ return false;
54
+ } finally {
55
+ setIsPending(false);
49
56
  }
50
57
  },
51
- onSettled: () => {
52
- void queryClient.invalidateQueries({ queryKey });
58
+ [userId, repository],
59
+ );
60
+
61
+ const mutate = useCallback(
62
+ (variables: RatingVariables): void => {
63
+ void mutateAsync(variables);
53
64
  },
54
- });
65
+ [mutateAsync],
66
+ );
67
+
68
+ return { mutate, mutateAsync, isPending };
55
69
  }
@@ -1,16 +1,14 @@
1
1
  /**
2
2
  * useCreations Hook
3
- * Fetches user's creations from repository
3
+ * Realtime Firestore listener for user's creations
4
+ * Auto-updates UI when Firestore data changes
4
5
  */
5
6
 
6
- import { useQuery } from "@umituz/react-native-design-system";
7
+ import { useState, useEffect, useCallback } from "react";
7
8
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
8
9
  import type { Creation } from "../../domain/entities/Creation";
9
10
 
10
- const CACHE_CONFIG = {
11
- staleTime: 5 * 60 * 1000, // 5 minutes - use cache invalidation on mutations
12
- gcTime: 30 * 60 * 1000,
13
- };
11
+ declare const __DEV__: boolean;
14
12
 
15
13
  interface UseCreationsProps {
16
14
  readonly userId: string | null;
@@ -18,21 +16,68 @@ interface UseCreationsProps {
18
16
  readonly enabled?: boolean;
19
17
  }
20
18
 
19
+ interface UseCreationsReturn {
20
+ readonly data: Creation[] | undefined;
21
+ readonly isLoading: boolean;
22
+ readonly error: Error | null;
23
+ readonly refetch: () => void;
24
+ }
25
+
21
26
  export function useCreations({
22
27
  userId,
23
28
  repository,
24
29
  enabled = true,
25
- }: UseCreationsProps) {
26
- return useQuery<Creation[]>({
27
- queryKey: ["creations", userId ?? ""],
28
- queryFn: async () => {
29
- if (!userId) {
30
- return [];
30
+ }: UseCreationsProps): UseCreationsReturn {
31
+ const [data, setData] = useState<Creation[] | undefined>(undefined);
32
+ const [isLoading, setIsLoading] = useState(true);
33
+ const [error, setError] = useState<Error | null>(null);
34
+
35
+ const refetch = useCallback(() => {
36
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
37
+ console.log("[useCreations] refetch() - realtime listener handles updates");
38
+ }
39
+ }, []);
40
+
41
+ useEffect(() => {
42
+ if (!userId || !enabled) {
43
+ setData([]);
44
+ setIsLoading(false);
45
+ return;
46
+ }
47
+
48
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
49
+ console.log("[useCreations] Setting up realtime listener", { userId });
50
+ }
51
+
52
+ setIsLoading(true);
53
+ setError(null);
54
+
55
+ const unsubscribe = repository.subscribeToAll(
56
+ userId,
57
+ (creations) => {
58
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
59
+ console.log("[useCreations] Realtime update:", creations.length);
60
+ }
61
+ setData(creations);
62
+ setIsLoading(false);
63
+ setError(null);
64
+ },
65
+ (err) => {
66
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
67
+ console.error("[useCreations] Realtime listener error:", err);
68
+ }
69
+ setError(err);
70
+ setIsLoading(false);
71
+ },
72
+ );
73
+
74
+ return () => {
75
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
76
+ console.log("[useCreations] Cleaning up realtime listener");
31
77
  }
32
- return repository.getAll(userId);
33
- },
34
- enabled: !!userId && enabled,
35
- staleTime: CACHE_CONFIG.staleTime,
36
- gcTime: CACHE_CONFIG.gcTime,
37
- });
78
+ unsubscribe();
79
+ };
80
+ }, [userId, repository, enabled]);
81
+
82
+ return { data, isLoading, error, refetch };
38
83
  }
@@ -1,48 +1,63 @@
1
1
  /**
2
2
  * useDeleteCreation Hook
3
- * Handles deletion of user creations with optimistic update
3
+ * Handles deletion of user creations
4
+ * Realtime listener handles UI updates automatically
4
5
  */
5
6
 
6
- import { useMutation, useQueryClient } from "@umituz/react-native-design-system";
7
+ import { useState, useCallback } from "react";
7
8
  import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
8
- import type { Creation } from "../../domain/entities/Creation";
9
+
10
+ declare const __DEV__: boolean;
9
11
 
10
12
  interface UseDeleteCreationProps {
11
13
  readonly userId: string | null;
12
14
  readonly repository: ICreationsRepository;
13
15
  }
14
16
 
17
+ interface UseDeleteCreationReturn {
18
+ readonly mutate: (creationId: string) => void;
19
+ readonly mutateAsync: (creationId: string) => Promise<boolean>;
20
+ readonly isPending: boolean;
21
+ }
22
+
15
23
  export function useDeleteCreation({
16
24
  userId,
17
25
  repository,
18
- }: UseDeleteCreationProps) {
19
- const queryClient = useQueryClient();
20
- const queryKey = ["creations", userId ?? ""];
26
+ }: UseDeleteCreationProps): UseDeleteCreationReturn {
27
+ const [isPending, setIsPending] = useState(false);
21
28
 
22
- return useMutation({
23
- mutationFn: async (creationId: string) => {
29
+ const mutateAsync = useCallback(
30
+ async (creationId: string): Promise<boolean> => {
24
31
  if (!userId) return false;
25
- return repository.delete(userId, creationId);
26
- },
27
- onMutate: async (creationId: string) => {
28
- await queryClient.cancelQueries({ queryKey });
29
- const previousData = queryClient.getQueryData<Creation[]>(queryKey);
30
-
31
- if (previousData) {
32
- queryClient.setQueryData<Creation[]>(queryKey, (old: Creation[] | undefined) =>
33
- old?.filter((c: Creation) => c.id !== creationId) ?? []
34
- );
35
- }
36
32
 
37
- return { previousData };
38
- },
39
- onError: (_error: Error, _variables: string, context: { previousData?: Creation[] } | undefined) => {
40
- if (context?.previousData) {
41
- queryClient.setQueryData(queryKey, context.previousData);
33
+ setIsPending(true);
34
+ try {
35
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
36
+ console.log("[useDeleteCreation] Deleting:", creationId);
37
+ }
38
+ const result = await repository.delete(userId, creationId);
39
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
40
+ console.log("[useDeleteCreation] Delete result:", result);
41
+ }
42
+ return result;
43
+ } catch (error) {
44
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
45
+ console.error("[useDeleteCreation] Error:", error);
46
+ }
47
+ return false;
48
+ } finally {
49
+ setIsPending(false);
42
50
  }
43
51
  },
44
- onSettled: () => {
45
- void queryClient.invalidateQueries({ queryKey });
52
+ [userId, repository],
53
+ );
54
+
55
+ const mutate = useCallback(
56
+ (creationId: string): void => {
57
+ void mutateAsync(creationId);
46
58
  },
47
- });
59
+ [mutateAsync],
60
+ );
61
+
62
+ return { mutate, mutateAsync, isPending };
48
63
  }