@classytic/arc-next 0.2.1 → 0.4.0

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
@@ -2,9 +2,9 @@
2
2
 
3
3
  import { getAuthContext, getAuthMode } from "./client.js";
4
4
  import { isKeysetPagination, isOffsetPagination } from "./api.js";
5
- import { DEFAULT_QUERY_CONFIG, createCacheUtils, createDetailQuery, createInfiniteListQuery, createListQuery, createQueryKeys, getItemId, updateListCache } from "./query.js";
6
- import { createOptimisticMutation, useMutationWithTransition } from "./mutation.js";
7
- import { useMutation, useQueryClient } from "@tanstack/react-query";
5
+ import { DEFAULT_QUERY_CONFIG, createCacheUtils, createQueryKeys, extractItem, getItemId, updateListCache, useDetailQuery, useInfiniteListQuery, useListQuery } from "./query.js";
6
+ import { useMutationWithTransition, useOptimisticMutation } from "./mutation.js";
7
+ import { useQueryClient } from "@tanstack/react-query";
8
8
  import { useCallback, useRef } from "react";
9
9
 
10
10
  //#region src/hooks.ts
@@ -12,6 +12,8 @@ let useRouterHook = null;
12
12
  /**
13
13
  * Configure the router hook for useNavigation. Call once at app init.
14
14
  *
15
+ * **SSR safety:** This sets module-level state. Call only in client-side code.
16
+ *
15
17
  * @example
16
18
  * import { useRouter } from "next/navigation";
17
19
  * configureNavigation(useRouter);
@@ -19,16 +21,27 @@ let useRouterHook = null;
19
21
  function configureNavigation(hook) {
20
22
  useRouterHook = hook;
21
23
  }
22
- function createEnabledRule(token, options) {
23
- if (getAuthMode() === "cookie" || options.public) return options.enabled ?? true;
24
+ function createEnabledRule(token, options, authMode = getAuthMode()) {
25
+ if (authMode === "cookie" || options.public) return options.enabled ?? true;
24
26
  return options.enabled !== void 0 ? options.enabled && !!token : !!token;
25
27
  }
26
- function createCrudHooks({ api, entityKey, singular, platformScoped, defaults = {}, callbacks = {}, client }) {
27
- const isPlatform = platformScoped ?? api.scope === "platform";
28
+ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults = {}, callbacks = {}, client }) {
29
+ const pluralName = plural ?? `${singular}s`;
30
+ /** Extract ID from an item using configured idField, falling back to _id → id */
31
+ function resolveItemId(item) {
32
+ if (!item || typeof item !== "object") return null;
33
+ const obj = item;
34
+ if (idField) {
35
+ const val = obj[idField];
36
+ if (val != null) return String(val);
37
+ }
38
+ return getItemId(item);
39
+ }
28
40
  const KEYS = createQueryKeys(entityKey);
29
41
  const cache = createCacheUtils(KEYS);
30
42
  const instanceToast = client?.toast;
31
43
  const instanceNavigation = client?.navigation ?? null;
44
+ const resolveAuthMode = () => client?.config?.authMode ?? getAuthMode();
32
45
  const config = {
33
46
  ...DEFAULT_QUERY_CONFIG,
34
47
  ...defaults,
@@ -56,15 +69,15 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
56
69
  token = auth.token;
57
70
  params = tokenOrParams ?? {};
58
71
  options = paramsOrOptions ?? {};
59
- if (!isPlatform && auth.organizationId && !params.organizationId) params = {
72
+ if (auth.organizationId && !params.organizationId) params = {
60
73
  ...params,
61
74
  organizationId: auth.organizationId
62
75
  };
63
76
  }
64
77
  const { organizationId, ...restParams } = params;
65
- const scope = options._scope || (isPlatform ? "platform" : organizationId ? "tenant" : "super-admin");
78
+ const scope = options._scope || (organizationId ? "tenant" : "super-admin");
66
79
  const { request: requestOpts, ...queryOpts } = options;
67
- return createListQuery({
80
+ return useListQuery({
68
81
  queryKey: KEYS.scopedList(scope, {
69
82
  organizationId,
70
83
  ...restParams
@@ -78,7 +91,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
78
91
  ...requestOpts
79
92
  }
80
93
  }),
81
- enabled: createEnabledRule(token, queryOpts),
94
+ enabled: createEnabledRule(token, queryOpts, resolveAuthMode()),
82
95
  options: {
83
96
  staleTime: queryOpts.staleTime ?? config.staleTime,
84
97
  gcTime: queryOpts.gcTime ?? config.gcTime,
@@ -89,6 +102,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
89
102
  },
90
103
  prefillDetailCache: queryOpts.prefillDetailCache ?? true,
91
104
  detailKeyBuilder: (id) => KEYS.detail(id),
105
+ itemIdResolver: resolveItemId,
92
106
  select: queryOpts.select
93
107
  });
94
108
  }
@@ -102,13 +116,13 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
102
116
  const auth = getAuthContext();
103
117
  token = auth.token;
104
118
  options = tokenOrOptions ?? {};
105
- if (!isPlatform && auth.organizationId && !options.organizationId) options = {
119
+ if (auth.organizationId && !options.organizationId) options = {
106
120
  ...options,
107
121
  organizationId: auth.organizationId
108
122
  };
109
123
  }
110
124
  const { organizationId, params: queryParams, request: requestOpts, ...restOptions } = options;
111
- return createDetailQuery({
125
+ return useDetailQuery({
112
126
  queryKey: queryParams ? [...KEYS.detail(id || ""), queryParams] : KEYS.detail(id || ""),
113
127
  queryFn: ({ signal }) => api.getById({
114
128
  id,
@@ -120,10 +134,11 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
120
134
  ...requestOpts
121
135
  }
122
136
  }),
123
- enabled: !!id && createEnabledRule(token, restOptions),
137
+ enabled: !!id && createEnabledRule(token, restOptions, resolveAuthMode()),
124
138
  options: {
125
139
  staleTime: restOptions.staleTime ?? config.staleTime,
126
140
  gcTime: restOptions.gcTime ?? config.gcTime,
141
+ refetchOnWindowFocus: restOptions.refetchOnWindowFocus ?? config.refetchOnWindowFocus,
127
142
  structuralSharing: restOptions.structuralSharing ?? config.structuralSharing,
128
143
  refetchInterval: restOptions.refetchInterval,
129
144
  refetchIntervalInBackground: restOptions.refetchIntervalInBackground
@@ -135,7 +150,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
135
150
  const queryClient = useQueryClient();
136
151
  const silentRef = useRef(false);
137
152
  const shouldToast = useCallback(() => !silentRef.current, []);
138
- const createMutation = createOptimisticMutation({
153
+ const createMutation = useOptimisticMutation({
139
154
  mutationFn: ({ token, organizationId, data }) => api.create({
140
155
  token,
141
156
  organizationId,
@@ -148,18 +163,18 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
148
163
  const optimisticItem = {
149
164
  ...data,
150
165
  _optimistic: true,
151
- [getItemId(data) ? "id" : "_id"]: getItemId(data) ?? `temp-${Date.now()}`
166
+ [idField ?? (resolveItemId(data) ? "id" : "_id")]: resolveItemId(data) ?? `temp-${Date.now()}`
152
167
  };
153
168
  return updateListCache(oldData, (arr) => [optimisticItem, ...arr || []]);
154
169
  },
155
- onSuccess: (data, variables) => {
156
- callbacks.onCreate?.onSuccess?.(data, { data: variables.data }, void 0);
170
+ onSuccess: (raw, variables) => {
171
+ callbacks.onCreate?.onSuccess?.(extractItem(raw), { data: variables.data }, void 0);
157
172
  },
158
173
  onError: (error, variables) => {
159
174
  callbacks.onCreate?.onError?.(error, { data: variables.data }, void 0);
160
175
  },
161
- onSettled: (data, error, variables) => {
162
- callbacks.onCreate?.onSettled?.(data, error, { data: variables.data }, void 0);
176
+ onSettled: (raw, error, variables) => {
177
+ callbacks.onCreate?.onSettled?.(raw ? extractItem(raw) : void 0, error, { data: variables.data }, void 0);
163
178
  },
164
179
  messages: {
165
180
  success: config.messages.createSuccess,
@@ -167,7 +182,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
167
182
  },
168
183
  toastHandler: instanceToast
169
184
  });
170
- const updateMutation = createOptimisticMutation({
185
+ const updateMutation = useOptimisticMutation({
171
186
  mutationFn: ({ token, organizationId, id, data }) => api.update({
172
187
  token,
173
188
  organizationId,
@@ -175,10 +190,10 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
175
190
  data
176
191
  }),
177
192
  queryClient,
178
- queryKeys: [KEYS.lists()],
193
+ queryKeys: [KEYS.lists(), KEYS.details()],
179
194
  shouldToast,
180
195
  optimisticUpdate: (oldData, { id, data }) => {
181
- const updated = updateListCache(oldData, (arr) => (arr || []).map((item) => getItemId(item) === id ? {
196
+ const updated = updateListCache(oldData, (arr) => (arr || []).map((item) => resolveItemId(item) === id ? {
182
197
  ...item,
183
198
  ...data
184
199
  } : item));
@@ -191,9 +206,9 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
191
206
  } : current);
192
207
  return updated;
193
208
  },
194
- onSuccess: (data, { id, data: updateData }) => {
209
+ onSuccess: (raw, { id, data: updateData }) => {
195
210
  queryClient.invalidateQueries({ queryKey: KEYS.detail(id) });
196
- callbacks.onUpdate?.onSuccess?.(data, {
211
+ callbacks.onUpdate?.onSuccess?.(extractItem(raw), {
197
212
  id,
198
213
  data: updateData
199
214
  }, void 0);
@@ -204,8 +219,8 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
204
219
  data
205
220
  }, void 0);
206
221
  },
207
- onSettled: (data, error, { id, data: updateData }) => {
208
- callbacks.onUpdate?.onSettled?.(data, error, {
222
+ onSettled: (raw, error, { id, data: updateData }) => {
223
+ callbacks.onUpdate?.onSettled?.(raw ? extractItem(raw) : void 0, error, {
209
224
  id,
210
225
  data: updateData
211
226
  }, void 0);
@@ -216,7 +231,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
216
231
  },
217
232
  toastHandler: instanceToast
218
233
  });
219
- const deleteMutation = createOptimisticMutation({
234
+ const deleteMutation = useOptimisticMutation({
220
235
  mutationFn: ({ token, organizationId, id }) => api.delete({
221
236
  token,
222
237
  organizationId,
@@ -226,10 +241,10 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
226
241
  queryKeys: [KEYS.lists()],
227
242
  shouldToast,
228
243
  optimisticUpdate: (oldData, { id }) => {
229
- queryClient.removeQueries({ queryKey: KEYS.detail(id) });
230
- return updateListCache(oldData, (arr) => (arr || []).filter((item) => getItemId(item) !== id));
244
+ return updateListCache(oldData, (arr) => (arr || []).filter((item) => resolveItemId(item) !== id));
231
245
  },
232
246
  onSuccess: (data, { id }) => {
247
+ queryClient.removeQueries({ queryKey: KEYS.detail(id) });
233
248
  callbacks.onDelete?.onSuccess?.(data, { id }, void 0);
234
249
  },
235
250
  onError: (error, { id }) => {
@@ -244,67 +259,97 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
244
259
  },
245
260
  toastHandler: instanceToast
246
261
  });
247
- const resolveAuth = (params) => {
262
+ const restoreMutation = useMutationWithTransition({
263
+ mutationFn: ({ token, organizationId, id }) => {
264
+ if (!api.restore) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a restore method`));
265
+ return api.restore({
266
+ token,
267
+ organizationId,
268
+ id
269
+ });
270
+ },
271
+ invalidateQueries: [KEYS.lists(), KEYS.custom("deleted")],
272
+ shouldToast,
273
+ messages: {
274
+ success: `${singular} restored successfully`,
275
+ error: `Failed to restore ${singular.toLowerCase()}`
276
+ },
277
+ toastHandler: instanceToast
278
+ });
279
+ const resolveAuth = useCallback((params) => {
248
280
  const auth = getAuthContext();
249
281
  return {
250
282
  ...params,
251
283
  token: params.token ?? auth.token,
252
- organizationId: isPlatform ? params.organizationId ?? null : params.organizationId ?? auth.organizationId
284
+ organizationId: params.organizationId ?? auth.organizationId
253
285
  };
254
- };
255
- const create = async (params, options) => {
256
- silentRef.current = options?.silent ?? false;
257
- try {
258
- const result = await createMutation.mutateAsync(resolveAuth(params));
259
- options?.onSuccess?.(result);
260
- options?.onSettled?.(result, null);
261
- return result;
262
- } catch (error) {
263
- options?.onError?.(error);
264
- options?.onSettled?.(void 0, error);
265
- throw error;
266
- } finally {
267
- silentRef.current = false;
268
- }
269
- };
270
- const update = async (params, options) => {
271
- silentRef.current = options?.silent ?? false;
272
- try {
273
- const result = await updateMutation.mutateAsync(resolveAuth(params));
274
- options?.onSuccess?.(result);
275
- options?.onSettled?.(result, null);
276
- return result;
277
- } catch (error) {
278
- options?.onError?.(error);
279
- options?.onSettled?.(void 0, error);
280
- throw error;
281
- } finally {
282
- silentRef.current = false;
283
- }
284
- };
285
- const remove = async (params, options) => {
286
- silentRef.current = options?.silent ?? false;
287
- try {
288
- const result = await deleteMutation.mutateAsync(resolveAuth(params));
289
- options?.onSuccess?.(result);
290
- options?.onSettled?.(result, null);
291
- return result;
292
- } catch (error) {
293
- options?.onError?.(error);
294
- options?.onSettled?.(void 0, error);
295
- throw error;
296
- } finally {
297
- silentRef.current = false;
298
- }
299
- };
286
+ }, []);
300
287
  return {
301
- create,
302
- update,
303
- remove,
288
+ create: useCallback(async (params, options) => {
289
+ silentRef.current = options?.silent ?? false;
290
+ try {
291
+ const entity = extractItem(await createMutation.mutateAsync(resolveAuth(params)));
292
+ options?.onSuccess?.(entity);
293
+ options?.onSettled?.(entity, null);
294
+ return entity;
295
+ } catch (error) {
296
+ options?.onError?.(error);
297
+ options?.onSettled?.(void 0, error);
298
+ throw error;
299
+ } finally {
300
+ silentRef.current = false;
301
+ }
302
+ }, [createMutation, resolveAuth]),
303
+ update: useCallback(async (params, options) => {
304
+ silentRef.current = options?.silent ?? false;
305
+ try {
306
+ const entity = extractItem(await updateMutation.mutateAsync(resolveAuth(params)));
307
+ options?.onSuccess?.(entity);
308
+ options?.onSettled?.(entity, null);
309
+ return entity;
310
+ } catch (error) {
311
+ options?.onError?.(error);
312
+ options?.onSettled?.(void 0, error);
313
+ throw error;
314
+ } finally {
315
+ silentRef.current = false;
316
+ }
317
+ }, [updateMutation, resolveAuth]),
318
+ remove: useCallback(async (params, options) => {
319
+ silentRef.current = options?.silent ?? false;
320
+ try {
321
+ const result = await deleteMutation.mutateAsync(resolveAuth(params));
322
+ options?.onSuccess?.(result);
323
+ options?.onSettled?.(result, null);
324
+ return result;
325
+ } catch (error) {
326
+ options?.onError?.(error);
327
+ options?.onSettled?.(void 0, error);
328
+ throw error;
329
+ } finally {
330
+ silentRef.current = false;
331
+ }
332
+ }, [deleteMutation, resolveAuth]),
333
+ restore: useCallback(async (params, options) => {
334
+ silentRef.current = options?.silent ?? false;
335
+ try {
336
+ const entity = extractItem(await restoreMutation.mutateAsync(resolveAuth(params)));
337
+ options?.onSuccess?.(entity);
338
+ options?.onSettled?.(entity, null);
339
+ return entity;
340
+ } catch (error) {
341
+ options?.onError?.(error);
342
+ options?.onSettled?.(void 0, error);
343
+ throw error;
344
+ } finally {
345
+ silentRef.current = false;
346
+ }
347
+ }, [restoreMutation, resolveAuth]),
304
348
  isCreating: createMutation.isPending,
305
349
  isUpdating: updateMutation.isPending,
306
350
  isDeleting: deleteMutation.isPending,
307
- isMutating: createMutation.isPending || updateMutation.isPending || deleteMutation.isPending
351
+ isRestoring: restoreMutation.isPending,
352
+ isMutating: createMutation.isPending || updateMutation.isPending || deleteMutation.isPending || restoreMutation.isPending
308
353
  };
309
354
  }
310
355
  function useInfiniteList(tokenOrParams, paramsOrOptions, maybeOptions) {
@@ -320,7 +365,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
320
365
  token = auth.token;
321
366
  params = tokenOrParams ?? {};
322
367
  options = paramsOrOptions ?? {};
323
- if (!isPlatform && auth.organizationId && !params.organizationId) params = {
368
+ if (auth.organizationId && !params.organizationId) params = {
324
369
  ...params,
325
370
  organizationId: auth.organizationId
326
371
  };
@@ -328,7 +373,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
328
373
  const { organizationId, ...restParams } = params;
329
374
  const scope = options._scope || (organizationId ? "tenant" : "super-admin");
330
375
  const { request: requestOpts, ...queryOpts } = options;
331
- return createInfiniteListQuery({
376
+ return useInfiniteListQuery({
332
377
  queryKey: [...KEYS.scopedList(scope, {
333
378
  organizationId,
334
379
  ...restParams
@@ -348,7 +393,7 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
348
393
  }
349
394
  });
350
395
  },
351
- enabled: createEnabledRule(token, queryOpts),
396
+ enabled: createEnabledRule(token, queryOpts, resolveAuthMode()),
352
397
  initialPageParam: restParams.after ? restParams.after : 1,
353
398
  getNextPageParam: (lastPage) => {
354
399
  const page = lastPage;
@@ -357,23 +402,31 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
357
402
  const p = page;
358
403
  if (typeof p.hasNext === "boolean" && typeof p.page === "number") return p.hasNext ? p.page + 1 : void 0;
359
404
  },
405
+ getPreviousPageParam: queryOpts.maxPages != null ? (firstPage) => {
406
+ const page = firstPage;
407
+ if (isOffsetPagination(page)) return page.hasPrev ? page.page - 1 : void 0;
408
+ const p = page;
409
+ if (typeof p.hasPrev === "boolean" && typeof p.page === "number") return p.hasPrev ? p.page - 1 : void 0;
410
+ } : void 0,
411
+ maxPages: queryOpts.maxPages,
360
412
  options: {
361
413
  staleTime: queryOpts.staleTime ?? config.staleTime,
362
414
  gcTime: queryOpts.gcTime ?? config.gcTime,
363
415
  refetchOnWindowFocus: queryOpts.refetchOnWindowFocus ?? config.refetchOnWindowFocus,
364
- structuralSharing: queryOpts.structuralSharing ?? config.structuralSharing
416
+ structuralSharing: queryOpts.structuralSharing ?? config.structuralSharing,
417
+ refetchInterval: queryOpts.refetchInterval,
418
+ refetchIntervalInBackground: queryOpts.refetchIntervalInBackground
365
419
  }
366
420
  });
367
421
  }
368
422
  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
423
  return useMutationWithTransition({
372
424
  mutationFn: ({ data, id, path }) => {
425
+ if (!api.upload) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define an upload method`));
373
426
  const auth = getAuthContext();
374
- return uploadApi({
427
+ return api.upload({
375
428
  token: auth.token,
376
- organizationId: isPlatform ? null : auth.organizationId,
429
+ organizationId: auth.organizationId,
377
430
  data,
378
431
  id,
379
432
  path
@@ -391,12 +444,10 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
391
444
  });
392
445
  }
393
446
  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
447
  const auth = getAuthContext();
397
- const token = auth.token;
398
- const organizationId = platformScoped ? null : params?.organizationId ?? auth.organizationId;
399
- const { organizationId: _, ...restParams } = params ?? {};
448
+ const token = params?.token ?? auth.token;
449
+ const organizationId = params?.organizationId ?? auth.organizationId;
450
+ const { organizationId: _, token: _t, ...restParams } = params ?? {};
400
451
  const searchParams = {
401
452
  q: query,
402
453
  ...restParams
@@ -406,22 +457,25 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
406
457
  organizationId,
407
458
  ...searchParams
408
459
  } : searchParams;
409
- return createListQuery({
460
+ return useListQuery({
410
461
  queryKey: [
411
462
  ...KEYS.lists(),
412
463
  "search",
413
464
  searchKeyParams
414
465
  ],
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),
466
+ queryFn: ({ signal }) => {
467
+ if (!api.search) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a search method`));
468
+ return api.search({
469
+ token,
470
+ organizationId,
471
+ params: searchParams,
472
+ options: {
473
+ signal,
474
+ ...requestOpts
475
+ }
476
+ });
477
+ },
478
+ enabled: !!api.search && query.length > 0 && createEnabledRule(token, queryOpts, resolveAuthMode()),
425
479
  options: {
426
480
  staleTime: queryOpts.staleTime ?? config.staleTime,
427
481
  gcTime: queryOpts.gcTime ?? config.gcTime
@@ -440,11 +494,252 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
440
494
  toastHandler: instanceToast
441
495
  });
442
496
  }
497
+ function useDeleted(params, options) {
498
+ const auth = getAuthContext();
499
+ const token = auth.token;
500
+ const mergedParams = params ?? {};
501
+ const organizationId = mergedParams.organizationId ?? auth.organizationId;
502
+ const { organizationId: _, ...restParams } = mergedParams;
503
+ const { request: requestOpts, ...queryOpts } = options ?? {};
504
+ return useListQuery({
505
+ queryKey: KEYS.custom("deleted", {
506
+ organizationId,
507
+ ...restParams
508
+ }),
509
+ queryFn: ({ signal }) => {
510
+ if (!api.getDeleted) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getDeleted method`));
511
+ return api.getDeleted({
512
+ token,
513
+ organizationId,
514
+ params: restParams,
515
+ options: {
516
+ signal,
517
+ ...requestOpts
518
+ }
519
+ });
520
+ },
521
+ enabled: !!api.getDeleted && createEnabledRule(token, queryOpts, resolveAuthMode()),
522
+ options: {
523
+ staleTime: queryOpts.staleTime ?? config.staleTime,
524
+ gcTime: queryOpts.gcTime ?? config.gcTime
525
+ },
526
+ select: queryOpts.select
527
+ });
528
+ }
529
+ function useDetailBySlug(slug, options) {
530
+ const auth = getAuthContext();
531
+ const token = auth.token;
532
+ const resolvedOptions = options ?? {};
533
+ const organizationId = resolvedOptions.organizationId ?? auth.organizationId;
534
+ const { params: queryParams, request: requestOpts, ...restOptions } = resolvedOptions;
535
+ return useDetailQuery({
536
+ queryKey: queryParams ? KEYS.custom("slug", slug, queryParams) : KEYS.custom("slug", slug),
537
+ queryFn: ({ signal }) => {
538
+ if (!api.getBySlug) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getBySlug method`));
539
+ return api.getBySlug({
540
+ slug,
541
+ token,
542
+ organizationId,
543
+ params: queryParams,
544
+ options: {
545
+ signal,
546
+ ...requestOpts
547
+ }
548
+ });
549
+ },
550
+ enabled: !!api.getBySlug && !!slug && createEnabledRule(token, restOptions, resolveAuthMode()),
551
+ options: {
552
+ staleTime: restOptions.staleTime ?? config.staleTime,
553
+ gcTime: restOptions.gcTime ?? config.gcTime,
554
+ refetchOnWindowFocus: restOptions.refetchOnWindowFocus ?? config.refetchOnWindowFocus,
555
+ structuralSharing: restOptions.structuralSharing ?? config.structuralSharing
556
+ },
557
+ select: restOptions.select
558
+ });
559
+ }
560
+ function useTree(params, options) {
561
+ const auth = getAuthContext();
562
+ const token = auth.token;
563
+ const mergedParams = params ?? {};
564
+ const organizationId = mergedParams.organizationId ?? auth.organizationId;
565
+ const { organizationId: _, ...restParams } = mergedParams;
566
+ const { request: requestOpts, ...queryOpts } = options ?? {};
567
+ return useListQuery({
568
+ queryKey: KEYS.custom("tree", {
569
+ organizationId,
570
+ ...restParams
571
+ }),
572
+ queryFn: ({ signal }) => {
573
+ if (!api.getTree) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getTree method`));
574
+ return api.getTree({
575
+ token,
576
+ organizationId,
577
+ params: restParams,
578
+ options: {
579
+ signal,
580
+ ...requestOpts
581
+ }
582
+ });
583
+ },
584
+ enabled: !!api.getTree && createEnabledRule(token, queryOpts, resolveAuthMode()),
585
+ options: {
586
+ staleTime: queryOpts.staleTime ?? config.staleTime,
587
+ gcTime: queryOpts.gcTime ?? config.gcTime
588
+ },
589
+ select: queryOpts.select
590
+ });
591
+ }
592
+ function useChildren(parentId, params, options) {
593
+ const auth = getAuthContext();
594
+ const token = auth.token;
595
+ const mergedParams = params ?? {};
596
+ const organizationId = mergedParams.organizationId ?? auth.organizationId;
597
+ const { organizationId: _, ...restParams } = mergedParams;
598
+ const { request: requestOpts, ...queryOpts } = options ?? {};
599
+ return useListQuery({
600
+ queryKey: KEYS.custom("children", parentId, {
601
+ organizationId,
602
+ ...restParams
603
+ }),
604
+ queryFn: ({ signal }) => {
605
+ if (!api.getChildren) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a getChildren method`));
606
+ return api.getChildren({
607
+ token,
608
+ organizationId,
609
+ parentId,
610
+ params: restParams,
611
+ options: {
612
+ signal,
613
+ ...requestOpts
614
+ }
615
+ });
616
+ },
617
+ enabled: !!api.getChildren && !!parentId && createEnabledRule(token, queryOpts, resolveAuthMode()),
618
+ options: {
619
+ staleTime: queryOpts.staleTime ?? config.staleTime,
620
+ gcTime: queryOpts.gcTime ?? config.gcTime
621
+ },
622
+ prefillDetailCache: queryOpts.prefillDetailCache ?? true,
623
+ detailKeyBuilder: (id) => KEYS.detail(id),
624
+ itemIdResolver: resolveItemId,
625
+ select: queryOpts.select
626
+ });
627
+ }
628
+ function useFindBy(field, value, options) {
629
+ const auth = getAuthContext();
630
+ const token = auth.token;
631
+ const organizationId = auth.organizationId;
632
+ const { operator, request: requestOpts, ...queryOpts } = options ?? {};
633
+ return useListQuery({
634
+ queryKey: KEYS.custom("findBy", field, value, operator),
635
+ queryFn: ({ signal }) => {
636
+ if (!api.findBy) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a findBy method`));
637
+ return api.findBy({
638
+ token,
639
+ organizationId,
640
+ field,
641
+ value,
642
+ operator,
643
+ options: {
644
+ signal,
645
+ ...requestOpts
646
+ }
647
+ });
648
+ },
649
+ enabled: !!api.findBy && value !== void 0 && value !== null && createEnabledRule(token, queryOpts, resolveAuthMode()),
650
+ options: {
651
+ staleTime: queryOpts.staleTime ?? config.staleTime,
652
+ gcTime: queryOpts.gcTime ?? config.gcTime
653
+ },
654
+ prefillDetailCache: queryOpts.prefillDetailCache ?? true,
655
+ detailKeyBuilder: (id) => KEYS.detail(id),
656
+ itemIdResolver: resolveItemId,
657
+ select: queryOpts.select
658
+ });
659
+ }
660
+ function useBulkActions() {
661
+ const bulkCreateMutation = useMutationWithTransition({
662
+ mutationFn: (vars) => {
663
+ if (!api.bulkCreate) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkCreate method`));
664
+ const auth = getAuthContext();
665
+ return api.bulkCreate({
666
+ token: vars.token ?? auth.token,
667
+ organizationId: vars.organizationId ?? auth.organizationId,
668
+ data: vars.data
669
+ });
670
+ },
671
+ invalidateQueries: [KEYS.lists()],
672
+ messages: {
673
+ success: `${pluralName} created successfully`,
674
+ error: `Failed to create ${pluralName.toLowerCase()}`
675
+ },
676
+ toastHandler: instanceToast
677
+ });
678
+ const bulkUpdateMutation = useMutationWithTransition({
679
+ mutationFn: (vars) => {
680
+ if (!api.bulkUpdate) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkUpdate method`));
681
+ const auth = getAuthContext();
682
+ return api.bulkUpdate({
683
+ token: vars.token ?? auth.token,
684
+ organizationId: vars.organizationId ?? auth.organizationId,
685
+ filter: vars.filter,
686
+ data: vars.data
687
+ });
688
+ },
689
+ invalidateQueries: [KEYS.lists(), KEYS.details()],
690
+ messages: {
691
+ success: `${pluralName} updated successfully`,
692
+ error: `Failed to update ${pluralName.toLowerCase()}`
693
+ },
694
+ toastHandler: instanceToast
695
+ });
696
+ const bulkDeleteMutation = useMutationWithTransition({
697
+ mutationFn: (vars) => {
698
+ if (!api.bulkDelete) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkDelete method`));
699
+ const auth = getAuthContext();
700
+ return api.bulkDelete({
701
+ token: vars.token ?? auth.token,
702
+ organizationId: vars.organizationId ?? auth.organizationId,
703
+ filter: vars.filter
704
+ });
705
+ },
706
+ invalidateQueries: [KEYS.lists()],
707
+ messages: {
708
+ success: `${pluralName} deleted successfully`,
709
+ error: `Failed to delete ${pluralName.toLowerCase()}`
710
+ },
711
+ toastHandler: instanceToast
712
+ });
713
+ return {
714
+ bulkCreate: async (params, options) => {
715
+ const items = (await bulkCreateMutation.mutateAsync(params))?.data ?? [];
716
+ options?.onSuccess?.(items);
717
+ return items;
718
+ },
719
+ bulkUpdate: async (params, options) => {
720
+ const result = await bulkUpdateMutation.mutateAsync(params);
721
+ options?.onSuccess?.(result);
722
+ return result;
723
+ },
724
+ bulkRemove: async (params, options) => {
725
+ const result = await bulkDeleteMutation.mutateAsync(params);
726
+ options?.onSuccess?.(result);
727
+ return result;
728
+ },
729
+ isBulkCreating: bulkCreateMutation.isPending,
730
+ isBulkUpdating: bulkUpdateMutation.isPending,
731
+ isBulkDeleting: bulkDeleteMutation.isPending
732
+ };
733
+ }
734
+ const resolvedRouterHook = instanceNavigation ?? useRouterHook ?? (() => ({
735
+ push: () => {},
736
+ replace: () => {}
737
+ }));
443
738
  function useNavigation() {
444
739
  const queryClient = useQueryClient();
445
- const router = (instanceNavigation ?? useRouterHook)?.();
740
+ const router = resolvedRouterHook();
446
741
  return useCallback((href, item, options = {}) => {
447
- const id = getItemId(item);
742
+ const id = resolveItemId(item);
448
743
  if (id) queryClient.setQueryData(KEYS.detail(id), { data: item });
449
744
  if (!router) return;
450
745
  const { scroll = true, replace = false } = options;
@@ -459,6 +754,12 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
459
754
  useDetail,
460
755
  useInfiniteList,
461
756
  useActions,
757
+ useBulkActions,
758
+ useDeleted,
759
+ useDetailBySlug,
760
+ useTree,
761
+ useChildren,
762
+ useFindBy,
462
763
  useUpload,
463
764
  useSearch,
464
765
  useCustomMutation,
@@ -467,5 +768,4 @@ function createCrudHooks({ api, entityKey, singular, platformScoped, defaults =
467
768
  }
468
769
 
469
770
  //#endregion
470
- export { configureNavigation, createCrudHooks };
471
- //# sourceMappingURL=hooks.js.map
771
+ export { configureNavigation, createCrudHooks };