@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/hooks.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { getAuthContext, getAuthMode } from "./client.js";
|
|
4
4
|
import { isKeysetPagination, isOffsetPagination } from "./api.js";
|
|
5
5
|
import { DEFAULT_QUERY_CONFIG, createCacheUtils, createDetailQuery, createInfiniteListQuery, createListQuery, createQueryKeys, getItemId, updateListCache } from "./query.js";
|
|
6
|
-
import { createOptimisticMutation } from "./mutation.js";
|
|
7
|
-
import { useQueryClient } from "@tanstack/react-query";
|
|
8
|
-
import { useCallback } from "react";
|
|
6
|
+
import { createOptimisticMutation, useMutationWithTransition } from "./mutation.js";
|
|
7
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
8
|
+
import { useCallback, useRef } from "react";
|
|
9
9
|
|
|
10
10
|
//#region src/hooks.ts
|
|
11
11
|
let useRouterHook = null;
|
|
@@ -23,7 +23,8 @@ function createEnabledRule(token, options) {
|
|
|
23
23
|
if (getAuthMode() === "cookie" || options.public) return options.enabled ?? true;
|
|
24
24
|
return options.enabled !== void 0 ? options.enabled && !!token : !!token;
|
|
25
25
|
}
|
|
26
|
-
function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks = {}, client }) {
|
|
26
|
+
function createCrudHooks({ api, entityKey, singular, platformScoped, defaults = {}, callbacks = {}, client }) {
|
|
27
|
+
const isPlatform = platformScoped ?? api.scope === "platform";
|
|
27
28
|
const KEYS = createQueryKeys(entityKey);
|
|
28
29
|
const cache = createCacheUtils(KEYS);
|
|
29
30
|
const instanceToast = client?.toast;
|
|
@@ -55,24 +56,27 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
55
56
|
token = auth.token;
|
|
56
57
|
params = tokenOrParams ?? {};
|
|
57
58
|
options = paramsOrOptions ?? {};
|
|
58
|
-
if (auth.organizationId && !params.organizationId) params = {
|
|
59
|
+
if (!isPlatform && auth.organizationId && !params.organizationId) params = {
|
|
59
60
|
...params,
|
|
60
61
|
organizationId: auth.organizationId
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
const { organizationId, ...restParams } = params;
|
|
64
|
-
const scope = options._scope || (organizationId ? "tenant" : "super-admin");
|
|
65
|
+
const scope = options._scope || (isPlatform ? "platform" : organizationId ? "tenant" : "super-admin");
|
|
65
66
|
const { request: requestOpts, ...queryOpts } = options;
|
|
66
67
|
return createListQuery({
|
|
67
68
|
queryKey: KEYS.scopedList(scope, {
|
|
68
69
|
organizationId,
|
|
69
70
|
...restParams
|
|
70
71
|
}),
|
|
71
|
-
queryFn: () => api.getAll({
|
|
72
|
+
queryFn: ({ signal }) => api.getAll({
|
|
72
73
|
token,
|
|
73
74
|
organizationId,
|
|
74
75
|
params: restParams,
|
|
75
|
-
options: {
|
|
76
|
+
options: {
|
|
77
|
+
signal,
|
|
78
|
+
...requestOpts
|
|
79
|
+
}
|
|
76
80
|
}),
|
|
77
81
|
enabled: createEnabledRule(token, queryOpts),
|
|
78
82
|
options: {
|
|
@@ -84,7 +88,8 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
84
88
|
refetchIntervalInBackground: queryOpts.refetchIntervalInBackground
|
|
85
89
|
},
|
|
86
90
|
prefillDetailCache: queryOpts.prefillDetailCache ?? true,
|
|
87
|
-
detailKeyBuilder: (id) => KEYS.detail(id)
|
|
91
|
+
detailKeyBuilder: (id) => KEYS.detail(id),
|
|
92
|
+
select: queryOpts.select
|
|
88
93
|
});
|
|
89
94
|
}
|
|
90
95
|
function useDetail(id, tokenOrOptions, maybeOptions) {
|
|
@@ -97,7 +102,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
97
102
|
const auth = getAuthContext();
|
|
98
103
|
token = auth.token;
|
|
99
104
|
options = tokenOrOptions ?? {};
|
|
100
|
-
if (auth.organizationId && !options.organizationId) options = {
|
|
105
|
+
if (!isPlatform && auth.organizationId && !options.organizationId) options = {
|
|
101
106
|
...options,
|
|
102
107
|
organizationId: auth.organizationId
|
|
103
108
|
};
|
|
@@ -105,12 +110,15 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
105
110
|
const { organizationId, params: queryParams, request: requestOpts, ...restOptions } = options;
|
|
106
111
|
return createDetailQuery({
|
|
107
112
|
queryKey: queryParams ? [...KEYS.detail(id || ""), queryParams] : KEYS.detail(id || ""),
|
|
108
|
-
queryFn: () => api.getById({
|
|
113
|
+
queryFn: ({ signal }) => api.getById({
|
|
109
114
|
id,
|
|
110
115
|
token,
|
|
111
116
|
organizationId,
|
|
112
117
|
params: queryParams,
|
|
113
|
-
options: {
|
|
118
|
+
options: {
|
|
119
|
+
signal,
|
|
120
|
+
...requestOpts
|
|
121
|
+
}
|
|
114
122
|
}),
|
|
115
123
|
enabled: !!id && createEnabledRule(token, restOptions),
|
|
116
124
|
options: {
|
|
@@ -119,11 +127,14 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
119
127
|
structuralSharing: restOptions.structuralSharing ?? config.structuralSharing,
|
|
120
128
|
refetchInterval: restOptions.refetchInterval,
|
|
121
129
|
refetchIntervalInBackground: restOptions.refetchIntervalInBackground
|
|
122
|
-
}
|
|
130
|
+
},
|
|
131
|
+
select: restOptions.select
|
|
123
132
|
});
|
|
124
133
|
}
|
|
125
134
|
function useActions() {
|
|
126
135
|
const queryClient = useQueryClient();
|
|
136
|
+
const silentRef = useRef(false);
|
|
137
|
+
const shouldToast = useCallback(() => !silentRef.current, []);
|
|
127
138
|
const createMutation = createOptimisticMutation({
|
|
128
139
|
mutationFn: ({ token, organizationId, data }) => api.create({
|
|
129
140
|
token,
|
|
@@ -132,6 +143,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
132
143
|
}),
|
|
133
144
|
queryClient,
|
|
134
145
|
queryKeys: [KEYS.lists()],
|
|
146
|
+
shouldToast,
|
|
135
147
|
optimisticUpdate: (oldData, { data }) => {
|
|
136
148
|
const optimisticItem = {
|
|
137
149
|
...data,
|
|
@@ -146,6 +158,9 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
146
158
|
onError: (error, variables) => {
|
|
147
159
|
callbacks.onCreate?.onError?.(error, { data: variables.data }, void 0);
|
|
148
160
|
},
|
|
161
|
+
onSettled: (data, error, variables) => {
|
|
162
|
+
callbacks.onCreate?.onSettled?.(data, error, { data: variables.data }, void 0);
|
|
163
|
+
},
|
|
149
164
|
messages: {
|
|
150
165
|
success: config.messages.createSuccess,
|
|
151
166
|
error: config.messages.createError
|
|
@@ -161,6 +176,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
161
176
|
}),
|
|
162
177
|
queryClient,
|
|
163
178
|
queryKeys: [KEYS.lists()],
|
|
179
|
+
shouldToast,
|
|
164
180
|
optimisticUpdate: (oldData, { id, data }) => {
|
|
165
181
|
const updated = updateListCache(oldData, (arr) => (arr || []).map((item) => getItemId(item) === id ? {
|
|
166
182
|
...item,
|
|
@@ -175,11 +191,11 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
175
191
|
} : current);
|
|
176
192
|
return updated;
|
|
177
193
|
},
|
|
178
|
-
onSuccess: (data, { id }) => {
|
|
194
|
+
onSuccess: (data, { id, data: updateData }) => {
|
|
179
195
|
queryClient.invalidateQueries({ queryKey: KEYS.detail(id) });
|
|
180
196
|
callbacks.onUpdate?.onSuccess?.(data, {
|
|
181
197
|
id,
|
|
182
|
-
data:
|
|
198
|
+
data: updateData
|
|
183
199
|
}, void 0);
|
|
184
200
|
},
|
|
185
201
|
onError: (error, { id, data }) => {
|
|
@@ -188,6 +204,12 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
188
204
|
data
|
|
189
205
|
}, void 0);
|
|
190
206
|
},
|
|
207
|
+
onSettled: (data, error, { id, data: updateData }) => {
|
|
208
|
+
callbacks.onUpdate?.onSettled?.(data, error, {
|
|
209
|
+
id,
|
|
210
|
+
data: updateData
|
|
211
|
+
}, void 0);
|
|
212
|
+
},
|
|
191
213
|
messages: {
|
|
192
214
|
success: config.messages.updateSuccess,
|
|
193
215
|
error: config.messages.updateError
|
|
@@ -202,6 +224,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
202
224
|
}),
|
|
203
225
|
queryClient,
|
|
204
226
|
queryKeys: [KEYS.lists()],
|
|
227
|
+
shouldToast,
|
|
205
228
|
optimisticUpdate: (oldData, { id }) => {
|
|
206
229
|
queryClient.removeQueries({ queryKey: KEYS.detail(id) });
|
|
207
230
|
return updateListCache(oldData, (arr) => (arr || []).filter((item) => getItemId(item) !== id));
|
|
@@ -212,6 +235,9 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
212
235
|
onError: (error, { id }) => {
|
|
213
236
|
callbacks.onDelete?.onError?.(error, { id }, void 0);
|
|
214
237
|
},
|
|
238
|
+
onSettled: (data, error, { id }) => {
|
|
239
|
+
callbacks.onDelete?.onSettled?.(data, error, { id }, void 0);
|
|
240
|
+
},
|
|
215
241
|
messages: {
|
|
216
242
|
success: config.messages.deleteSuccess,
|
|
217
243
|
error: config.messages.deleteError
|
|
@@ -223,37 +249,52 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
223
249
|
return {
|
|
224
250
|
...params,
|
|
225
251
|
token: params.token ?? auth.token,
|
|
226
|
-
organizationId: params.organizationId ?? auth.organizationId
|
|
252
|
+
organizationId: isPlatform ? params.organizationId ?? null : params.organizationId ?? auth.organizationId
|
|
227
253
|
};
|
|
228
254
|
};
|
|
229
255
|
const create = async (params, options) => {
|
|
256
|
+
silentRef.current = options?.silent ?? false;
|
|
230
257
|
try {
|
|
231
258
|
const result = await createMutation.mutateAsync(resolveAuth(params));
|
|
232
259
|
options?.onSuccess?.(result);
|
|
260
|
+
options?.onSettled?.(result, null);
|
|
233
261
|
return result;
|
|
234
262
|
} catch (error) {
|
|
235
263
|
options?.onError?.(error);
|
|
264
|
+
options?.onSettled?.(void 0, error);
|
|
236
265
|
throw error;
|
|
266
|
+
} finally {
|
|
267
|
+
silentRef.current = false;
|
|
237
268
|
}
|
|
238
269
|
};
|
|
239
270
|
const update = async (params, options) => {
|
|
271
|
+
silentRef.current = options?.silent ?? false;
|
|
240
272
|
try {
|
|
241
273
|
const result = await updateMutation.mutateAsync(resolveAuth(params));
|
|
242
274
|
options?.onSuccess?.(result);
|
|
275
|
+
options?.onSettled?.(result, null);
|
|
243
276
|
return result;
|
|
244
277
|
} catch (error) {
|
|
245
278
|
options?.onError?.(error);
|
|
279
|
+
options?.onSettled?.(void 0, error);
|
|
246
280
|
throw error;
|
|
281
|
+
} finally {
|
|
282
|
+
silentRef.current = false;
|
|
247
283
|
}
|
|
248
284
|
};
|
|
249
285
|
const remove = async (params, options) => {
|
|
286
|
+
silentRef.current = options?.silent ?? false;
|
|
250
287
|
try {
|
|
251
288
|
const result = await deleteMutation.mutateAsync(resolveAuth(params));
|
|
252
289
|
options?.onSuccess?.(result);
|
|
290
|
+
options?.onSettled?.(result, null);
|
|
253
291
|
return result;
|
|
254
292
|
} catch (error) {
|
|
255
293
|
options?.onError?.(error);
|
|
294
|
+
options?.onSettled?.(void 0, error);
|
|
256
295
|
throw error;
|
|
296
|
+
} finally {
|
|
297
|
+
silentRef.current = false;
|
|
257
298
|
}
|
|
258
299
|
};
|
|
259
300
|
return {
|
|
@@ -279,7 +320,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
279
320
|
token = auth.token;
|
|
280
321
|
params = tokenOrParams ?? {};
|
|
281
322
|
options = paramsOrOptions ?? {};
|
|
282
|
-
if (auth.organizationId && !params.organizationId) params = {
|
|
323
|
+
if (!isPlatform && auth.organizationId && !params.organizationId) params = {
|
|
283
324
|
...params,
|
|
284
325
|
organizationId: auth.organizationId
|
|
285
326
|
};
|
|
@@ -292,7 +333,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
292
333
|
organizationId,
|
|
293
334
|
...restParams
|
|
294
335
|
}), "infinite"],
|
|
295
|
-
queryFn: ({ pageParam }) => {
|
|
336
|
+
queryFn: ({ pageParam, signal }) => {
|
|
296
337
|
const paginationParams = typeof pageParam === "string" ? { after: pageParam } : { page: pageParam };
|
|
297
338
|
return api.getAll({
|
|
298
339
|
token,
|
|
@@ -301,7 +342,10 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
301
342
|
...restParams,
|
|
302
343
|
...paginationParams
|
|
303
344
|
},
|
|
304
|
-
options: {
|
|
345
|
+
options: {
|
|
346
|
+
signal,
|
|
347
|
+
...requestOpts
|
|
348
|
+
}
|
|
305
349
|
});
|
|
306
350
|
},
|
|
307
351
|
enabled: createEnabledRule(token, queryOpts),
|
|
@@ -321,6 +365,81 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
321
365
|
}
|
|
322
366
|
});
|
|
323
367
|
}
|
|
368
|
+
function useUpload(options) {
|
|
369
|
+
if (!api.upload) throw new Error(`[arc-next] "${entityKey}" api does not define an upload method`);
|
|
370
|
+
const uploadApi = api.upload;
|
|
371
|
+
return useMutationWithTransition({
|
|
372
|
+
mutationFn: ({ data, id, path }) => {
|
|
373
|
+
const auth = getAuthContext();
|
|
374
|
+
return uploadApi({
|
|
375
|
+
token: auth.token,
|
|
376
|
+
organizationId: isPlatform ? null : auth.organizationId,
|
|
377
|
+
data,
|
|
378
|
+
id,
|
|
379
|
+
path
|
|
380
|
+
});
|
|
381
|
+
},
|
|
382
|
+
invalidateQueries: options?.invalidateQueries ?? [KEYS.lists()],
|
|
383
|
+
onSuccess: (data) => options?.onSuccess?.(data),
|
|
384
|
+
onError: (error) => options?.onError?.(error),
|
|
385
|
+
onSettled: (data, error) => options?.onSettled?.(data, error),
|
|
386
|
+
messages: options?.messages ?? {
|
|
387
|
+
success: `${singular} uploaded successfully`,
|
|
388
|
+
error: `Failed to upload ${singular.toLowerCase()}`
|
|
389
|
+
},
|
|
390
|
+
toastHandler: instanceToast
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
function useSearch(query, params, options) {
|
|
394
|
+
if (!api.search) throw new Error(`[arc-next] "${entityKey}" api does not define a search method`);
|
|
395
|
+
const searchApi = api.search;
|
|
396
|
+
const auth = getAuthContext();
|
|
397
|
+
const token = auth.token;
|
|
398
|
+
const organizationId = platformScoped ? null : params?.organizationId ?? auth.organizationId;
|
|
399
|
+
const { organizationId: _, ...restParams } = params ?? {};
|
|
400
|
+
const searchParams = {
|
|
401
|
+
q: query,
|
|
402
|
+
...restParams
|
|
403
|
+
};
|
|
404
|
+
const { request: requestOpts, ...queryOpts } = options ?? {};
|
|
405
|
+
const searchKeyParams = organizationId ? {
|
|
406
|
+
organizationId,
|
|
407
|
+
...searchParams
|
|
408
|
+
} : searchParams;
|
|
409
|
+
return createListQuery({
|
|
410
|
+
queryKey: [
|
|
411
|
+
...KEYS.lists(),
|
|
412
|
+
"search",
|
|
413
|
+
searchKeyParams
|
|
414
|
+
],
|
|
415
|
+
queryFn: ({ signal }) => searchApi({
|
|
416
|
+
token,
|
|
417
|
+
organizationId,
|
|
418
|
+
params: searchParams,
|
|
419
|
+
options: {
|
|
420
|
+
signal,
|
|
421
|
+
...requestOpts
|
|
422
|
+
}
|
|
423
|
+
}),
|
|
424
|
+
enabled: query.length > 0 && createEnabledRule(token, queryOpts),
|
|
425
|
+
options: {
|
|
426
|
+
staleTime: queryOpts.staleTime ?? config.staleTime,
|
|
427
|
+
gcTime: queryOpts.gcTime ?? config.gcTime
|
|
428
|
+
},
|
|
429
|
+
select: queryOpts.select
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function useCustomMutation(mutationConfig) {
|
|
433
|
+
return useMutationWithTransition({
|
|
434
|
+
mutationFn: mutationConfig.mutationFn,
|
|
435
|
+
invalidateQueries: mutationConfig.invalidateQueries ?? [KEYS.lists()],
|
|
436
|
+
onSuccess: mutationConfig.onSuccess,
|
|
437
|
+
onError: mutationConfig.onError,
|
|
438
|
+
onSettled: mutationConfig.onSettled,
|
|
439
|
+
messages: mutationConfig.messages,
|
|
440
|
+
toastHandler: instanceToast
|
|
441
|
+
});
|
|
442
|
+
}
|
|
324
443
|
function useNavigation() {
|
|
325
444
|
const queryClient = useQueryClient();
|
|
326
445
|
const router = (instanceNavigation ?? useRouterHook)?.();
|
|
@@ -340,6 +459,9 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
|
|
|
340
459
|
useDetail,
|
|
341
460
|
useInfiniteList,
|
|
342
461
|
useActions,
|
|
462
|
+
useUpload,
|
|
463
|
+
useSearch,
|
|
464
|
+
useCustomMutation,
|
|
343
465
|
useNavigation
|
|
344
466
|
};
|
|
345
467
|
}
|
package/dist/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["\"use client\";\n\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { useCallback } from \"react\";\nimport { createOptimisticMutation } from \"./mutation.js\";\nimport {\n createListQuery,\n createDetailQuery,\n createInfiniteListQuery,\n updateListCache,\n getItemId,\n createQueryKeys,\n createCacheUtils,\n DEFAULT_QUERY_CONFIG,\n} from \"./query.js\";\nimport type {\n QueryKeys,\n CacheUtils,\n ListQueryOptions,\n DetailQueryOptions,\n ListQueryResult,\n DetailQueryResult,\n InfiniteListQueryOptions,\n InfiniteListQueryResult,\n} from \"./query.js\";\nimport {\n isOffsetPagination,\n isKeysetPagination,\n} from \"./api.js\";\nimport type { PaginatedResponse } from \"./api.js\";\nimport type { MutationCallbacks } from \"./mutation.js\";\nimport { getAuthMode, getAuthContext } from \"./client.js\";\nimport type { ArcClient, ToastHandler, UseRouterHook } from \"./client.js\";\n\n// Re-export UseRouterHook for backward compatibility\nexport type { UseRouterHook } from \"./client.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CrudApi<T, TCreate = Partial<T>, TUpdate = Partial<T>> {\n getAll: (options: {\n token?: string | null;\n organizationId?: string | null;\n params?: Record<string, unknown>;\n options?: { signal?: AbortSignal; [key: string]: unknown };\n }) => Promise<unknown>;\n\n getById: (options: {\n id: string;\n token?: string | null;\n organizationId?: string | null;\n params?: { select?: string; populate?: string | string[] };\n options?: { signal?: AbortSignal; [key: string]: unknown };\n }) => Promise<unknown>;\n\n create: (options: {\n token?: string | null;\n organizationId?: string | null;\n data: TCreate;\n }) => Promise<unknown>;\n\n update: (options: {\n token?: string | null;\n organizationId?: string | null;\n id: string;\n data: TUpdate;\n }) => Promise<unknown>;\n\n delete: (options: {\n token?: string | null;\n organizationId?: string | null;\n id: string;\n }) => Promise<unknown>;\n}\n\nexport interface CrudHooksConfig<T, TCreate = Partial<T>, TUpdate = Partial<T>> {\n api: CrudApi<T, TCreate, TUpdate>;\n entityKey: string;\n singular: string;\n plural?: string;\n defaults?: {\n staleTime?: number;\n gcTime?: number;\n refetchOnWindowFocus?: boolean;\n structuralSharing?: boolean;\n messages?: {\n createSuccess?: string;\n createError?: string;\n updateSuccess?: string;\n updateError?: string;\n deleteSuccess?: string;\n deleteError?: string;\n };\n };\n callbacks?: {\n onCreate?: MutationCallbacks<T, { data: TCreate }>;\n onUpdate?: MutationCallbacks<T, { id: string; data: TUpdate }>;\n onDelete?: MutationCallbacks<unknown, { id: string }>;\n };\n client?: ArcClient;\n}\n\nexport interface MutationParams<TData> {\n token?: string | null;\n organizationId?: string | null;\n data: TData;\n}\n\nexport interface UpdateParams<TData> extends MutationParams<TData> {\n id: string;\n}\n\nexport interface DeleteParams {\n token?: string | null;\n organizationId?: string | null;\n id: string;\n}\n\nexport interface CallOptions<TData = unknown> {\n onSuccess?: (data: TData) => void;\n onError?: (error: Error) => void;\n silent?: boolean;\n}\n\nexport interface CrudActions<T, TCreate, TUpdate> {\n create: (params: MutationParams<TCreate>, options?: CallOptions<T>) => Promise<T>;\n update: (params: UpdateParams<TUpdate>, options?: CallOptions<T>) => Promise<T>;\n remove: (params: DeleteParams, options?: CallOptions) => Promise<unknown>;\n isCreating: boolean;\n isUpdating: boolean;\n isDeleting: boolean;\n isMutating: boolean;\n}\n\nexport interface NavigationOptions {\n scroll?: boolean;\n replace?: boolean;\n}\n\nexport type NavigateFn<T> = (href: string, item: T, options?: NavigationOptions) => void;\n\nexport interface CrudHooksReturn<T, TCreate, TUpdate> {\n KEYS: QueryKeys;\n cache: CacheUtils<T>;\n useList: {\n /** New signature — auto-injects token/orgId from configureAuth() context */\n (params?: Record<string, unknown>, options?: ListQueryOptions): ListQueryResult<T>;\n /** Legacy signature — explicit token */\n (token: string | null, params?: Record<string, unknown>, options?: ListQueryOptions): ListQueryResult<T>;\n };\n useDetail: {\n /** New signature — auto-injects token from configureAuth() context */\n (id: string | null, options?: DetailQueryOptions): DetailQueryResult<T>;\n /** Legacy signature — explicit token */\n (id: string | null, token: string | null, options?: DetailQueryOptions): DetailQueryResult<T>;\n };\n useInfiniteList: {\n /** New signature — auto-injects token/orgId from configureAuth() context */\n (params?: Record<string, unknown>, options?: InfiniteListQueryOptions): InfiniteListQueryResult<T>;\n /** Legacy signature — explicit token */\n (token: string | null, params?: Record<string, unknown>, options?: InfiniteListQueryOptions): InfiniteListQueryResult<T>;\n };\n useActions: () => CrudActions<T, TCreate, TUpdate>;\n useNavigation: () => NavigateFn<T>;\n}\n\n// ============================================================================\n// Navigation Configuration (Global)\n// ============================================================================\n\nlet useRouterHook: UseRouterHook | null = null;\n\n/**\n * Configure the router hook for useNavigation. Call once at app init.\n *\n * @example\n * import { useRouter } from \"next/navigation\";\n * configureNavigation(useRouter);\n */\nexport function configureNavigation(hook: UseRouterHook): void {\n useRouterHook = hook;\n}\n\n// ============================================================================\n// Enabled Rule\n// ============================================================================\n\nfunction createEnabledRule(token: string | null, options: { public?: boolean; enabled?: boolean }): boolean {\n // Cookie-based auth: no token needed, queries always enabled (cookies sent via credentials: 'include')\n if (getAuthMode() === 'cookie' || options.public) {\n return options.enabled ?? true;\n }\n // Bearer token auth: require token to enable queries\n return options.enabled !== undefined ? options.enabled && !!token : !!token;\n}\n\n// ============================================================================\n// Create CRUD Hooks Factory\n// ============================================================================\n\nexport function createCrudHooks<T, TCreate = Partial<T>, TUpdate = Partial<T>>({\n api,\n entityKey,\n singular,\n defaults = {},\n callbacks = {},\n client,\n}: CrudHooksConfig<T, TCreate, TUpdate>): CrudHooksReturn<T, TCreate, TUpdate> {\n const KEYS = createQueryKeys(entityKey);\n const cache = createCacheUtils<T>(KEYS);\n\n // Resolve toast handler: client instance → global\n const instanceToast: ToastHandler | undefined = client?.toast;\n // Resolve navigation hook: client instance → global\n const instanceNavigation: UseRouterHook | null = client?.navigation ?? null;\n\n const config = {\n ...DEFAULT_QUERY_CONFIG,\n ...defaults,\n structuralSharing: defaults.structuralSharing,\n messages: {\n createSuccess: `${singular} created successfully`,\n createError: `Failed to create ${singular.toLowerCase()}`,\n updateSuccess: `${singular} updated successfully`,\n updateError: `Failed to update ${singular.toLowerCase()}`,\n deleteSuccess: `${singular} deleted successfully`,\n deleteError: `Failed to delete ${singular.toLowerCase()}`,\n ...defaults.messages,\n },\n };\n\n // ========== useList ==========\n\n function useList(\n tokenOrParams?: string | null | Record<string, unknown>,\n paramsOrOptions?: Record<string, unknown> | ListQueryOptions,\n maybeOptions?: ListQueryOptions,\n ): ListQueryResult<T> {\n let token: string | null;\n let params: Record<string, unknown>;\n let options: ListQueryOptions;\n\n // Detect which overload: first arg is string|null = legacy, object/undefined = new\n if (tokenOrParams === null || typeof tokenOrParams === 'string') {\n token = tokenOrParams;\n params = (paramsOrOptions as Record<string, unknown>) ?? {};\n options = maybeOptions ?? {};\n } else {\n const auth = getAuthContext();\n token = auth.token;\n params = (tokenOrParams as Record<string, unknown>) ?? {};\n options = (paramsOrOptions as ListQueryOptions) ?? {};\n if (auth.organizationId && !params.organizationId) {\n params = { ...params, organizationId: auth.organizationId };\n }\n }\n\n const { organizationId, ...restParams } = params;\n const scope = options._scope || (organizationId ? \"tenant\" : \"super-admin\");\n const { request: requestOpts, ...queryOpts } = options;\n\n return createListQuery<T>({\n queryKey: KEYS.scopedList(scope, { organizationId, ...restParams }),\n queryFn: () => api.getAll({ token, organizationId: organizationId as string | null, params: restParams, options: { ...requestOpts } }),\n enabled: createEnabledRule(token, queryOpts),\n options: {\n staleTime: queryOpts.staleTime ?? config.staleTime,\n gcTime: queryOpts.gcTime ?? config.gcTime,\n refetchOnWindowFocus: queryOpts.refetchOnWindowFocus ?? config.refetchOnWindowFocus,\n structuralSharing: queryOpts.structuralSharing ?? config.structuralSharing,\n refetchInterval: queryOpts.refetchInterval,\n refetchIntervalInBackground: queryOpts.refetchIntervalInBackground,\n },\n prefillDetailCache: queryOpts.prefillDetailCache ?? true,\n detailKeyBuilder: (id) => KEYS.detail(id),\n });\n }\n\n // ========== useDetail ==========\n\n function useDetail(\n id: string | null,\n tokenOrOptions?: string | null | DetailQueryOptions,\n maybeOptions?: DetailQueryOptions,\n ): DetailQueryResult<T> {\n let token: string | null;\n let options: DetailQueryOptions;\n\n // Detect which overload: second arg is string|null = legacy, object/undefined = new\n if (tokenOrOptions === null || typeof tokenOrOptions === 'string') {\n token = tokenOrOptions;\n options = maybeOptions ?? {};\n } else {\n const auth = getAuthContext();\n token = auth.token;\n options = (tokenOrOptions as DetailQueryOptions) ?? {};\n if (auth.organizationId && !options.organizationId) {\n options = { ...options, organizationId: auth.organizationId };\n }\n }\n\n const { organizationId, params: queryParams, request: requestOpts, ...restOptions } = options;\n\n return createDetailQuery<T>({\n queryKey: queryParams ? [...KEYS.detail(id || \"\"), queryParams] : KEYS.detail(id || \"\"),\n queryFn: () => api.getById({ id: id!, token, organizationId, params: queryParams, options: { ...requestOpts } }),\n enabled: !!id && createEnabledRule(token, restOptions),\n options: {\n staleTime: restOptions.staleTime ?? config.staleTime,\n gcTime: restOptions.gcTime ?? config.gcTime,\n structuralSharing: restOptions.structuralSharing ?? config.structuralSharing,\n refetchInterval: restOptions.refetchInterval,\n refetchIntervalInBackground: restOptions.refetchIntervalInBackground,\n },\n });\n }\n\n // ========== useActions ==========\n\n function useActions(): CrudActions<T, TCreate, TUpdate> {\n const queryClient = useQueryClient();\n\n const createMutation = createOptimisticMutation({\n mutationFn: ({ token, organizationId, data }: MutationParams<TCreate>) =>\n api.create({ token, organizationId, data }),\n queryClient,\n queryKeys: [KEYS.lists()],\n optimisticUpdate: (oldData, { data }) => {\n const optimisticItem = {\n ...(data as object),\n _optimistic: true,\n [getItemId(data) ? \"id\" : \"_id\"]: getItemId(data) ?? `temp-${Date.now()}`,\n };\n return updateListCache(oldData, (arr: unknown[]) => [optimisticItem, ...(arr || [])]);\n },\n onSuccess: (data, variables) => {\n callbacks.onCreate?.onSuccess?.(data as T, { data: variables.data }, undefined);\n },\n onError: (error, variables) => {\n callbacks.onCreate?.onError?.(error, { data: variables.data }, undefined);\n },\n messages: { success: config.messages.createSuccess, error: config.messages.createError },\n toastHandler: instanceToast,\n });\n\n const updateMutation = createOptimisticMutation({\n mutationFn: ({ token, organizationId, id, data }: UpdateParams<TUpdate>) =>\n api.update({ token, organizationId, id, data }),\n queryClient,\n queryKeys: [KEYS.lists()],\n optimisticUpdate: (oldData, { id, data }) => {\n const updated = updateListCache(oldData, (arr: unknown[]) =>\n (arr || []).map((item) => (getItemId(item) === id ? { ...(item as object), ...(data as object) } : item))\n );\n queryClient.setQueryData(KEYS.detail(id), (current: unknown) =>\n current ? { ...(current as object), data: { ...((current as { data?: object }).data || {}), ...(data as object) } } : current\n );\n return updated;\n },\n onSuccess: (data, { id }) => {\n queryClient.invalidateQueries({ queryKey: KEYS.detail(id) });\n callbacks.onUpdate?.onSuccess?.(data as T, { id, data: {} as TUpdate }, undefined);\n },\n onError: (error, { id, data }) => {\n callbacks.onUpdate?.onError?.(error, { id, data }, undefined);\n },\n messages: { success: config.messages.updateSuccess, error: config.messages.updateError },\n toastHandler: instanceToast,\n });\n\n const deleteMutation = createOptimisticMutation({\n mutationFn: ({ token, organizationId, id }: DeleteParams) => api.delete({ token, organizationId, id }),\n queryClient,\n queryKeys: [KEYS.lists()],\n optimisticUpdate: (oldData, { id }) => {\n queryClient.removeQueries({ queryKey: KEYS.detail(id) });\n return updateListCache(oldData, (arr: unknown[]) => (arr || []).filter((item) => getItemId(item) !== id));\n },\n onSuccess: (data, { id }) => {\n callbacks.onDelete?.onSuccess?.(data, { id }, undefined);\n },\n onError: (error, { id }) => {\n callbacks.onDelete?.onError?.(error, { id }, undefined);\n },\n messages: { success: config.messages.deleteSuccess, error: config.messages.deleteError },\n toastHandler: instanceToast,\n });\n\n const resolveAuth = <P extends { token?: string | null; organizationId?: string | null }>(params: P): P => {\n const auth = getAuthContext();\n return {\n ...params,\n token: params.token ?? auth.token,\n organizationId: params.organizationId ?? auth.organizationId,\n };\n };\n\n const create = async (params: MutationParams<TCreate>, options?: CallOptions<T>): Promise<T> => {\n try {\n const result = await createMutation.mutateAsync(resolveAuth(params));\n options?.onSuccess?.(result as T);\n return result as T;\n } catch (error) {\n options?.onError?.(error as Error);\n throw error;\n }\n };\n\n const update = async (params: UpdateParams<TUpdate>, options?: CallOptions<T>): Promise<T> => {\n try {\n const result = await updateMutation.mutateAsync(resolveAuth(params));\n options?.onSuccess?.(result as T);\n return result as T;\n } catch (error) {\n options?.onError?.(error as Error);\n throw error;\n }\n };\n\n const remove = async (params: DeleteParams, options?: CallOptions): Promise<unknown> => {\n try {\n const result = await deleteMutation.mutateAsync(resolveAuth(params));\n options?.onSuccess?.(result);\n return result;\n } catch (error) {\n options?.onError?.(error as Error);\n throw error;\n }\n };\n\n return {\n create,\n update,\n remove,\n isCreating: createMutation.isPending,\n isUpdating: updateMutation.isPending,\n isDeleting: deleteMutation.isPending,\n isMutating: createMutation.isPending || updateMutation.isPending || deleteMutation.isPending,\n };\n }\n\n // ========== useInfiniteList ==========\n\n function useInfiniteList(\n tokenOrParams?: string | null | Record<string, unknown>,\n paramsOrOptions?: Record<string, unknown> | InfiniteListQueryOptions,\n maybeOptions?: InfiniteListQueryOptions,\n ): InfiniteListQueryResult<T> {\n let token: string | null;\n let params: Record<string, unknown>;\n let options: InfiniteListQueryOptions;\n\n if (tokenOrParams === null || typeof tokenOrParams === 'string') {\n token = tokenOrParams;\n params = (paramsOrOptions as Record<string, unknown>) ?? {};\n options = maybeOptions ?? {};\n } else {\n const auth = getAuthContext();\n token = auth.token;\n params = (tokenOrParams as Record<string, unknown>) ?? {};\n options = (paramsOrOptions as InfiniteListQueryOptions) ?? {};\n if (auth.organizationId && !params.organizationId) {\n params = { ...params, organizationId: auth.organizationId };\n }\n }\n\n const { organizationId, ...restParams } = params;\n const scope = options._scope || (organizationId ? \"tenant\" : \"super-admin\");\n const { request: requestOpts, ...queryOpts } = options;\n\n return createInfiniteListQuery<T>({\n queryKey: [...KEYS.scopedList(scope, { organizationId, ...restParams }), 'infinite'],\n queryFn: ({ pageParam }) => {\n const paginationParams = typeof pageParam === 'string'\n ? { after: pageParam }\n : { page: pageParam };\n\n return api.getAll({\n token,\n organizationId: organizationId as string | null,\n params: { ...restParams, ...paginationParams },\n options: { ...requestOpts },\n });\n },\n enabled: createEnabledRule(token, queryOpts),\n initialPageParam: restParams.after ? restParams.after : 1,\n getNextPageParam: (lastPage) => {\n const page = lastPage as PaginatedResponse<T>;\n if (isKeysetPagination(page)) {\n return page.hasMore ? page.next : undefined;\n }\n if (isOffsetPagination(page)) {\n return page.hasNext ? page.page + 1 : undefined;\n }\n // Fallback: check common pagination fields\n const p = page as unknown as Record<string, unknown>;\n if (typeof p.hasNext === 'boolean' && typeof p.page === 'number') {\n return p.hasNext ? (p.page as number) + 1 : undefined;\n }\n return undefined;\n },\n options: {\n staleTime: queryOpts.staleTime ?? config.staleTime,\n gcTime: queryOpts.gcTime ?? config.gcTime,\n refetchOnWindowFocus: queryOpts.refetchOnWindowFocus ?? config.refetchOnWindowFocus,\n structuralSharing: queryOpts.structuralSharing ?? config.structuralSharing,\n },\n });\n }\n\n // ========== useNavigation ==========\n\n function useNavigation(): NavigateFn<T> {\n const queryClient = useQueryClient();\n const activeRouterHook = instanceNavigation ?? useRouterHook;\n const router = activeRouterHook?.();\n\n return useCallback(\n (href: string, item: T, options: NavigationOptions = {}) => {\n const id = getItemId(item);\n if (id) {\n queryClient.setQueryData(KEYS.detail(id), { data: item });\n }\n\n if (!router) return;\n\n const { scroll = true, replace = false } = options;\n if (replace) {\n router.replace(href, { scroll });\n } else {\n router.push(href, { scroll });\n }\n },\n [queryClient, router]\n );\n }\n\n return {\n KEYS,\n cache,\n useList,\n useDetail,\n useInfiniteList,\n useActions,\n useNavigation,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hooks.js","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["\"use client\";\n\nimport { useQueryClient, useMutation, type QueryKey } from \"@tanstack/react-query\";\nimport { useCallback, useRef } from \"react\";\nimport { createOptimisticMutation, useMutationWithTransition } from \"./mutation.js\";\nimport type { MutationMessages } from \"./mutation.js\";\nimport {\n createListQuery,\n createDetailQuery,\n createInfiniteListQuery,\n updateListCache,\n getItemId,\n createQueryKeys,\n createCacheUtils,\n DEFAULT_QUERY_CONFIG,\n} from \"./query.js\";\nimport type {\n QueryKeys,\n CacheUtils,\n ListQueryOptions,\n DetailQueryOptions,\n ListQueryResult,\n DetailQueryResult,\n InfiniteListQueryOptions,\n InfiniteListQueryResult,\n} from \"./query.js\";\nimport {\n isOffsetPagination,\n isKeysetPagination,\n} from \"./api.js\";\nimport type { PaginatedResponse } from \"./api.js\";\nimport type { MutationCallbacks, TransitionMutationReturn } from \"./mutation.js\";\nimport { getAuthMode, getAuthContext } from \"./client.js\";\nimport type { ArcClient, ToastHandler, UseRouterHook } from \"./client.js\";\n\n// Re-export UseRouterHook for backward compatibility\nexport type { UseRouterHook } from \"./client.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CrudApi<T, TCreate = Partial<T>, TUpdate = Partial<T>> {\n /** Scope from BaseApi — used to auto-detect platformScoped */\n scope?: 'tenant' | 'platform';\n\n getAll: (options: {\n token?: string | null;\n organizationId?: string | null;\n params?: Record<string, unknown>;\n options?: { signal?: AbortSignal; [key: string]: unknown };\n }) => Promise<unknown>;\n\n getById: (options: {\n id: string;\n token?: string | null;\n organizationId?: string | null;\n params?: { select?: string; populate?: string | string[] };\n options?: { signal?: AbortSignal; [key: string]: unknown };\n }) => Promise<unknown>;\n\n create: (options: {\n token?: string | null;\n organizationId?: string | null;\n data: TCreate;\n }) => Promise<unknown>;\n\n update: (options: {\n token?: string | null;\n organizationId?: string | null;\n id: string;\n data: TUpdate;\n }) => Promise<unknown>;\n\n delete: (options: {\n token?: string | null;\n organizationId?: string | null;\n id: string;\n }) => Promise<unknown>;\n\n upload?: (options: {\n token?: string | null;\n organizationId?: string | null;\n data: FormData;\n id?: string;\n path?: string;\n }) => Promise<unknown>;\n\n search?: (options: {\n token?: string | null;\n organizationId?: string | null;\n params?: Record<string, unknown>;\n options?: { signal?: AbortSignal; [key: string]: unknown };\n }) => Promise<unknown>;\n}\n\nexport interface CrudHooksConfig<T, TCreate = Partial<T>, TUpdate = Partial<T>> {\n api: CrudApi<T, TCreate, TUpdate>;\n entityKey: string;\n singular: string;\n plural?: string;\n /**\n * Platform-scoped hooks skip automatic organizationId injection.\n *\n * Use this for admin/superadmin hooks that query across all orgs\n * (e.g., APIs configured with `x-arc-scope: platform`).\n *\n * When true, the hooks will NOT auto-inject the user's active\n * organizationId into requests — the API's own headers control scoping.\n *\n * @default false\n */\n platformScoped?: boolean;\n defaults?: {\n staleTime?: number;\n gcTime?: number;\n refetchOnWindowFocus?: boolean;\n structuralSharing?: boolean;\n messages?: {\n createSuccess?: string;\n createError?: string;\n updateSuccess?: string;\n updateError?: string;\n deleteSuccess?: string;\n deleteError?: string;\n };\n };\n callbacks?: {\n onCreate?: MutationCallbacks<T, { data: TCreate }>;\n onUpdate?: MutationCallbacks<T, { id: string; data: TUpdate }>;\n onDelete?: MutationCallbacks<unknown, { id: string }>;\n };\n client?: ArcClient;\n}\n\nexport interface MutationParams<TData> {\n token?: string | null;\n organizationId?: string | null;\n data: TData;\n}\n\nexport interface UpdateParams<TData> extends MutationParams<TData> {\n id: string;\n}\n\nexport interface DeleteParams {\n token?: string | null;\n organizationId?: string | null;\n id: string;\n}\n\nexport interface CallOptions<TData = unknown> {\n onSuccess?: (data: TData) => void;\n onError?: (error: Error) => void;\n onSettled?: (data: TData | undefined, error: Error | null) => void;\n silent?: boolean;\n}\n\nexport interface CrudActions<T, TCreate, TUpdate> {\n create: (params: MutationParams<TCreate>, options?: CallOptions<T>) => Promise<T>;\n update: (params: UpdateParams<TUpdate>, options?: CallOptions<T>) => Promise<T>;\n remove: (params: DeleteParams, options?: CallOptions) => Promise<unknown>;\n isCreating: boolean;\n isUpdating: boolean;\n isDeleting: boolean;\n isMutating: boolean;\n}\n\nexport interface NavigationOptions {\n scroll?: boolean;\n replace?: boolean;\n}\n\nexport type NavigateFn<T> = (href: string, item: T, options?: NavigationOptions) => void;\n\nexport interface CrudHooksReturn<T, TCreate, TUpdate> {\n KEYS: QueryKeys;\n cache: CacheUtils<T>;\n useList: {\n /** New signature — auto-injects token/orgId from configureAuth() context */\n (params?: Record<string, unknown>, options?: ListQueryOptions<T>): ListQueryResult<T>;\n /** Legacy signature — explicit token */\n (token: string | null, params?: Record<string, unknown>, options?: ListQueryOptions<T>): ListQueryResult<T>;\n };\n useDetail: {\n /** New signature — auto-injects token from configureAuth() context */\n (id: string | null, options?: DetailQueryOptions<T>): DetailQueryResult<T>;\n /** Legacy signature — explicit token */\n (id: string | null, token: string | null, options?: DetailQueryOptions<T>): DetailQueryResult<T>;\n };\n useInfiniteList: {\n /** New signature — auto-injects token/orgId from configureAuth() context */\n (params?: Record<string, unknown>, options?: InfiniteListQueryOptions): InfiniteListQueryResult<T>;\n /** Legacy signature — explicit token */\n (token: string | null, params?: Record<string, unknown>, options?: InfiniteListQueryOptions): InfiniteListQueryResult<T>;\n };\n useActions: () => CrudActions<T, TCreate, TUpdate>;\n useUpload: (options?: {\n invalidateQueries?: QueryKey[];\n messages?: MutationMessages;\n onSuccess?: (data: unknown) => void;\n onError?: (error: Error) => void;\n onSettled?: (data: unknown | undefined, error: Error | null) => void;\n }) => TransitionMutationReturn<unknown, { data: FormData; id?: string; path?: string }>;\n useSearch: (query: string, params?: Record<string, unknown>, options?: ListQueryOptions<T>) => ListQueryResult<T>;\n useCustomMutation: <TData = unknown, TVariables = unknown>(config: {\n mutationFn: (variables: TVariables) => Promise<TData>;\n invalidateQueries?: QueryKey[];\n messages?: MutationMessages;\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 }) => TransitionMutationReturn<TData, TVariables>;\n useNavigation: () => NavigateFn<T>;\n}\n\n// ============================================================================\n// Navigation Configuration (Global)\n// ============================================================================\n\nlet useRouterHook: UseRouterHook | null = null;\n\n/**\n * Configure the router hook for useNavigation. Call once at app init.\n *\n * @example\n * import { useRouter } from \"next/navigation\";\n * configureNavigation(useRouter);\n */\nexport function configureNavigation(hook: UseRouterHook): void {\n useRouterHook = hook;\n}\n\n// ============================================================================\n// Enabled Rule\n// ============================================================================\n\nfunction createEnabledRule(token: string | null, options: { public?: boolean; enabled?: boolean }): boolean {\n // Cookie-based auth: no token needed, queries always enabled (cookies sent via credentials: 'include')\n if (getAuthMode() === 'cookie' || options.public) {\n return options.enabled ?? true;\n }\n // Bearer token auth: require token to enable queries\n return options.enabled !== undefined ? options.enabled && !!token : !!token;\n}\n\n// ============================================================================\n// Create CRUD Hooks Factory\n// ============================================================================\n\nexport function createCrudHooks<T, TCreate = Partial<T>, TUpdate = Partial<T>>({\n api,\n entityKey,\n singular,\n platformScoped,\n defaults = {},\n callbacks = {},\n client,\n}: CrudHooksConfig<T, TCreate, TUpdate>): CrudHooksReturn<T, TCreate, TUpdate> {\n // Auto-detect from api.scope when platformScoped is not explicitly set\n const isPlatform = platformScoped ?? (api.scope === 'platform');\n const KEYS = createQueryKeys(entityKey);\n const cache = createCacheUtils<T>(KEYS);\n\n // Resolve toast handler: client instance → global\n const instanceToast: ToastHandler | undefined = client?.toast;\n // Resolve navigation hook: client instance → global\n const instanceNavigation: UseRouterHook | null = client?.navigation ?? null;\n\n const config = {\n ...DEFAULT_QUERY_CONFIG,\n ...defaults,\n structuralSharing: defaults.structuralSharing,\n messages: {\n createSuccess: `${singular} created successfully`,\n createError: `Failed to create ${singular.toLowerCase()}`,\n updateSuccess: `${singular} updated successfully`,\n updateError: `Failed to update ${singular.toLowerCase()}`,\n deleteSuccess: `${singular} deleted successfully`,\n deleteError: `Failed to delete ${singular.toLowerCase()}`,\n ...defaults.messages,\n },\n };\n\n // ========== useList ==========\n\n function useList(\n tokenOrParams?: string | null | Record<string, unknown>,\n paramsOrOptions?: Record<string, unknown> | ListQueryOptions,\n maybeOptions?: ListQueryOptions,\n ): ListQueryResult<T> {\n let token: string | null;\n let params: Record<string, unknown>;\n let options: ListQueryOptions;\n\n // Detect which overload: first arg is string|null = legacy, object/undefined = new\n if (tokenOrParams === null || typeof tokenOrParams === 'string') {\n token = tokenOrParams;\n params = (paramsOrOptions as Record<string, unknown>) ?? {};\n options = maybeOptions ?? {};\n } else {\n const auth = getAuthContext();\n token = auth.token;\n params = (tokenOrParams as Record<string, unknown>) ?? {};\n options = (paramsOrOptions as ListQueryOptions) ?? {};\n if (!isPlatform && auth.organizationId && !params.organizationId) {\n params = { ...params, organizationId: auth.organizationId };\n }\n }\n\n const { organizationId, ...restParams } = params;\n const scope = options._scope || (isPlatform ? \"platform\" : organizationId ? \"tenant\" : \"super-admin\");\n const { request: requestOpts, ...queryOpts } = options;\n\n return createListQuery<T>({\n queryKey: KEYS.scopedList(scope, { organizationId, ...restParams }),\n queryFn: ({ signal }) => api.getAll({ token, organizationId: organizationId as string | null, params: restParams, options: { signal, ...requestOpts } }),\n enabled: createEnabledRule(token, queryOpts),\n options: {\n staleTime: queryOpts.staleTime ?? config.staleTime,\n gcTime: queryOpts.gcTime ?? config.gcTime,\n refetchOnWindowFocus: queryOpts.refetchOnWindowFocus ?? config.refetchOnWindowFocus,\n structuralSharing: queryOpts.structuralSharing ?? config.structuralSharing,\n refetchInterval: queryOpts.refetchInterval,\n refetchIntervalInBackground: queryOpts.refetchIntervalInBackground,\n },\n prefillDetailCache: queryOpts.prefillDetailCache ?? true,\n detailKeyBuilder: (id) => KEYS.detail(id),\n select: queryOpts.select,\n });\n }\n\n // ========== useDetail ==========\n\n function useDetail(\n id: string | null,\n tokenOrOptions?: string | null | DetailQueryOptions,\n maybeOptions?: DetailQueryOptions,\n ): DetailQueryResult<T> {\n let token: string | null;\n let options: DetailQueryOptions;\n\n // Detect which overload: second arg is string|null = legacy, object/undefined = new\n if (tokenOrOptions === null || typeof tokenOrOptions === 'string') {\n token = tokenOrOptions;\n options = maybeOptions ?? {};\n } else {\n const auth = getAuthContext();\n token = auth.token;\n options = (tokenOrOptions as DetailQueryOptions) ?? {};\n if (!isPlatform && auth.organizationId && !options.organizationId) {\n options = { ...options, organizationId: auth.organizationId };\n }\n }\n\n const { organizationId, params: queryParams, request: requestOpts, ...restOptions } = options;\n\n return createDetailQuery<T>({\n queryKey: queryParams ? [...KEYS.detail(id || \"\"), queryParams] : KEYS.detail(id || \"\"),\n queryFn: ({ signal }) => api.getById({ id: id!, token, organizationId, params: queryParams, options: { signal, ...requestOpts } }),\n enabled: !!id && createEnabledRule(token, restOptions),\n options: {\n staleTime: restOptions.staleTime ?? config.staleTime,\n gcTime: restOptions.gcTime ?? config.gcTime,\n structuralSharing: restOptions.structuralSharing ?? config.structuralSharing,\n refetchInterval: restOptions.refetchInterval,\n refetchIntervalInBackground: restOptions.refetchIntervalInBackground,\n },\n select: restOptions.select,\n });\n }\n\n // ========== useActions ==========\n\n function useActions(): CrudActions<T, TCreate, TUpdate> {\n const queryClient = useQueryClient();\n const silentRef = useRef(false);\n const shouldToast = useCallback(() => !silentRef.current, []);\n\n const createMutation = createOptimisticMutation({\n mutationFn: ({ token, organizationId, data }: MutationParams<TCreate>) =>\n api.create({ token, organizationId, data }),\n queryClient,\n queryKeys: [KEYS.lists()],\n shouldToast,\n optimisticUpdate: (oldData, { data }) => {\n const optimisticItem = {\n ...(data as object),\n _optimistic: true,\n [getItemId(data) ? \"id\" : \"_id\"]: getItemId(data) ?? `temp-${Date.now()}`,\n };\n return updateListCache(oldData, (arr: unknown[]) => [optimisticItem, ...(arr || [])]);\n },\n onSuccess: (data, variables) => {\n callbacks.onCreate?.onSuccess?.(data as T, { data: variables.data }, undefined);\n },\n onError: (error, variables) => {\n callbacks.onCreate?.onError?.(error, { data: variables.data }, undefined);\n },\n onSettled: (data, error, variables) => {\n callbacks.onCreate?.onSettled?.(data as T | undefined, error, { data: variables.data }, undefined);\n },\n messages: { success: config.messages.createSuccess, error: config.messages.createError },\n toastHandler: instanceToast,\n });\n\n const updateMutation = createOptimisticMutation({\n mutationFn: ({ token, organizationId, id, data }: UpdateParams<TUpdate>) =>\n api.update({ token, organizationId, id, data }),\n queryClient,\n queryKeys: [KEYS.lists()],\n shouldToast,\n optimisticUpdate: (oldData, { id, data }) => {\n const updated = updateListCache(oldData, (arr: unknown[]) =>\n (arr || []).map((item) => (getItemId(item) === id ? { ...(item as object), ...(data as object) } : item))\n );\n queryClient.setQueryData(KEYS.detail(id), (current: unknown) =>\n current ? { ...(current as object), data: { ...((current as { data?: object }).data || {}), ...(data as object) } } : current\n );\n return updated;\n },\n onSuccess: (data, { id, data: updateData }) => {\n queryClient.invalidateQueries({ queryKey: KEYS.detail(id) });\n callbacks.onUpdate?.onSuccess?.(data as T, { id, data: updateData }, undefined);\n },\n onError: (error, { id, data }) => {\n callbacks.onUpdate?.onError?.(error, { id, data }, undefined);\n },\n onSettled: (data, error, { id, data: updateData }) => {\n callbacks.onUpdate?.onSettled?.(data as T | undefined, error, { id, data: updateData }, undefined);\n },\n messages: { success: config.messages.updateSuccess, error: config.messages.updateError },\n toastHandler: instanceToast,\n });\n\n const deleteMutation = createOptimisticMutation({\n mutationFn: ({ token, organizationId, id }: DeleteParams) => api.delete({ token, organizationId, id }),\n queryClient,\n queryKeys: [KEYS.lists()],\n shouldToast,\n optimisticUpdate: (oldData, { id }) => {\n queryClient.removeQueries({ queryKey: KEYS.detail(id) });\n return updateListCache(oldData, (arr: unknown[]) => (arr || []).filter((item) => getItemId(item) !== id));\n },\n onSuccess: (data, { id }) => {\n callbacks.onDelete?.onSuccess?.(data, { id }, undefined);\n },\n onError: (error, { id }) => {\n callbacks.onDelete?.onError?.(error, { id }, undefined);\n },\n onSettled: (data, error, { id }) => {\n callbacks.onDelete?.onSettled?.(data, error, { id }, undefined);\n },\n messages: { success: config.messages.deleteSuccess, error: config.messages.deleteError },\n toastHandler: instanceToast,\n });\n\n const resolveAuth = <P extends { token?: string | null; organizationId?: string | null }>(params: P): P => {\n const auth = getAuthContext();\n return {\n ...params,\n token: params.token ?? auth.token,\n organizationId: isPlatform ? (params.organizationId ?? null) : (params.organizationId ?? auth.organizationId),\n };\n };\n\n const create = async (params: MutationParams<TCreate>, options?: CallOptions<T>): Promise<T> => {\n silentRef.current = options?.silent ?? false;\n try {\n const result = await createMutation.mutateAsync(resolveAuth(params));\n options?.onSuccess?.(result as T);\n options?.onSettled?.(result as T, null);\n return result as T;\n } catch (error) {\n options?.onError?.(error as Error);\n options?.onSettled?.(undefined, error as Error);\n throw error;\n } finally {\n silentRef.current = false;\n }\n };\n\n const update = async (params: UpdateParams<TUpdate>, options?: CallOptions<T>): Promise<T> => {\n silentRef.current = options?.silent ?? false;\n try {\n const result = await updateMutation.mutateAsync(resolveAuth(params));\n options?.onSuccess?.(result as T);\n options?.onSettled?.(result as T, null);\n return result as T;\n } catch (error) {\n options?.onError?.(error as Error);\n options?.onSettled?.(undefined, error as Error);\n throw error;\n } finally {\n silentRef.current = false;\n }\n };\n\n const remove = async (params: DeleteParams, options?: CallOptions): Promise<unknown> => {\n silentRef.current = options?.silent ?? false;\n try {\n const result = await deleteMutation.mutateAsync(resolveAuth(params));\n options?.onSuccess?.(result);\n options?.onSettled?.(result, null);\n return result;\n } catch (error) {\n options?.onError?.(error as Error);\n options?.onSettled?.(undefined, error as Error);\n throw error;\n } finally {\n silentRef.current = false;\n }\n };\n\n return {\n create,\n update,\n remove,\n isCreating: createMutation.isPending,\n isUpdating: updateMutation.isPending,\n isDeleting: deleteMutation.isPending,\n isMutating: createMutation.isPending || updateMutation.isPending || deleteMutation.isPending,\n };\n }\n\n // ========== useInfiniteList ==========\n\n function useInfiniteList(\n tokenOrParams?: string | null | Record<string, unknown>,\n paramsOrOptions?: Record<string, unknown> | InfiniteListQueryOptions,\n maybeOptions?: InfiniteListQueryOptions,\n ): InfiniteListQueryResult<T> {\n let token: string | null;\n let params: Record<string, unknown>;\n let options: InfiniteListQueryOptions;\n\n if (tokenOrParams === null || typeof tokenOrParams === 'string') {\n token = tokenOrParams;\n params = (paramsOrOptions as Record<string, unknown>) ?? {};\n options = maybeOptions ?? {};\n } else {\n const auth = getAuthContext();\n token = auth.token;\n params = (tokenOrParams as Record<string, unknown>) ?? {};\n options = (paramsOrOptions as InfiniteListQueryOptions) ?? {};\n if (!isPlatform && auth.organizationId && !params.organizationId) {\n params = { ...params, organizationId: auth.organizationId };\n }\n }\n\n const { organizationId, ...restParams } = params;\n const scope = options._scope || (organizationId ? \"tenant\" : \"super-admin\");\n const { request: requestOpts, ...queryOpts } = options;\n\n return createInfiniteListQuery<T>({\n queryKey: [...KEYS.scopedList(scope, { organizationId, ...restParams }), 'infinite'],\n queryFn: ({ pageParam, signal }) => {\n const paginationParams = typeof pageParam === 'string'\n ? { after: pageParam }\n : { page: pageParam };\n\n return api.getAll({\n token,\n organizationId: organizationId as string | null,\n params: { ...restParams, ...paginationParams },\n options: { signal, ...requestOpts },\n });\n },\n enabled: createEnabledRule(token, queryOpts),\n initialPageParam: restParams.after ? restParams.after : 1,\n getNextPageParam: (lastPage) => {\n const page = lastPage as PaginatedResponse<T>;\n if (isKeysetPagination(page)) {\n return page.hasMore ? page.next : undefined;\n }\n if (isOffsetPagination(page)) {\n return page.hasNext ? page.page + 1 : undefined;\n }\n // Fallback: check common pagination fields\n const p = page as unknown as Record<string, unknown>;\n if (typeof p.hasNext === 'boolean' && typeof p.page === 'number') {\n return p.hasNext ? (p.page as number) + 1 : undefined;\n }\n return undefined;\n },\n options: {\n staleTime: queryOpts.staleTime ?? config.staleTime,\n gcTime: queryOpts.gcTime ?? config.gcTime,\n refetchOnWindowFocus: queryOpts.refetchOnWindowFocus ?? config.refetchOnWindowFocus,\n structuralSharing: queryOpts.structuralSharing ?? config.structuralSharing,\n },\n });\n }\n\n // ========== useUpload ==========\n\n function useUpload(options?: {\n invalidateQueries?: QueryKey[];\n messages?: MutationMessages;\n onSuccess?: (data: unknown) => void;\n onError?: (error: Error) => void;\n onSettled?: (data: unknown | undefined, error: Error | null) => void;\n }) {\n if (!api.upload) {\n throw new Error(`[arc-next] \"${entityKey}\" api does not define an upload method`);\n }\n\n const uploadApi = api.upload;\n\n return useMutationWithTransition<unknown, { data: FormData; id?: string; path?: string }>({\n mutationFn: ({ data, id, path }) => {\n const auth = getAuthContext();\n return uploadApi({ token: auth.token, organizationId: isPlatform ? null : auth.organizationId, data, id, path });\n },\n invalidateQueries: options?.invalidateQueries ?? [KEYS.lists()],\n onSuccess: (data) => options?.onSuccess?.(data),\n onError: (error) => options?.onError?.(error),\n onSettled: (data, error) => options?.onSettled?.(data, error),\n messages: options?.messages ?? {\n success: `${singular} uploaded successfully`,\n error: `Failed to upload ${singular.toLowerCase()}`,\n },\n toastHandler: instanceToast,\n });\n }\n\n // ========== useSearch ==========\n\n function useSearch(\n query: string,\n params?: Record<string, unknown>,\n options?: ListQueryOptions<T>,\n ): ListQueryResult<T> {\n if (!api.search) {\n throw new Error(`[arc-next] \"${entityKey}\" api does not define a search method`);\n }\n\n const searchApi = api.search;\n const auth = getAuthContext();\n const token = auth.token;\n const organizationId = platformScoped ? null : (params?.organizationId as string | null ?? auth.organizationId);\n const { organizationId: _, ...restParams } = params ?? {};\n const searchParams = { q: query, ...restParams };\n const { request: requestOpts, ...queryOpts } = options ?? {};\n\n // Include organizationId in query key when present to isolate per-tenant search cache\n const searchKeyParams = organizationId\n ? { organizationId, ...searchParams }\n : searchParams;\n\n return createListQuery<T>({\n queryKey: [...KEYS.lists(), 'search', searchKeyParams],\n queryFn: ({ signal }) => searchApi({\n token,\n organizationId,\n params: searchParams,\n options: { signal, ...requestOpts },\n }),\n enabled: query.length > 0 && createEnabledRule(token, queryOpts),\n options: {\n staleTime: queryOpts.staleTime ?? config.staleTime,\n gcTime: queryOpts.gcTime ?? config.gcTime,\n },\n select: queryOpts.select,\n });\n }\n\n // ========== useCustomMutation ==========\n\n function useCustomMutation<TData = unknown, TVariables = unknown>(mutationConfig: {\n mutationFn: (variables: TVariables) => Promise<TData>;\n invalidateQueries?: QueryKey[];\n messages?: MutationMessages;\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 }) {\n return useMutationWithTransition<TData, TVariables>({\n mutationFn: mutationConfig.mutationFn,\n invalidateQueries: mutationConfig.invalidateQueries ?? [KEYS.lists()],\n onSuccess: mutationConfig.onSuccess,\n onError: mutationConfig.onError,\n onSettled: mutationConfig.onSettled,\n messages: mutationConfig.messages,\n toastHandler: instanceToast,\n });\n }\n\n // ========== useNavigation ==========\n\n function useNavigation(): NavigateFn<T> {\n const queryClient = useQueryClient();\n const activeRouterHook = instanceNavigation ?? useRouterHook;\n const router = activeRouterHook?.();\n\n return useCallback(\n (href: string, item: T, options: NavigationOptions = {}) => {\n const id = getItemId(item);\n if (id) {\n queryClient.setQueryData(KEYS.detail(id), { data: item });\n }\n\n if (!router) return;\n\n const { scroll = true, replace = false } = options;\n if (replace) {\n router.replace(href, { scroll });\n } else {\n router.push(href, { scroll });\n }\n },\n [queryClient, router]\n );\n }\n\n return {\n KEYS,\n cache,\n useList,\n useDetail,\n useInfiniteList,\n useActions,\n useUpload,\n useSearch,\n useCustomMutation,\n useNavigation,\n };\n}\n"],"mappings":";;;;;;;;;;AA4NA,IAAI,gBAAsC;;;;;;;;AAS1C,SAAgB,oBAAoB,MAA2B;AAC7D,iBAAgB;;AAOlB,SAAS,kBAAkB,OAAsB,SAA2D;AAE1G,KAAI,aAAa,KAAK,YAAY,QAAQ,OACxC,QAAO,QAAQ,WAAW;AAG5B,QAAO,QAAQ,YAAY,SAAY,QAAQ,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC;;AAOxE,SAAgB,gBAA+D,EAC7E,KACA,WACA,UACA,gBACA,WAAW,EAAE,EACb,YAAY,EAAE,EACd,UAC6E;CAE7E,MAAM,aAAa,kBAAmB,IAAI,UAAU;CACpD,MAAM,OAAO,gBAAgB,UAAU;CACvC,MAAM,QAAQ,iBAAoB,KAAK;CAGvC,MAAM,gBAA0C,QAAQ;CAExD,MAAM,qBAA2C,QAAQ,cAAc;CAEvE,MAAM,SAAS;EACb,GAAG;EACH,GAAG;EACH,mBAAmB,SAAS;EAC5B,UAAU;GACR,eAAe,GAAG,SAAS;GAC3B,aAAa,oBAAoB,SAAS,aAAa;GACvD,eAAe,GAAG,SAAS;GAC3B,aAAa,oBAAoB,SAAS,aAAa;GACvD,eAAe,GAAG,SAAS;GAC3B,aAAa,oBAAoB,SAAS,aAAa;GACvD,GAAG,SAAS;GACb;EACF;CAID,SAAS,QACP,eACA,iBACA,cACoB;EACpB,IAAI;EACJ,IAAI;EACJ,IAAI;AAGJ,MAAI,kBAAkB,QAAQ,OAAO,kBAAkB,UAAU;AAC/D,WAAQ;AACR,YAAU,mBAA+C,EAAE;AAC3D,aAAU,gBAAgB,EAAE;SACvB;GACL,MAAM,OAAO,gBAAgB;AAC7B,WAAQ,KAAK;AACb,YAAU,iBAA6C,EAAE;AACzD,aAAW,mBAAwC,EAAE;AACrD,OAAI,CAAC,cAAc,KAAK,kBAAkB,CAAC,OAAO,eAChD,UAAS;IAAE,GAAG;IAAQ,gBAAgB,KAAK;IAAgB;;EAI/D,MAAM,EAAE,gBAAgB,GAAG,eAAe;EAC1C,MAAM,QAAQ,QAAQ,WAAW,aAAa,aAAa,iBAAiB,WAAW;EACvF,MAAM,EAAE,SAAS,aAAa,GAAG,cAAc;AAE/C,SAAO,gBAAmB;GACxB,UAAU,KAAK,WAAW,OAAO;IAAE;IAAgB,GAAG;IAAY,CAAC;GACnE,UAAU,EAAE,aAAa,IAAI,OAAO;IAAE;IAAuB;IAAiC,QAAQ;IAAY,SAAS;KAAE;KAAQ,GAAG;KAAa;IAAE,CAAC;GACxJ,SAAS,kBAAkB,OAAO,UAAU;GAC5C,SAAS;IACP,WAAW,UAAU,aAAa,OAAO;IACzC,QAAQ,UAAU,UAAU,OAAO;IACnC,sBAAsB,UAAU,wBAAwB,OAAO;IAC/D,mBAAmB,UAAU,qBAAqB,OAAO;IACzD,iBAAiB,UAAU;IAC3B,6BAA6B,UAAU;IACxC;GACD,oBAAoB,UAAU,sBAAsB;GACpD,mBAAmB,OAAO,KAAK,OAAO,GAAG;GACzC,QAAQ,UAAU;GACnB,CAAC;;CAKJ,SAAS,UACP,IACA,gBACA,cACsB;EACtB,IAAI;EACJ,IAAI;AAGJ,MAAI,mBAAmB,QAAQ,OAAO,mBAAmB,UAAU;AACjE,WAAQ;AACR,aAAU,gBAAgB,EAAE;SACvB;GACL,MAAM,OAAO,gBAAgB;AAC7B,WAAQ,KAAK;AACb,aAAW,kBAAyC,EAAE;AACtD,OAAI,CAAC,cAAc,KAAK,kBAAkB,CAAC,QAAQ,eACjD,WAAU;IAAE,GAAG;IAAS,gBAAgB,KAAK;IAAgB;;EAIjE,MAAM,EAAE,gBAAgB,QAAQ,aAAa,SAAS,aAAa,GAAG,gBAAgB;AAEtF,SAAO,kBAAqB;GAC1B,UAAU,cAAc,CAAC,GAAG,KAAK,OAAO,MAAM,GAAG,EAAE,YAAY,GAAG,KAAK,OAAO,MAAM,GAAG;GACvF,UAAU,EAAE,aAAa,IAAI,QAAQ;IAAM;IAAK;IAAO;IAAgB,QAAQ;IAAa,SAAS;KAAE;KAAQ,GAAG;KAAa;IAAE,CAAC;GAClI,SAAS,CAAC,CAAC,MAAM,kBAAkB,OAAO,YAAY;GACtD,SAAS;IACP,WAAW,YAAY,aAAa,OAAO;IAC3C,QAAQ,YAAY,UAAU,OAAO;IACrC,mBAAmB,YAAY,qBAAqB,OAAO;IAC3D,iBAAiB,YAAY;IAC7B,6BAA6B,YAAY;IAC1C;GACD,QAAQ,YAAY;GACrB,CAAC;;CAKJ,SAAS,aAA+C;EACtD,MAAM,cAAc,gBAAgB;EACpC,MAAM,YAAY,OAAO,MAAM;EAC/B,MAAM,cAAc,kBAAkB,CAAC,UAAU,SAAS,EAAE,CAAC;EAE7D,MAAM,iBAAiB,yBAAyB;GAC9C,aAAa,EAAE,OAAO,gBAAgB,WACpC,IAAI,OAAO;IAAE;IAAO;IAAgB;IAAM,CAAC;GAC7C;GACA,WAAW,CAAC,KAAK,OAAO,CAAC;GACzB;GACA,mBAAmB,SAAS,EAAE,WAAW;IACvC,MAAM,iBAAiB;KACrB,GAAI;KACJ,aAAa;MACZ,UAAU,KAAK,GAAG,OAAO,QAAQ,UAAU,KAAK,IAAI,QAAQ,KAAK,KAAK;KACxE;AACD,WAAO,gBAAgB,UAAU,QAAmB,CAAC,gBAAgB,GAAI,OAAO,EAAE,CAAE,CAAC;;GAEvF,YAAY,MAAM,cAAc;AAC9B,cAAU,UAAU,YAAY,MAAW,EAAE,MAAM,UAAU,MAAM,EAAE,OAAU;;GAEjF,UAAU,OAAO,cAAc;AAC7B,cAAU,UAAU,UAAU,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAU;;GAE3E,YAAY,MAAM,OAAO,cAAc;AACrC,cAAU,UAAU,YAAY,MAAuB,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAU;;GAEpG,UAAU;IAAE,SAAS,OAAO,SAAS;IAAe,OAAO,OAAO,SAAS;IAAa;GACxF,cAAc;GACf,CAAC;EAEF,MAAM,iBAAiB,yBAAyB;GAC9C,aAAa,EAAE,OAAO,gBAAgB,IAAI,WACxC,IAAI,OAAO;IAAE;IAAO;IAAgB;IAAI;IAAM,CAAC;GACjD;GACA,WAAW,CAAC,KAAK,OAAO,CAAC;GACzB;GACA,mBAAmB,SAAS,EAAE,IAAI,WAAW;IAC3C,MAAM,UAAU,gBAAgB,UAAU,SACvC,OAAO,EAAE,EAAE,KAAK,SAAU,UAAU,KAAK,KAAK,KAAK;KAAE,GAAI;KAAiB,GAAI;KAAiB,GAAG,KAAM,CAC1G;AACD,gBAAY,aAAa,KAAK,OAAO,GAAG,GAAG,YACzC,UAAU;KAAE,GAAI;KAAoB,MAAM;MAAE,GAAK,QAA8B,QAAQ,EAAE;MAAG,GAAI;MAAiB;KAAE,GAAG,QACvH;AACD,WAAO;;GAET,YAAY,MAAM,EAAE,IAAI,MAAM,iBAAiB;AAC7C,gBAAY,kBAAkB,EAAE,UAAU,KAAK,OAAO,GAAG,EAAE,CAAC;AAC5D,cAAU,UAAU,YAAY,MAAW;KAAE;KAAI,MAAM;KAAY,EAAE,OAAU;;GAEjF,UAAU,OAAO,EAAE,IAAI,WAAW;AAChC,cAAU,UAAU,UAAU,OAAO;KAAE;KAAI;KAAM,EAAE,OAAU;;GAE/D,YAAY,MAAM,OAAO,EAAE,IAAI,MAAM,iBAAiB;AACpD,cAAU,UAAU,YAAY,MAAuB,OAAO;KAAE;KAAI,MAAM;KAAY,EAAE,OAAU;;GAEpG,UAAU;IAAE,SAAS,OAAO,SAAS;IAAe,OAAO,OAAO,SAAS;IAAa;GACxF,cAAc;GACf,CAAC;EAEF,MAAM,iBAAiB,yBAAyB;GAC9C,aAAa,EAAE,OAAO,gBAAgB,SAAuB,IAAI,OAAO;IAAE;IAAO;IAAgB;IAAI,CAAC;GACtG;GACA,WAAW,CAAC,KAAK,OAAO,CAAC;GACzB;GACA,mBAAmB,SAAS,EAAE,SAAS;AACrC,gBAAY,cAAc,EAAE,UAAU,KAAK,OAAO,GAAG,EAAE,CAAC;AACxD,WAAO,gBAAgB,UAAU,SAAoB,OAAO,EAAE,EAAE,QAAQ,SAAS,UAAU,KAAK,KAAK,GAAG,CAAC;;GAE3G,YAAY,MAAM,EAAE,SAAS;AAC3B,cAAU,UAAU,YAAY,MAAM,EAAE,IAAI,EAAE,OAAU;;GAE1D,UAAU,OAAO,EAAE,SAAS;AAC1B,cAAU,UAAU,UAAU,OAAO,EAAE,IAAI,EAAE,OAAU;;GAEzD,YAAY,MAAM,OAAO,EAAE,SAAS;AAClC,cAAU,UAAU,YAAY,MAAM,OAAO,EAAE,IAAI,EAAE,OAAU;;GAEjE,UAAU;IAAE,SAAS,OAAO,SAAS;IAAe,OAAO,OAAO,SAAS;IAAa;GACxF,cAAc;GACf,CAAC;EAEF,MAAM,eAAoF,WAAiB;GACzG,MAAM,OAAO,gBAAgB;AAC7B,UAAO;IACL,GAAG;IACH,OAAO,OAAO,SAAS,KAAK;IAC5B,gBAAgB,aAAc,OAAO,kBAAkB,OAAS,OAAO,kBAAkB,KAAK;IAC/F;;EAGH,MAAM,SAAS,OAAO,QAAiC,YAAyC;AAC9F,aAAU,UAAU,SAAS,UAAU;AACvC,OAAI;IACF,MAAM,SAAS,MAAM,eAAe,YAAY,YAAY,OAAO,CAAC;AACpE,aAAS,YAAY,OAAY;AACjC,aAAS,YAAY,QAAa,KAAK;AACvC,WAAO;YACA,OAAO;AACd,aAAS,UAAU,MAAe;AAClC,aAAS,YAAY,QAAW,MAAe;AAC/C,UAAM;aACE;AACR,cAAU,UAAU;;;EAIxB,MAAM,SAAS,OAAO,QAA+B,YAAyC;AAC5F,aAAU,UAAU,SAAS,UAAU;AACvC,OAAI;IACF,MAAM,SAAS,MAAM,eAAe,YAAY,YAAY,OAAO,CAAC;AACpE,aAAS,YAAY,OAAY;AACjC,aAAS,YAAY,QAAa,KAAK;AACvC,WAAO;YACA,OAAO;AACd,aAAS,UAAU,MAAe;AAClC,aAAS,YAAY,QAAW,MAAe;AAC/C,UAAM;aACE;AACR,cAAU,UAAU;;;EAIxB,MAAM,SAAS,OAAO,QAAsB,YAA4C;AACtF,aAAU,UAAU,SAAS,UAAU;AACvC,OAAI;IACF,MAAM,SAAS,MAAM,eAAe,YAAY,YAAY,OAAO,CAAC;AACpE,aAAS,YAAY,OAAO;AAC5B,aAAS,YAAY,QAAQ,KAAK;AAClC,WAAO;YACA,OAAO;AACd,aAAS,UAAU,MAAe;AAClC,aAAS,YAAY,QAAW,MAAe;AAC/C,UAAM;aACE;AACR,cAAU,UAAU;;;AAIxB,SAAO;GACL;GACA;GACA;GACA,YAAY,eAAe;GAC3B,YAAY,eAAe;GAC3B,YAAY,eAAe;GAC3B,YAAY,eAAe,aAAa,eAAe,aAAa,eAAe;GACpF;;CAKH,SAAS,gBACP,eACA,iBACA,cAC4B;EAC5B,IAAI;EACJ,IAAI;EACJ,IAAI;AAEJ,MAAI,kBAAkB,QAAQ,OAAO,kBAAkB,UAAU;AAC/D,WAAQ;AACR,YAAU,mBAA+C,EAAE;AAC3D,aAAU,gBAAgB,EAAE;SACvB;GACL,MAAM,OAAO,gBAAgB;AAC7B,WAAQ,KAAK;AACb,YAAU,iBAA6C,EAAE;AACzD,aAAW,mBAAgD,EAAE;AAC7D,OAAI,CAAC,cAAc,KAAK,kBAAkB,CAAC,OAAO,eAChD,UAAS;IAAE,GAAG;IAAQ,gBAAgB,KAAK;IAAgB;;EAI/D,MAAM,EAAE,gBAAgB,GAAG,eAAe;EAC1C,MAAM,QAAQ,QAAQ,WAAW,iBAAiB,WAAW;EAC7D,MAAM,EAAE,SAAS,aAAa,GAAG,cAAc;AAE/C,SAAO,wBAA2B;GAChC,UAAU,CAAC,GAAG,KAAK,WAAW,OAAO;IAAE;IAAgB,GAAG;IAAY,CAAC,EAAE,WAAW;GACpF,UAAU,EAAE,WAAW,aAAa;IAClC,MAAM,mBAAmB,OAAO,cAAc,WAC1C,EAAE,OAAO,WAAW,GACpB,EAAE,MAAM,WAAW;AAEvB,WAAO,IAAI,OAAO;KAChB;KACgB;KAChB,QAAQ;MAAE,GAAG;MAAY,GAAG;MAAkB;KAC9C,SAAS;MAAE;MAAQ,GAAG;MAAa;KACpC,CAAC;;GAEJ,SAAS,kBAAkB,OAAO,UAAU;GAC5C,kBAAkB,WAAW,QAAQ,WAAW,QAAQ;GACxD,mBAAmB,aAAa;IAC9B,MAAM,OAAO;AACb,QAAI,mBAAmB,KAAK,CAC1B,QAAO,KAAK,UAAU,KAAK,OAAO;AAEpC,QAAI,mBAAmB,KAAK,CAC1B,QAAO,KAAK,UAAU,KAAK,OAAO,IAAI;IAGxC,MAAM,IAAI;AACV,QAAI,OAAO,EAAE,YAAY,aAAa,OAAO,EAAE,SAAS,SACtD,QAAO,EAAE,UAAW,EAAE,OAAkB,IAAI;;GAIhD,SAAS;IACP,WAAW,UAAU,aAAa,OAAO;IACzC,QAAQ,UAAU,UAAU,OAAO;IACnC,sBAAsB,UAAU,wBAAwB,OAAO;IAC/D,mBAAmB,UAAU,qBAAqB,OAAO;IAC1D;GACF,CAAC;;CAKJ,SAAS,UAAU,SAMhB;AACD,MAAI,CAAC,IAAI,OACP,OAAM,IAAI,MAAM,eAAe,UAAU,wCAAwC;EAGnF,MAAM,YAAY,IAAI;AAEtB,SAAO,0BAAmF;GACxF,aAAa,EAAE,MAAM,IAAI,WAAW;IAClC,MAAM,OAAO,gBAAgB;AAC7B,WAAO,UAAU;KAAE,OAAO,KAAK;KAAO,gBAAgB,aAAa,OAAO,KAAK;KAAgB;KAAM;KAAI;KAAM,CAAC;;GAElH,mBAAmB,SAAS,qBAAqB,CAAC,KAAK,OAAO,CAAC;GAC/D,YAAY,SAAS,SAAS,YAAY,KAAK;GAC/C,UAAU,UAAU,SAAS,UAAU,MAAM;GAC7C,YAAY,MAAM,UAAU,SAAS,YAAY,MAAM,MAAM;GAC7D,UAAU,SAAS,YAAY;IAC7B,SAAS,GAAG,SAAS;IACrB,OAAO,oBAAoB,SAAS,aAAa;IAClD;GACD,cAAc;GACf,CAAC;;CAKJ,SAAS,UACP,OACA,QACA,SACoB;AACpB,MAAI,CAAC,IAAI,OACP,OAAM,IAAI,MAAM,eAAe,UAAU,uCAAuC;EAGlF,MAAM,YAAY,IAAI;EACtB,MAAM,OAAO,gBAAgB;EAC7B,MAAM,QAAQ,KAAK;EACnB,MAAM,iBAAiB,iBAAiB,OAAQ,QAAQ,kBAAmC,KAAK;EAChG,MAAM,EAAE,gBAAgB,GAAG,GAAG,eAAe,UAAU,EAAE;EACzD,MAAM,eAAe;GAAE,GAAG;GAAO,GAAG;GAAY;EAChD,MAAM,EAAE,SAAS,aAAa,GAAG,cAAc,WAAW,EAAE;EAG5D,MAAM,kBAAkB,iBACpB;GAAE;GAAgB,GAAG;GAAc,GACnC;AAEJ,SAAO,gBAAmB;GACxB,UAAU;IAAC,GAAG,KAAK,OAAO;IAAE;IAAU;IAAgB;GACtD,UAAU,EAAE,aAAa,UAAU;IACjC;IACA;IACA,QAAQ;IACR,SAAS;KAAE;KAAQ,GAAG;KAAa;IACpC,CAAC;GACF,SAAS,MAAM,SAAS,KAAK,kBAAkB,OAAO,UAAU;GAChE,SAAS;IACP,WAAW,UAAU,aAAa,OAAO;IACzC,QAAQ,UAAU,UAAU,OAAO;IACpC;GACD,QAAQ,UAAU;GACnB,CAAC;;CAKJ,SAAS,kBAAyD,gBAO/D;AACD,SAAO,0BAA6C;GAClD,YAAY,eAAe;GAC3B,mBAAmB,eAAe,qBAAqB,CAAC,KAAK,OAAO,CAAC;GACrE,WAAW,eAAe;GAC1B,SAAS,eAAe;GACxB,WAAW,eAAe;GAC1B,UAAU,eAAe;GACzB,cAAc;GACf,CAAC;;CAKJ,SAAS,gBAA+B;EACtC,MAAM,cAAc,gBAAgB;EAEpC,MAAM,UADmB,sBAAsB,kBACZ;AAEnC,SAAO,aACJ,MAAc,MAAS,UAA6B,EAAE,KAAK;GAC1D,MAAM,KAAK,UAAU,KAAK;AAC1B,OAAI,GACF,aAAY,aAAa,KAAK,OAAO,GAAG,EAAE,EAAE,MAAM,MAAM,CAAC;AAG3D,OAAI,CAAC,OAAQ;GAEb,MAAM,EAAE,SAAS,MAAM,UAAU,UAAU;AAC3C,OAAI,QACF,QAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC;OAEhC,QAAO,KAAK,MAAM,EAAE,QAAQ,CAAC;KAGjC,CAAC,aAAa,OAAO,CACtB;;AAGH,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
|
package/dist/mutation.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ToastHandler } from "./client.js";
|
|
2
2
|
import * as _tanstack_react_query0 from "@tanstack/react-query";
|
|
3
|
-
import { QueryClient, QueryKey } from "@tanstack/react-query";
|
|
3
|
+
import { QueryClient, QueryKey, UseMutateAsyncFunction, UseMutateFunction } from "@tanstack/react-query";
|
|
4
4
|
|
|
5
5
|
//#region src/mutation.d.ts
|
|
6
6
|
interface MutationMessages {
|
|
@@ -13,6 +13,16 @@ interface MutationCallbacks<TData, TVariables, TContext = unknown> {
|
|
|
13
13
|
onError?: (error: Error, variables: TVariables, context: TContext) => void | Promise<void>;
|
|
14
14
|
onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables, context: TContext) => void | Promise<void>;
|
|
15
15
|
}
|
|
16
|
+
interface TransitionMutationReturn<TData, TVariables> {
|
|
17
|
+
mutate: UseMutateFunction<TData, Error, TVariables>;
|
|
18
|
+
mutateAsync: UseMutateAsyncFunction<TData, Error, TVariables>;
|
|
19
|
+
isPending: boolean;
|
|
20
|
+
isSuccess: boolean;
|
|
21
|
+
isError: boolean;
|
|
22
|
+
error: Error | null;
|
|
23
|
+
data: TData | undefined;
|
|
24
|
+
reset: () => void;
|
|
25
|
+
}
|
|
16
26
|
/**
|
|
17
27
|
* Configure toast handler. Call once at app init.
|
|
18
28
|
*
|
|
@@ -30,11 +40,13 @@ interface TransitionMutationConfig<TData, TVariables> {
|
|
|
30
40
|
messages?: MutationMessages;
|
|
31
41
|
useTransition?: boolean;
|
|
32
42
|
showToast?: boolean;
|
|
43
|
+
/** Per-call toast guard — return false to suppress toast for this invocation */
|
|
44
|
+
shouldToast?: () => boolean;
|
|
33
45
|
toastHandler?: ToastHandler;
|
|
34
46
|
}
|
|
35
47
|
declare function useMutationWithTransition<TData, TVariables>(config: TransitionMutationConfig<TData, TVariables>): {
|
|
36
|
-
mutate:
|
|
37
|
-
mutateAsync:
|
|
48
|
+
mutate: UseMutateFunction<TData, Error, TVariables, unknown>;
|
|
49
|
+
mutateAsync: UseMutateAsyncFunction<TData, Error, TVariables, unknown>;
|
|
38
50
|
isPending: boolean;
|
|
39
51
|
isSuccess: boolean;
|
|
40
52
|
isError: boolean;
|
|
@@ -54,13 +66,13 @@ interface OptimisticMutationConfig<TData, TVariables> {
|
|
|
54
66
|
toastHandler?: ToastHandler;
|
|
55
67
|
}
|
|
56
68
|
declare function useMutationWithOptimistic<TData, TVariables>(config: OptimisticMutationConfig<TData, TVariables>): {
|
|
57
|
-
mutate:
|
|
69
|
+
mutate: UseMutateFunction<TData, Error, TVariables, {
|
|
58
70
|
previous: {
|
|
59
71
|
key: readonly unknown[];
|
|
60
72
|
data: unknown;
|
|
61
73
|
}[];
|
|
62
74
|
}>;
|
|
63
|
-
mutateAsync:
|
|
75
|
+
mutateAsync: UseMutateAsyncFunction<TData, Error, TVariables, {
|
|
64
76
|
previous: {
|
|
65
77
|
key: readonly unknown[];
|
|
66
78
|
data: unknown;
|
|
@@ -80,19 +92,13 @@ interface CreateOptimisticMutationConfig<TData, TVariables> {
|
|
|
80
92
|
optimisticUpdate?: (oldData: unknown, variables: TVariables) => unknown;
|
|
81
93
|
onSuccess?: (data: TData, variables: TVariables) => void;
|
|
82
94
|
onError?: (error: Error, variables: TVariables) => void;
|
|
95
|
+
onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;
|
|
83
96
|
messages?: MutationMessages;
|
|
97
|
+
/** Per-call toast guard — return false to suppress toast for this invocation */
|
|
98
|
+
shouldToast?: () => boolean;
|
|
84
99
|
toastHandler?: ToastHandler;
|
|
85
100
|
}
|
|
86
|
-
declare function createOptimisticMutation<TData, TVariables>({
|
|
87
|
-
mutationFn,
|
|
88
|
-
queryClient,
|
|
89
|
-
queryKeys,
|
|
90
|
-
optimisticUpdate,
|
|
91
|
-
onSuccess,
|
|
92
|
-
onError,
|
|
93
|
-
messages,
|
|
94
|
-
toastHandler: instanceToast
|
|
95
|
-
}: CreateOptimisticMutationConfig<TData, TVariables>): _tanstack_react_query0.UseMutationResult<TData, Error, TVariables, {
|
|
101
|
+
declare function createOptimisticMutation<TData, TVariables>(config: CreateOptimisticMutationConfig<TData, TVariables>): _tanstack_react_query0.UseMutationResult<TData, Error, TVariables, {
|
|
96
102
|
previous: {
|
|
97
103
|
key: readonly unknown[];
|
|
98
104
|
data: [readonly unknown[], unknown][];
|
|
@@ -114,5 +120,5 @@ declare const QUERY_CONFIGS: {
|
|
|
114
120
|
};
|
|
115
121
|
};
|
|
116
122
|
//#endregion
|
|
117
|
-
export { CreateOptimisticMutationConfig, MutationCallbacks, MutationMessages, OptimisticMutationConfig, QUERY_CONFIGS, type ToastHandler, TransitionMutationConfig, configureToast, createOptimisticMutation, useMutationWithOptimistic, useMutationWithTransition };
|
|
123
|
+
export { CreateOptimisticMutationConfig, MutationCallbacks, MutationMessages, OptimisticMutationConfig, QUERY_CONFIGS, type ToastHandler, TransitionMutationConfig, TransitionMutationReturn, configureToast, createOptimisticMutation, useMutationWithOptimistic, useMutationWithTransition };
|
|
118
124
|
//# sourceMappingURL=mutation.d.ts.map
|
package/dist/mutation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation.d.ts","names":[],"sources":["../src/mutation.ts"],"mappings":";;;;;UAciB,gBAAA;EACf,OAAA,cAAqB,IAAA,WAAe,SAAA;EACpC,KAAA,cAAmB,KAAA,EAAO,KAAA,EAAO,SAAA;AAAA;AAAA,UAGlB,iBAAA;EACf,QAAA,IAAY,SAAA,EAAW,UAAA,KAAe,QAAA,GAAW,OAAA,CAAQ,QAAA;EACzD,SAAA,IAAa,IAAA,EAAM,KAAA,EAAO,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,QAAA,YAAoB,OAAA;EAC9E,OAAA,IAAW,KAAA,EAAO,KAAA,EAAO,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,QAAA,YAAoB,OAAA;EAC7E,SAAA,IAAa,IAAA,EAAM,KAAA,cAAmB,KAAA,EAAO,KAAA,SAAc,SAAA,EAAW,UAAA,EAAY,OAAA,EAAS,QAAA,YAAoB,OAAA;AAAA
|
|
1
|
+
{"version":3,"file":"mutation.d.ts","names":[],"sources":["../src/mutation.ts"],"mappings":"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"}
|
package/dist/mutation.js
CHANGED
|
@@ -49,11 +49,11 @@ function useMutationWithTransition(config) {
|
|
|
49
49
|
};
|
|
50
50
|
if (withTransition && invalidateQueries.length > 0) startTransition(invalidate);
|
|
51
51
|
else invalidate();
|
|
52
|
-
if (toast) showToast("success", messages, data, variables, void 0, instanceToast);
|
|
52
|
+
if (toast && (config.shouldToast?.() ?? true)) showToast("success", messages, data, variables, void 0, instanceToast);
|
|
53
53
|
onSuccess?.(data, variables);
|
|
54
54
|
},
|
|
55
55
|
onError: (error, variables) => {
|
|
56
|
-
if (toast) showToast("error", messages, null, variables, error, instanceToast);
|
|
56
|
+
if (toast && (config.shouldToast?.() ?? true)) showToast("error", messages, null, variables, error, instanceToast);
|
|
57
57
|
onError?.(error, variables);
|
|
58
58
|
},
|
|
59
59
|
onSettled: (data, error, variables) => {
|
|
@@ -112,7 +112,8 @@ function useMutationWithOptimistic(config) {
|
|
|
112
112
|
reset: mutation.reset
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
|
-
function createOptimisticMutation(
|
|
115
|
+
function createOptimisticMutation(config) {
|
|
116
|
+
const { mutationFn, queryClient, queryKeys, optimisticUpdate, onSuccess, onError, onSettled, messages, toastHandler: instanceToast } = config;
|
|
116
117
|
return useMutation({
|
|
117
118
|
mutationFn,
|
|
118
119
|
onMutate: async (variables) => {
|
|
@@ -132,7 +133,7 @@ function createOptimisticMutation({ mutationFn, queryClient, queryKeys, optimist
|
|
|
132
133
|
return { previous };
|
|
133
134
|
},
|
|
134
135
|
onSuccess: (data, variables) => {
|
|
135
|
-
if (messages?.success) showToast("success", messages, data, variables, void 0, instanceToast);
|
|
136
|
+
if ((config.shouldToast?.() ?? true) && messages?.success) showToast("success", messages, data, variables, void 0, instanceToast);
|
|
136
137
|
queryKeys.forEach((key) => queryClient.invalidateQueries({ queryKey: key }));
|
|
137
138
|
onSuccess?.(data, variables);
|
|
138
139
|
},
|
|
@@ -140,8 +141,11 @@ function createOptimisticMutation({ mutationFn, queryClient, queryKeys, optimist
|
|
|
140
141
|
context?.previous?.forEach(({ data }) => {
|
|
141
142
|
data.forEach(([qKey, qData]) => queryClient.setQueryData(qKey, qData));
|
|
142
143
|
});
|
|
143
|
-
showToast("error", messages, null, variables, error, instanceToast);
|
|
144
|
+
if (config.shouldToast?.() ?? true) showToast("error", messages, null, variables, error, instanceToast);
|
|
144
145
|
onError?.(error, variables);
|
|
146
|
+
},
|
|
147
|
+
onSettled: (data, error, variables) => {
|
|
148
|
+
onSettled?.(data, error, variables);
|
|
145
149
|
}
|
|
146
150
|
});
|
|
147
151
|
}
|