@classytic/arc-next 0.3.1 → 0.4.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/dist/hooks.js CHANGED
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
 
3
- import { getAuthContext, getAuthMode } from "./client.js";
3
+ import { getAuthMode, getClientAuthContext } from "./client.js";
4
4
  import { isKeysetPagination, isOffsetPagination } from "./api.js";
5
5
  import { DEFAULT_QUERY_CONFIG, createCacheUtils, createQueryKeys, extractItem, getItemId, updateListCache, useDetailQuery, useInfiniteListQuery, useListQuery } from "./query.js";
6
6
  import { useMutationWithTransition, useOptimisticMutation } from "./mutation.js";
7
- import { useMutation, useQueryClient } from "@tanstack/react-query";
8
- import { useCallback, useMemo, useRef } from "react";
7
+ import { useQueryClient } from "@tanstack/react-query";
8
+ import { useCallback, useRef } from "react";
9
9
 
10
10
  //#region src/hooks.ts
11
11
  let useRouterHook = null;
@@ -21,11 +21,27 @@ let useRouterHook = null;
21
21
  function configureNavigation(hook) {
22
22
  useRouterHook = hook;
23
23
  }
24
- function createEnabledRule(token, options, authMode = getAuthMode()) {
24
+ function createEnabledRule(token, options, authMode = getAuthMode(), hasStaticAuth = false) {
25
25
  if (authMode === "cookie" || options.public) return options.enabled ?? true;
26
+ if (hasStaticAuth) return options.enabled ?? true;
26
27
  return options.enabled !== void 0 ? options.enabled && !!token : !!token;
27
28
  }
28
- function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks = {}, client }) {
29
+ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults = {}, callbacks = {}, client }) {
30
+ const pluralName = plural ?? `${singular}s`;
31
+ /** Resolve auth context — per-client auth takes priority over global */
32
+ const resolveAuth = () => getClientAuthContext(client);
33
+ /** Whether auth is provided via static config (headers, internalApiKey, per-client auth) — no token needed for enablement */
34
+ const hasStaticAuth = !!(client?.config?.defaultHeaders || client?.config?.internalApiKey || client?.auth);
35
+ /** Extract ID from an item using configured idField, falling back to _id → id */
36
+ function resolveItemId(item) {
37
+ if (!item || typeof item !== "object") return null;
38
+ const obj = item;
39
+ if (idField) {
40
+ const val = obj[idField];
41
+ if (val != null) return String(val);
42
+ }
43
+ return getItemId(item);
44
+ }
29
45
  const KEYS = createQueryKeys(entityKey);
30
46
  const cache = createCacheUtils(KEYS);
31
47
  const instanceToast = client?.toast;
@@ -49,12 +65,12 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
49
65
  let token;
50
66
  let params;
51
67
  let options;
52
- if (tokenOrParams === null || typeof tokenOrParams === "string") {
68
+ if (typeof tokenOrParams === "string" || tokenOrParams === null && maybeOptions !== void 0) {
53
69
  token = tokenOrParams;
54
70
  params = paramsOrOptions ?? {};
55
71
  options = maybeOptions ?? {};
56
72
  } else {
57
- const auth = getAuthContext();
73
+ const auth = resolveAuth();
58
74
  token = auth.token;
59
75
  params = tokenOrParams ?? {};
60
76
  options = paramsOrOptions ?? {};
@@ -80,7 +96,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
80
96
  ...requestOpts
81
97
  }
82
98
  }),
83
- enabled: createEnabledRule(token, queryOpts, resolveAuthMode()),
99
+ enabled: createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
84
100
  options: {
85
101
  staleTime: queryOpts.staleTime ?? config.staleTime,
86
102
  gcTime: queryOpts.gcTime ?? config.gcTime,
@@ -90,18 +106,19 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
90
106
  refetchIntervalInBackground: queryOpts.refetchIntervalInBackground
91
107
  },
92
108
  prefillDetailCache: queryOpts.prefillDetailCache ?? true,
93
- detailKeyBuilder: (id) => KEYS.detail(id),
109
+ detailKeyBuilder: (id) => KEYS.scopedDetail(id, organizationId ?? null),
110
+ itemIdResolver: resolveItemId,
94
111
  select: queryOpts.select
95
112
  });
96
113
  }
97
114
  function useDetail(id, tokenOrOptions, maybeOptions) {
98
115
  let token;
99
116
  let options;
100
- if (tokenOrOptions === null || typeof tokenOrOptions === "string") {
117
+ if (typeof tokenOrOptions === "string" || tokenOrOptions === null && maybeOptions !== void 0) {
101
118
  token = tokenOrOptions;
102
119
  options = maybeOptions ?? {};
103
120
  } else {
104
- const auth = getAuthContext();
121
+ const auth = resolveAuth();
105
122
  token = auth.token;
106
123
  options = tokenOrOptions ?? {};
107
124
  if (auth.organizationId && !options.organizationId) options = {
@@ -110,8 +127,9 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
110
127
  };
111
128
  }
112
129
  const { organizationId, params: queryParams, request: requestOpts, ...restOptions } = options;
130
+ const detailKey = KEYS.scopedDetail(id || "", organizationId ?? null);
113
131
  return useDetailQuery({
114
- queryKey: queryParams ? [...KEYS.detail(id || ""), queryParams] : KEYS.detail(id || ""),
132
+ queryKey: queryParams ? [...detailKey, queryParams] : detailKey,
115
133
  queryFn: ({ signal }) => api.getById({
116
134
  id,
117
135
  token,
@@ -122,7 +140,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
122
140
  ...requestOpts
123
141
  }
124
142
  }),
125
- enabled: !!id && createEnabledRule(token, restOptions, resolveAuthMode()),
143
+ enabled: !!id && createEnabledRule(token, restOptions, resolveAuthMode(), hasStaticAuth),
126
144
  options: {
127
145
  staleTime: restOptions.staleTime ?? config.staleTime,
128
146
  gcTime: restOptions.gcTime ?? config.gcTime,
@@ -151,7 +169,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
151
169
  const optimisticItem = {
152
170
  ...data,
153
171
  _optimistic: true,
154
- [getItemId(data) ? "id" : "_id"]: getItemId(data) ?? `temp-${Date.now()}`
172
+ [idField ?? (resolveItemId(data) ? "id" : "_id")]: resolveItemId(data) ?? `temp-${Date.now()}`
155
173
  };
156
174
  return updateListCache(oldData, (arr) => [optimisticItem, ...arr || []]);
157
175
  },
@@ -181,17 +199,20 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
181
199
  queryKeys: [KEYS.lists(), KEYS.details()],
182
200
  shouldToast,
183
201
  optimisticUpdate: (oldData, { id, data }) => {
184
- const updated = updateListCache(oldData, (arr) => (arr || []).map((item) => getItemId(item) === id ? {
202
+ const updated = updateListCache(oldData, (arr) => (arr || []).map((item) => resolveItemId(item) === id ? {
185
203
  ...item,
186
204
  ...data
187
205
  } : item));
188
- queryClient.setQueryData(KEYS.detail(id), (current) => current ? {
206
+ const detailUpdater = (current) => current ? {
189
207
  ...current,
190
208
  data: {
191
209
  ...current.data || {},
192
210
  ...data
193
211
  }
194
- } : current);
212
+ } : current;
213
+ queryClient.getQueriesData({ queryKey: KEYS.detail(id) }).forEach(([qKey, qData]) => {
214
+ if (qData) queryClient.setQueryData(qKey, detailUpdater);
215
+ });
195
216
  return updated;
196
217
  },
197
218
  onSuccess: (raw, { id, data: updateData }) => {
@@ -229,7 +250,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
229
250
  queryKeys: [KEYS.lists()],
230
251
  shouldToast,
231
252
  optimisticUpdate: (oldData, { id }) => {
232
- return updateListCache(oldData, (arr) => (arr || []).filter((item) => getItemId(item) !== id));
253
+ return updateListCache(oldData, (arr) => (arr || []).filter((item) => resolveItemId(item) !== id));
233
254
  },
234
255
  onSuccess: (data, { id }) => {
235
256
  queryClient.removeQueries({ queryKey: KEYS.detail(id) });
@@ -247,8 +268,25 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
247
268
  },
248
269
  toastHandler: instanceToast
249
270
  });
250
- const resolveAuth = useCallback((params) => {
251
- const auth = getAuthContext();
271
+ const restoreMutation = useMutationWithTransition({
272
+ mutationFn: ({ token, organizationId, id }) => {
273
+ if (!api.restore) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a restore method`));
274
+ return api.restore({
275
+ token,
276
+ organizationId,
277
+ id
278
+ });
279
+ },
280
+ invalidateQueries: [KEYS.lists(), KEYS.custom("deleted")],
281
+ shouldToast,
282
+ messages: {
283
+ success: `${singular} restored successfully`,
284
+ error: `Failed to restore ${singular.toLowerCase()}`
285
+ },
286
+ toastHandler: instanceToast
287
+ });
288
+ const resolveActionAuth = useCallback((params) => {
289
+ const auth = resolveAuth();
252
290
  return {
253
291
  ...params,
254
292
  token: params.token ?? auth.token,
@@ -259,7 +297,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
259
297
  create: useCallback(async (params, options) => {
260
298
  silentRef.current = options?.silent ?? false;
261
299
  try {
262
- const entity = extractItem(await createMutation.mutateAsync(resolveAuth(params)));
300
+ const entity = extractItem(await createMutation.mutateAsync(resolveActionAuth(params)));
263
301
  options?.onSuccess?.(entity);
264
302
  options?.onSettled?.(entity, null);
265
303
  return entity;
@@ -270,11 +308,11 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
270
308
  } finally {
271
309
  silentRef.current = false;
272
310
  }
273
- }, [createMutation, resolveAuth]),
311
+ }, [createMutation, resolveActionAuth]),
274
312
  update: useCallback(async (params, options) => {
275
313
  silentRef.current = options?.silent ?? false;
276
314
  try {
277
- const entity = extractItem(await updateMutation.mutateAsync(resolveAuth(params)));
315
+ const entity = extractItem(await updateMutation.mutateAsync(resolveActionAuth(params)));
278
316
  options?.onSuccess?.(entity);
279
317
  options?.onSettled?.(entity, null);
280
318
  return entity;
@@ -285,11 +323,11 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
285
323
  } finally {
286
324
  silentRef.current = false;
287
325
  }
288
- }, [updateMutation, resolveAuth]),
326
+ }, [updateMutation, resolveActionAuth]),
289
327
  remove: useCallback(async (params, options) => {
290
328
  silentRef.current = options?.silent ?? false;
291
329
  try {
292
- const result = await deleteMutation.mutateAsync(resolveAuth(params));
330
+ const result = await deleteMutation.mutateAsync(resolveActionAuth(params));
293
331
  options?.onSuccess?.(result);
294
332
  options?.onSettled?.(result, null);
295
333
  return result;
@@ -300,23 +338,39 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
300
338
  } finally {
301
339
  silentRef.current = false;
302
340
  }
303
- }, [deleteMutation, resolveAuth]),
341
+ }, [deleteMutation, resolveActionAuth]),
342
+ restore: useCallback(async (params, options) => {
343
+ silentRef.current = options?.silent ?? false;
344
+ try {
345
+ const entity = extractItem(await restoreMutation.mutateAsync(resolveActionAuth(params)));
346
+ options?.onSuccess?.(entity);
347
+ options?.onSettled?.(entity, null);
348
+ return entity;
349
+ } catch (error) {
350
+ options?.onError?.(error);
351
+ options?.onSettled?.(void 0, error);
352
+ throw error;
353
+ } finally {
354
+ silentRef.current = false;
355
+ }
356
+ }, [restoreMutation, resolveActionAuth]),
304
357
  isCreating: createMutation.isPending,
305
358
  isUpdating: updateMutation.isPending,
306
359
  isDeleting: deleteMutation.isPending,
307
- isMutating: createMutation.isPending || updateMutation.isPending || deleteMutation.isPending
360
+ isRestoring: restoreMutation.isPending,
361
+ isMutating: createMutation.isPending || updateMutation.isPending || deleteMutation.isPending || restoreMutation.isPending
308
362
  };
309
363
  }
310
364
  function useInfiniteList(tokenOrParams, paramsOrOptions, maybeOptions) {
311
365
  let token;
312
366
  let params;
313
367
  let options;
314
- if (tokenOrParams === null || typeof tokenOrParams === "string") {
368
+ if (typeof tokenOrParams === "string" || tokenOrParams === null && maybeOptions !== void 0) {
315
369
  token = tokenOrParams;
316
370
  params = paramsOrOptions ?? {};
317
371
  options = maybeOptions ?? {};
318
372
  } else {
319
- const auth = getAuthContext();
373
+ const auth = resolveAuth();
320
374
  token = auth.token;
321
375
  params = tokenOrParams ?? {};
322
376
  options = paramsOrOptions ?? {};
@@ -348,7 +402,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
348
402
  }
349
403
  });
350
404
  },
351
- enabled: createEnabledRule(token, queryOpts, resolveAuthMode()),
405
+ enabled: createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
352
406
  initialPageParam: restParams.after ? restParams.after : 1,
353
407
  getNextPageParam: (lastPage) => {
354
408
  const page = lastPage;
@@ -357,6 +411,13 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
357
411
  const p = page;
358
412
  if (typeof p.hasNext === "boolean" && typeof p.page === "number") return p.hasNext ? p.page + 1 : void 0;
359
413
  },
414
+ getPreviousPageParam: queryOpts.maxPages != null ? (firstPage) => {
415
+ const page = firstPage;
416
+ if (isOffsetPagination(page)) return page.hasPrev ? page.page - 1 : void 0;
417
+ const p = page;
418
+ if (typeof p.hasPrev === "boolean" && typeof p.page === "number") return p.hasPrev ? p.page - 1 : void 0;
419
+ } : void 0,
420
+ maxPages: queryOpts.maxPages,
360
421
  options: {
361
422
  staleTime: queryOpts.staleTime ?? config.staleTime,
362
423
  gcTime: queryOpts.gcTime ?? config.gcTime,
@@ -371,7 +432,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
371
432
  return useMutationWithTransition({
372
433
  mutationFn: ({ data, id, path }) => {
373
434
  if (!api.upload) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define an upload method`));
374
- const auth = getAuthContext();
435
+ const auth = resolveAuth();
375
436
  return api.upload({
376
437
  token: auth.token,
377
438
  organizationId: auth.organizationId,
@@ -392,9 +453,7 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
392
453
  });
393
454
  }
394
455
  function useSearch(query, params, options) {
395
- if (!api.search) throw new Error(`[arc-next] "${entityKey}" api does not define a search method`);
396
- const searchApi = api.search;
397
- const auth = getAuthContext();
456
+ const auth = resolveAuth();
398
457
  const token = params?.token ?? auth.token;
399
458
  const organizationId = params?.organizationId ?? auth.organizationId;
400
459
  const { organizationId: _, token: _t, ...restParams } = params ?? {};
@@ -413,16 +472,19 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
413
472
  "search",
414
473
  searchKeyParams
415
474
  ],
416
- queryFn: ({ signal }) => searchApi({
417
- token,
418
- organizationId,
419
- params: searchParams,
420
- options: {
421
- signal,
422
- ...requestOpts
423
- }
424
- }),
425
- enabled: query.length > 0 && createEnabledRule(token, queryOpts, resolveAuthMode()),
475
+ queryFn: ({ signal }) => {
476
+ if (!api.search) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a search method`));
477
+ return api.search({
478
+ token,
479
+ organizationId,
480
+ params: searchParams,
481
+ options: {
482
+ signal,
483
+ ...requestOpts
484
+ }
485
+ });
486
+ },
487
+ enabled: !!api.search && query.length > 0 && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
426
488
  options: {
427
489
  staleTime: queryOpts.staleTime ?? config.staleTime,
428
490
  gcTime: queryOpts.gcTime ?? config.gcTime
@@ -441,6 +503,248 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
441
503
  toastHandler: instanceToast
442
504
  });
443
505
  }
506
+ function useDeleted(params, options) {
507
+ const auth = resolveAuth();
508
+ const token = auth.token;
509
+ const mergedParams = params ?? {};
510
+ const organizationId = mergedParams.organizationId ?? auth.organizationId;
511
+ const { organizationId: _, ...restParams } = mergedParams;
512
+ const { request: requestOpts, ...queryOpts } = options ?? {};
513
+ return useListQuery({
514
+ queryKey: KEYS.custom("deleted", {
515
+ organizationId,
516
+ ...restParams
517
+ }),
518
+ queryFn: ({ signal }) => {
519
+ if (!api.getDeleted) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getDeleted method`));
520
+ return api.getDeleted({
521
+ token,
522
+ organizationId,
523
+ params: restParams,
524
+ options: {
525
+ signal,
526
+ ...requestOpts
527
+ }
528
+ });
529
+ },
530
+ enabled: !!api.getDeleted && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
531
+ options: {
532
+ staleTime: queryOpts.staleTime ?? config.staleTime,
533
+ gcTime: queryOpts.gcTime ?? config.gcTime
534
+ },
535
+ select: queryOpts.select
536
+ });
537
+ }
538
+ function useDetailBySlug(slug, options) {
539
+ const auth = resolveAuth();
540
+ const token = auth.token;
541
+ const resolvedOptions = options ?? {};
542
+ const organizationId = resolvedOptions.organizationId ?? auth.organizationId;
543
+ const { params: queryParams, request: requestOpts, ...restOptions } = resolvedOptions;
544
+ return useDetailQuery({
545
+ queryKey: queryParams ? KEYS.custom("slug", slug, queryParams) : KEYS.custom("slug", slug),
546
+ queryFn: ({ signal }) => {
547
+ if (!api.getBySlug) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getBySlug method`));
548
+ return api.getBySlug({
549
+ slug,
550
+ token,
551
+ organizationId,
552
+ params: queryParams,
553
+ options: {
554
+ signal,
555
+ ...requestOpts
556
+ }
557
+ });
558
+ },
559
+ enabled: !!api.getBySlug && !!slug && createEnabledRule(token, restOptions, resolveAuthMode(), hasStaticAuth),
560
+ options: {
561
+ staleTime: restOptions.staleTime ?? config.staleTime,
562
+ gcTime: restOptions.gcTime ?? config.gcTime,
563
+ refetchOnWindowFocus: restOptions.refetchOnWindowFocus ?? config.refetchOnWindowFocus,
564
+ structuralSharing: restOptions.structuralSharing ?? config.structuralSharing
565
+ },
566
+ select: restOptions.select
567
+ });
568
+ }
569
+ function useTree(params, options) {
570
+ const auth = resolveAuth();
571
+ const token = auth.token;
572
+ const mergedParams = params ?? {};
573
+ const organizationId = mergedParams.organizationId ?? auth.organizationId;
574
+ const { organizationId: _, ...restParams } = mergedParams;
575
+ const { request: requestOpts, ...queryOpts } = options ?? {};
576
+ return useListQuery({
577
+ queryKey: KEYS.custom("tree", {
578
+ organizationId,
579
+ ...restParams
580
+ }),
581
+ queryFn: ({ signal }) => {
582
+ if (!api.getTree) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getTree method`));
583
+ return api.getTree({
584
+ token,
585
+ organizationId,
586
+ params: restParams,
587
+ options: {
588
+ signal,
589
+ ...requestOpts
590
+ }
591
+ });
592
+ },
593
+ enabled: !!api.getTree && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
594
+ options: {
595
+ staleTime: queryOpts.staleTime ?? config.staleTime,
596
+ gcTime: queryOpts.gcTime ?? config.gcTime
597
+ },
598
+ select: queryOpts.select
599
+ });
600
+ }
601
+ function useChildren(parentId, params, options) {
602
+ const auth = resolveAuth();
603
+ const token = auth.token;
604
+ const mergedParams = params ?? {};
605
+ const organizationId = mergedParams.organizationId ?? auth.organizationId;
606
+ const { organizationId: _, ...restParams } = mergedParams;
607
+ const { request: requestOpts, ...queryOpts } = options ?? {};
608
+ return useListQuery({
609
+ queryKey: KEYS.custom("children", parentId, {
610
+ organizationId,
611
+ ...restParams
612
+ }),
613
+ queryFn: ({ signal }) => {
614
+ if (!api.getChildren) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getChildren method`));
615
+ return api.getChildren({
616
+ token,
617
+ organizationId,
618
+ parentId,
619
+ params: restParams,
620
+ options: {
621
+ signal,
622
+ ...requestOpts
623
+ }
624
+ });
625
+ },
626
+ enabled: !!api.getChildren && !!parentId && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
627
+ options: {
628
+ staleTime: queryOpts.staleTime ?? config.staleTime,
629
+ gcTime: queryOpts.gcTime ?? config.gcTime
630
+ },
631
+ prefillDetailCache: queryOpts.prefillDetailCache ?? true,
632
+ detailKeyBuilder: (id) => KEYS.scopedDetail(id, organizationId ?? null),
633
+ itemIdResolver: resolveItemId,
634
+ select: queryOpts.select
635
+ });
636
+ }
637
+ function useFindBy(field, value, options) {
638
+ const auth = resolveAuth();
639
+ const token = auth.token;
640
+ const organizationId = auth.organizationId;
641
+ const { operator, request: requestOpts, ...queryOpts } = options ?? {};
642
+ return useListQuery({
643
+ queryKey: KEYS.custom("findBy", {
644
+ field,
645
+ value,
646
+ operator,
647
+ organizationId
648
+ }),
649
+ queryFn: ({ signal }) => {
650
+ if (!api.findBy) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a findBy method`));
651
+ return api.findBy({
652
+ token,
653
+ organizationId,
654
+ field,
655
+ value,
656
+ operator,
657
+ options: {
658
+ signal,
659
+ ...requestOpts
660
+ }
661
+ });
662
+ },
663
+ enabled: !!api.findBy && value !== void 0 && value !== null && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
664
+ options: {
665
+ staleTime: queryOpts.staleTime ?? config.staleTime,
666
+ gcTime: queryOpts.gcTime ?? config.gcTime
667
+ },
668
+ prefillDetailCache: queryOpts.prefillDetailCache ?? true,
669
+ detailKeyBuilder: (id) => KEYS.scopedDetail(id, organizationId ?? null),
670
+ itemIdResolver: resolveItemId,
671
+ select: queryOpts.select
672
+ });
673
+ }
674
+ function useBulkActions() {
675
+ const bulkCreateMutation = useMutationWithTransition({
676
+ mutationFn: (vars) => {
677
+ if (!api.bulkCreate) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkCreate method`));
678
+ const auth = resolveAuth();
679
+ return api.bulkCreate({
680
+ token: vars.token ?? auth.token,
681
+ organizationId: vars.organizationId ?? auth.organizationId,
682
+ data: vars.data
683
+ });
684
+ },
685
+ invalidateQueries: [KEYS.lists()],
686
+ messages: {
687
+ success: `${pluralName} created successfully`,
688
+ error: `Failed to create ${pluralName.toLowerCase()}`
689
+ },
690
+ toastHandler: instanceToast
691
+ });
692
+ const bulkUpdateMutation = useMutationWithTransition({
693
+ mutationFn: (vars) => {
694
+ if (!api.bulkUpdate) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkUpdate method`));
695
+ const auth = resolveAuth();
696
+ return api.bulkUpdate({
697
+ token: vars.token ?? auth.token,
698
+ organizationId: vars.organizationId ?? auth.organizationId,
699
+ filter: vars.filter,
700
+ data: vars.data
701
+ });
702
+ },
703
+ invalidateQueries: [KEYS.lists(), KEYS.details()],
704
+ messages: {
705
+ success: `${pluralName} updated successfully`,
706
+ error: `Failed to update ${pluralName.toLowerCase()}`
707
+ },
708
+ toastHandler: instanceToast
709
+ });
710
+ const bulkDeleteMutation = useMutationWithTransition({
711
+ mutationFn: (vars) => {
712
+ if (!api.bulkDelete) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkDelete method`));
713
+ const auth = resolveAuth();
714
+ return api.bulkDelete({
715
+ token: vars.token ?? auth.token,
716
+ organizationId: vars.organizationId ?? auth.organizationId,
717
+ filter: vars.filter
718
+ });
719
+ },
720
+ invalidateQueries: [KEYS.lists()],
721
+ messages: {
722
+ success: `${pluralName} deleted successfully`,
723
+ error: `Failed to delete ${pluralName.toLowerCase()}`
724
+ },
725
+ toastHandler: instanceToast
726
+ });
727
+ return {
728
+ bulkCreate: async (params, options) => {
729
+ const items = (await bulkCreateMutation.mutateAsync(params))?.data ?? [];
730
+ options?.onSuccess?.(items);
731
+ return items;
732
+ },
733
+ bulkUpdate: async (params, options) => {
734
+ const result = await bulkUpdateMutation.mutateAsync(params);
735
+ options?.onSuccess?.(result);
736
+ return result;
737
+ },
738
+ bulkRemove: async (params, options) => {
739
+ const result = await bulkDeleteMutation.mutateAsync(params);
740
+ options?.onSuccess?.(result);
741
+ return result;
742
+ },
743
+ isBulkCreating: bulkCreateMutation.isPending,
744
+ isBulkUpdating: bulkUpdateMutation.isPending,
745
+ isBulkDeleting: bulkDeleteMutation.isPending
746
+ };
747
+ }
444
748
  const resolvedRouterHook = instanceNavigation ?? useRouterHook ?? (() => ({
445
749
  push: () => {},
446
750
  replace: () => {}
@@ -449,8 +753,12 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
449
753
  const queryClient = useQueryClient();
450
754
  const router = resolvedRouterHook();
451
755
  return useCallback((href, item, options = {}) => {
452
- const id = getItemId(item);
453
- if (id) queryClient.setQueryData(KEYS.detail(id), { data: item });
756
+ const id = resolveItemId(item);
757
+ if (id) {
758
+ const orgId = resolveAuth().organizationId;
759
+ queryClient.setQueryData(KEYS.scopedDetail(id, orgId), { data: item });
760
+ if (orgId) queryClient.setQueryData(KEYS.detail(id), { data: item });
761
+ }
454
762
  if (!router) return;
455
763
  const { scroll = true, replace = false } = options;
456
764
  if (replace) router.replace(href, { scroll });
@@ -464,6 +772,12 @@ function createCrudHooks({ api, entityKey, singular, defaults = {}, callbacks =
464
772
  useDetail,
465
773
  useInfiniteList,
466
774
  useActions,
775
+ useBulkActions,
776
+ useDeleted,
777
+ useDetailBySlug,
778
+ useTree,
779
+ useChildren,
780
+ useFindBy,
467
781
  useUpload,
468
782
  useSearch,
469
783
  useCustomMutation,
@@ -1,5 +1,5 @@
1
1
  import { ToastHandler } from "./client.js";
2
- import * as _tanstack_react_query0 from "@tanstack/react-query";
2
+ import * as _$_tanstack_react_query0 from "@tanstack/react-query";
3
3
  import { QueryClient, QueryKey, UseMutateAsyncFunction, UseMutateFunction } from "@tanstack/react-query";
4
4
 
5
5
  //#region src/mutation.d.ts
@@ -47,8 +47,8 @@ interface TransitionMutationConfig<TData, TVariables> {
47
47
  toastHandler?: ToastHandler;
48
48
  }
49
49
  declare function useMutationWithTransition<TData, TVariables>(config: TransitionMutationConfig<TData, TVariables>): {
50
- mutate: UseMutateFunction<TData, Error, TVariables, unknown>;
51
- mutateAsync: UseMutateAsyncFunction<TData, Error, TVariables, unknown>;
50
+ mutate: UseMutateFunction<TData, Error, TVariables, void>;
51
+ mutateAsync: UseMutateAsyncFunction<TData, Error, TVariables, void>;
52
52
  isPending: boolean;
53
53
  isSuccess: boolean;
54
54
  isError: boolean;
@@ -100,28 +100,11 @@ interface CreateOptimisticMutationConfig<TData, TVariables> {
100
100
  shouldToast?: () => boolean;
101
101
  toastHandler?: ToastHandler;
102
102
  }
103
- declare function useOptimisticMutation<TData, TVariables>(config: CreateOptimisticMutationConfig<TData, TVariables>): _tanstack_react_query0.UseMutationResult<TData, Error, TVariables, {
103
+ declare function useOptimisticMutation<TData, TVariables>(config: CreateOptimisticMutationConfig<TData, TVariables>): _$_tanstack_react_query0.UseMutationResult<TData, Error, TVariables, {
104
104
  previous: {
105
105
  key: readonly unknown[];
106
106
  data: [readonly unknown[], unknown][];
107
107
  }[];
108
108
  }>;
109
- declare const QUERY_CONFIGS: {
110
- readonly realtime: {
111
- readonly staleTime: 20000;
112
- readonly refetchInterval: 30000;
113
- };
114
- readonly frequent: {
115
- readonly staleTime: 60000;
116
- };
117
- readonly stable: {
118
- readonly staleTime: 300000;
119
- };
120
- readonly static: {
121
- readonly staleTime: 600000;
122
- };
123
- };
124
- /** @deprecated Use `useOptimisticMutation` */
125
- declare const createOptimisticMutation: typeof useOptimisticMutation;
126
109
  //#endregion
127
- export { CreateOptimisticMutationConfig, MutationCallbacks, MutationMessages, OptimisticMutationConfig, QUERY_CONFIGS, TransitionMutationConfig, TransitionMutationReturn, configureToast, createOptimisticMutation, useMutationWithOptimistic, useMutationWithTransition, useOptimisticMutation };
110
+ export { CreateOptimisticMutationConfig, MutationCallbacks, MutationMessages, OptimisticMutationConfig, TransitionMutationConfig, TransitionMutationReturn, configureToast, useMutationWithOptimistic, useMutationWithTransition, useOptimisticMutation };