@classytic/arc-next 0.4.0 → 0.5.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/README.md +272 -616
- package/dist/api.d.ts +64 -127
- package/dist/api.js +55 -121
- package/dist/cache.d.ts +108 -0
- package/dist/cache.js +197 -0
- package/dist/client.d.ts +340 -8
- package/dist/client.js +427 -22
- package/dist/hooks.d.ts +96 -20
- package/dist/hooks.js +247 -115
- package/dist/mutation.d.ts +18 -4
- package/dist/mutation.js +20 -4
- package/dist/prefetch.d.ts +37 -3
- package/dist/prefetch.js +56 -17
- package/dist/presets/bulk.d.ts +42 -0
- package/dist/presets/bulk.js +50 -0
- package/dist/presets/search.d.ts +55 -0
- package/dist/presets/search.js +60 -0
- package/dist/presets/slug.d.ts +28 -0
- package/dist/presets/slug.js +27 -0
- package/dist/presets/soft-delete.d.ts +33 -0
- package/dist/presets/soft-delete.js +45 -0
- package/dist/presets/tree.d.ts +31 -0
- package/dist/presets/tree.js +47 -0
- package/dist/query.d.ts +86 -64
- package/dist/query.js +69 -151
- package/dist/sse.d.ts +116 -30
- package/dist/sse.js +179 -102
- package/dist/upload.d.ts +181 -0
- package/dist/upload.js +346 -0
- package/dist/ws.d.ts +167 -0
- package/dist/ws.js +274 -0
- package/package.json +37 -1
package/dist/hooks.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { getAuthMode, getClientAuthContext } from "./client.js";
|
|
4
4
|
import { isKeysetPagination, isOffsetPagination } from "./api.js";
|
|
5
|
-
import { DEFAULT_QUERY_CONFIG, createCacheUtils, createQueryKeys, extractItem, getItemId, updateListCache
|
|
5
|
+
import { DEFAULT_QUERY_CONFIG, createCacheUtils, createQueryKeys, extractItem, getItemId, updateListCache } from "./cache.js";
|
|
6
|
+
import { useDetailQuery, useInfiniteListQuery, useListQuery } from "./query.js";
|
|
6
7
|
import { useMutationWithTransition, useOptimisticMutation } from "./mutation.js";
|
|
8
|
+
import { subscribeToEvents } from "./sse.js";
|
|
9
|
+
import { connectWs } from "./ws.js";
|
|
7
10
|
import { useQueryClient } from "@tanstack/react-query";
|
|
8
|
-
import { useCallback, useRef } from "react";
|
|
11
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
12
|
|
|
10
13
|
//#region src/hooks.ts
|
|
11
14
|
let useRouterHook = null;
|
|
@@ -21,12 +24,17 @@ let useRouterHook = null;
|
|
|
21
24
|
function configureNavigation(hook) {
|
|
22
25
|
useRouterHook = hook;
|
|
23
26
|
}
|
|
24
|
-
function createEnabledRule(token, options, authMode = getAuthMode()) {
|
|
27
|
+
function createEnabledRule(token, options, authMode = getAuthMode(), hasStaticAuth = false) {
|
|
25
28
|
if (authMode === "cookie" || options.public) return options.enabled ?? true;
|
|
29
|
+
if (hasStaticAuth) return options.enabled ?? true;
|
|
26
30
|
return options.enabled !== void 0 ? options.enabled && !!token : !!token;
|
|
27
31
|
}
|
|
28
32
|
function createCrudHooks({ api, entityKey, singular, plural, idField, defaults = {}, callbacks = {}, client }) {
|
|
29
33
|
const pluralName = plural ?? `${singular}s`;
|
|
34
|
+
/** Resolve auth context — per-client auth takes priority over global */
|
|
35
|
+
const resolveAuth = () => getClientAuthContext(client);
|
|
36
|
+
/** Whether auth is provided via static config (headers, internalApiKey, per-client auth) — no token needed for enablement */
|
|
37
|
+
const hasStaticAuth = !!(client?.config?.defaultHeaders || client?.config?.internalApiKey || client?.auth);
|
|
30
38
|
/** Extract ID from an item using configured idField, falling back to _id → id */
|
|
31
39
|
function resolveItemId(item) {
|
|
32
40
|
if (!item || typeof item !== "object") return null;
|
|
@@ -60,12 +68,12 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
60
68
|
let token;
|
|
61
69
|
let params;
|
|
62
70
|
let options;
|
|
63
|
-
if (tokenOrParams ===
|
|
71
|
+
if (typeof tokenOrParams === "string" || tokenOrParams === null && maybeOptions !== void 0) {
|
|
64
72
|
token = tokenOrParams;
|
|
65
73
|
params = paramsOrOptions ?? {};
|
|
66
74
|
options = maybeOptions ?? {};
|
|
67
75
|
} else {
|
|
68
|
-
const auth =
|
|
76
|
+
const auth = resolveAuth();
|
|
69
77
|
token = auth.token;
|
|
70
78
|
params = tokenOrParams ?? {};
|
|
71
79
|
options = paramsOrOptions ?? {};
|
|
@@ -91,7 +99,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
91
99
|
...requestOpts
|
|
92
100
|
}
|
|
93
101
|
}),
|
|
94
|
-
enabled: createEnabledRule(token, queryOpts, resolveAuthMode()),
|
|
102
|
+
enabled: createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
|
|
95
103
|
options: {
|
|
96
104
|
staleTime: queryOpts.staleTime ?? config.staleTime,
|
|
97
105
|
gcTime: queryOpts.gcTime ?? config.gcTime,
|
|
@@ -101,7 +109,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
101
109
|
refetchIntervalInBackground: queryOpts.refetchIntervalInBackground
|
|
102
110
|
},
|
|
103
111
|
prefillDetailCache: queryOpts.prefillDetailCache ?? true,
|
|
104
|
-
detailKeyBuilder: (id) => KEYS.
|
|
112
|
+
detailKeyBuilder: (id) => KEYS.scopedDetail(id, organizationId ?? null),
|
|
105
113
|
itemIdResolver: resolveItemId,
|
|
106
114
|
select: queryOpts.select
|
|
107
115
|
});
|
|
@@ -109,11 +117,11 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
109
117
|
function useDetail(id, tokenOrOptions, maybeOptions) {
|
|
110
118
|
let token;
|
|
111
119
|
let options;
|
|
112
|
-
if (tokenOrOptions ===
|
|
120
|
+
if (typeof tokenOrOptions === "string" || tokenOrOptions === null && maybeOptions !== void 0) {
|
|
113
121
|
token = tokenOrOptions;
|
|
114
122
|
options = maybeOptions ?? {};
|
|
115
123
|
} else {
|
|
116
|
-
const auth =
|
|
124
|
+
const auth = resolveAuth();
|
|
117
125
|
token = auth.token;
|
|
118
126
|
options = tokenOrOptions ?? {};
|
|
119
127
|
if (auth.organizationId && !options.organizationId) options = {
|
|
@@ -122,8 +130,9 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
122
130
|
};
|
|
123
131
|
}
|
|
124
132
|
const { organizationId, params: queryParams, request: requestOpts, ...restOptions } = options;
|
|
133
|
+
const detailKey = KEYS.scopedDetail(id || "", organizationId ?? null);
|
|
125
134
|
return useDetailQuery({
|
|
126
|
-
queryKey: queryParams ? [...
|
|
135
|
+
queryKey: queryParams ? [...detailKey, queryParams] : detailKey,
|
|
127
136
|
queryFn: ({ signal }) => api.getById({
|
|
128
137
|
id,
|
|
129
138
|
token,
|
|
@@ -134,7 +143,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
134
143
|
...requestOpts
|
|
135
144
|
}
|
|
136
145
|
}),
|
|
137
|
-
enabled: !!id && createEnabledRule(token, restOptions, resolveAuthMode()),
|
|
146
|
+
enabled: !!id && createEnabledRule(token, restOptions, resolveAuthMode(), hasStaticAuth),
|
|
138
147
|
options: {
|
|
139
148
|
staleTime: restOptions.staleTime ?? config.staleTime,
|
|
140
149
|
gcTime: restOptions.gcTime ?? config.gcTime,
|
|
@@ -197,13 +206,16 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
197
206
|
...item,
|
|
198
207
|
...data
|
|
199
208
|
} : item));
|
|
200
|
-
|
|
209
|
+
const detailUpdater = (current) => current ? {
|
|
201
210
|
...current,
|
|
202
211
|
data: {
|
|
203
212
|
...current.data || {},
|
|
204
213
|
...data
|
|
205
214
|
}
|
|
206
|
-
} : current
|
|
215
|
+
} : current;
|
|
216
|
+
queryClient.getQueriesData({ queryKey: KEYS.detail(id) }).forEach(([qKey, qData]) => {
|
|
217
|
+
if (qData) queryClient.setQueryData(qKey, detailUpdater);
|
|
218
|
+
});
|
|
207
219
|
return updated;
|
|
208
220
|
},
|
|
209
221
|
onSuccess: (raw, { id, data: updateData }) => {
|
|
@@ -276,8 +288,8 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
276
288
|
},
|
|
277
289
|
toastHandler: instanceToast
|
|
278
290
|
});
|
|
279
|
-
const
|
|
280
|
-
const auth =
|
|
291
|
+
const resolveActionAuth = useCallback((params) => {
|
|
292
|
+
const auth = resolveAuth();
|
|
281
293
|
return {
|
|
282
294
|
...params,
|
|
283
295
|
token: params.token ?? auth.token,
|
|
@@ -288,7 +300,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
288
300
|
create: useCallback(async (params, options) => {
|
|
289
301
|
silentRef.current = options?.silent ?? false;
|
|
290
302
|
try {
|
|
291
|
-
const entity = extractItem(await createMutation.mutateAsync(
|
|
303
|
+
const entity = extractItem(await createMutation.mutateAsync(resolveActionAuth(params)));
|
|
292
304
|
options?.onSuccess?.(entity);
|
|
293
305
|
options?.onSettled?.(entity, null);
|
|
294
306
|
return entity;
|
|
@@ -299,11 +311,11 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
299
311
|
} finally {
|
|
300
312
|
silentRef.current = false;
|
|
301
313
|
}
|
|
302
|
-
}, [createMutation,
|
|
314
|
+
}, [createMutation, resolveActionAuth]),
|
|
303
315
|
update: useCallback(async (params, options) => {
|
|
304
316
|
silentRef.current = options?.silent ?? false;
|
|
305
317
|
try {
|
|
306
|
-
const entity = extractItem(await updateMutation.mutateAsync(
|
|
318
|
+
const entity = extractItem(await updateMutation.mutateAsync(resolveActionAuth(params)));
|
|
307
319
|
options?.onSuccess?.(entity);
|
|
308
320
|
options?.onSettled?.(entity, null);
|
|
309
321
|
return entity;
|
|
@@ -314,11 +326,11 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
314
326
|
} finally {
|
|
315
327
|
silentRef.current = false;
|
|
316
328
|
}
|
|
317
|
-
}, [updateMutation,
|
|
329
|
+
}, [updateMutation, resolveActionAuth]),
|
|
318
330
|
remove: useCallback(async (params, options) => {
|
|
319
331
|
silentRef.current = options?.silent ?? false;
|
|
320
332
|
try {
|
|
321
|
-
const result = await deleteMutation.mutateAsync(
|
|
333
|
+
const result = await deleteMutation.mutateAsync(resolveActionAuth(params));
|
|
322
334
|
options?.onSuccess?.(result);
|
|
323
335
|
options?.onSettled?.(result, null);
|
|
324
336
|
return result;
|
|
@@ -329,11 +341,11 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
329
341
|
} finally {
|
|
330
342
|
silentRef.current = false;
|
|
331
343
|
}
|
|
332
|
-
}, [deleteMutation,
|
|
344
|
+
}, [deleteMutation, resolveActionAuth]),
|
|
333
345
|
restore: useCallback(async (params, options) => {
|
|
334
346
|
silentRef.current = options?.silent ?? false;
|
|
335
347
|
try {
|
|
336
|
-
const entity = extractItem(await restoreMutation.mutateAsync(
|
|
348
|
+
const entity = extractItem(await restoreMutation.mutateAsync(resolveActionAuth(params)));
|
|
337
349
|
options?.onSuccess?.(entity);
|
|
338
350
|
options?.onSettled?.(entity, null);
|
|
339
351
|
return entity;
|
|
@@ -344,7 +356,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
344
356
|
} finally {
|
|
345
357
|
silentRef.current = false;
|
|
346
358
|
}
|
|
347
|
-
}, [restoreMutation,
|
|
359
|
+
}, [restoreMutation, resolveActionAuth]),
|
|
348
360
|
isCreating: createMutation.isPending,
|
|
349
361
|
isUpdating: updateMutation.isPending,
|
|
350
362
|
isDeleting: deleteMutation.isPending,
|
|
@@ -356,12 +368,12 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
356
368
|
let token;
|
|
357
369
|
let params;
|
|
358
370
|
let options;
|
|
359
|
-
if (tokenOrParams ===
|
|
371
|
+
if (typeof tokenOrParams === "string" || tokenOrParams === null && maybeOptions !== void 0) {
|
|
360
372
|
token = tokenOrParams;
|
|
361
373
|
params = paramsOrOptions ?? {};
|
|
362
374
|
options = maybeOptions ?? {};
|
|
363
375
|
} else {
|
|
364
|
-
const auth =
|
|
376
|
+
const auth = resolveAuth();
|
|
365
377
|
token = auth.token;
|
|
366
378
|
params = tokenOrParams ?? {};
|
|
367
379
|
options = paramsOrOptions ?? {};
|
|
@@ -393,7 +405,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
393
405
|
}
|
|
394
406
|
});
|
|
395
407
|
},
|
|
396
|
-
enabled: createEnabledRule(token, queryOpts, resolveAuthMode()),
|
|
408
|
+
enabled: createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
|
|
397
409
|
initialPageParam: restParams.after ? restParams.after : 1,
|
|
398
410
|
getNextPageParam: (lastPage) => {
|
|
399
411
|
const page = lastPage;
|
|
@@ -423,7 +435,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
423
435
|
return useMutationWithTransition({
|
|
424
436
|
mutationFn: ({ data, id, path }) => {
|
|
425
437
|
if (!api.upload) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define an upload method`));
|
|
426
|
-
const auth =
|
|
438
|
+
const auth = resolveAuth();
|
|
427
439
|
return api.upload({
|
|
428
440
|
token: auth.token,
|
|
429
441
|
organizationId: auth.organizationId,
|
|
@@ -443,46 +455,6 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
443
455
|
toastHandler: instanceToast
|
|
444
456
|
});
|
|
445
457
|
}
|
|
446
|
-
function useSearch(query, params, options) {
|
|
447
|
-
const auth = getAuthContext();
|
|
448
|
-
const token = params?.token ?? auth.token;
|
|
449
|
-
const organizationId = params?.organizationId ?? auth.organizationId;
|
|
450
|
-
const { organizationId: _, token: _t, ...restParams } = params ?? {};
|
|
451
|
-
const searchParams = {
|
|
452
|
-
q: query,
|
|
453
|
-
...restParams
|
|
454
|
-
};
|
|
455
|
-
const { request: requestOpts, ...queryOpts } = options ?? {};
|
|
456
|
-
const searchKeyParams = organizationId ? {
|
|
457
|
-
organizationId,
|
|
458
|
-
...searchParams
|
|
459
|
-
} : searchParams;
|
|
460
|
-
return useListQuery({
|
|
461
|
-
queryKey: [
|
|
462
|
-
...KEYS.lists(),
|
|
463
|
-
"search",
|
|
464
|
-
searchKeyParams
|
|
465
|
-
],
|
|
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()),
|
|
479
|
-
options: {
|
|
480
|
-
staleTime: queryOpts.staleTime ?? config.staleTime,
|
|
481
|
-
gcTime: queryOpts.gcTime ?? config.gcTime
|
|
482
|
-
},
|
|
483
|
-
select: queryOpts.select
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
458
|
function useCustomMutation(mutationConfig) {
|
|
487
459
|
return useMutationWithTransition({
|
|
488
460
|
mutationFn: mutationConfig.mutationFn,
|
|
@@ -495,7 +467,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
495
467
|
});
|
|
496
468
|
}
|
|
497
469
|
function useDeleted(params, options) {
|
|
498
|
-
const auth =
|
|
470
|
+
const auth = resolveAuth();
|
|
499
471
|
const token = auth.token;
|
|
500
472
|
const mergedParams = params ?? {};
|
|
501
473
|
const organizationId = mergedParams.organizationId ?? auth.organizationId;
|
|
@@ -518,7 +490,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
518
490
|
}
|
|
519
491
|
});
|
|
520
492
|
},
|
|
521
|
-
enabled: !!api.getDeleted && createEnabledRule(token, queryOpts, resolveAuthMode()),
|
|
493
|
+
enabled: !!api.getDeleted && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
|
|
522
494
|
options: {
|
|
523
495
|
staleTime: queryOpts.staleTime ?? config.staleTime,
|
|
524
496
|
gcTime: queryOpts.gcTime ?? config.gcTime
|
|
@@ -527,7 +499,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
527
499
|
});
|
|
528
500
|
}
|
|
529
501
|
function useDetailBySlug(slug, options) {
|
|
530
|
-
const auth =
|
|
502
|
+
const auth = resolveAuth();
|
|
531
503
|
const token = auth.token;
|
|
532
504
|
const resolvedOptions = options ?? {};
|
|
533
505
|
const organizationId = resolvedOptions.organizationId ?? auth.organizationId;
|
|
@@ -547,7 +519,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
547
519
|
}
|
|
548
520
|
});
|
|
549
521
|
},
|
|
550
|
-
enabled: !!api.getBySlug && !!slug && createEnabledRule(token, restOptions, resolveAuthMode()),
|
|
522
|
+
enabled: !!api.getBySlug && !!slug && createEnabledRule(token, restOptions, resolveAuthMode(), hasStaticAuth),
|
|
551
523
|
options: {
|
|
552
524
|
staleTime: restOptions.staleTime ?? config.staleTime,
|
|
553
525
|
gcTime: restOptions.gcTime ?? config.gcTime,
|
|
@@ -558,7 +530,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
558
530
|
});
|
|
559
531
|
}
|
|
560
532
|
function useTree(params, options) {
|
|
561
|
-
const auth =
|
|
533
|
+
const auth = resolveAuth();
|
|
562
534
|
const token = auth.token;
|
|
563
535
|
const mergedParams = params ?? {};
|
|
564
536
|
const organizationId = mergedParams.organizationId ?? auth.organizationId;
|
|
@@ -581,7 +553,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
581
553
|
}
|
|
582
554
|
});
|
|
583
555
|
},
|
|
584
|
-
enabled: !!api.getTree && createEnabledRule(token, queryOpts, resolveAuthMode()),
|
|
556
|
+
enabled: !!api.getTree && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
|
|
585
557
|
options: {
|
|
586
558
|
staleTime: queryOpts.staleTime ?? config.staleTime,
|
|
587
559
|
gcTime: queryOpts.gcTime ?? config.gcTime
|
|
@@ -590,7 +562,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
590
562
|
});
|
|
591
563
|
}
|
|
592
564
|
function useChildren(parentId, params, options) {
|
|
593
|
-
const auth =
|
|
565
|
+
const auth = resolveAuth();
|
|
594
566
|
const token = auth.token;
|
|
595
567
|
const mergedParams = params ?? {};
|
|
596
568
|
const organizationId = mergedParams.organizationId ?? auth.organizationId;
|
|
@@ -614,45 +586,13 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
614
586
|
}
|
|
615
587
|
});
|
|
616
588
|
},
|
|
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()),
|
|
589
|
+
enabled: !!api.getChildren && !!parentId && createEnabledRule(token, queryOpts, resolveAuthMode(), hasStaticAuth),
|
|
650
590
|
options: {
|
|
651
591
|
staleTime: queryOpts.staleTime ?? config.staleTime,
|
|
652
592
|
gcTime: queryOpts.gcTime ?? config.gcTime
|
|
653
593
|
},
|
|
654
594
|
prefillDetailCache: queryOpts.prefillDetailCache ?? true,
|
|
655
|
-
detailKeyBuilder: (id) => KEYS.
|
|
595
|
+
detailKeyBuilder: (id) => KEYS.scopedDetail(id, organizationId ?? null),
|
|
656
596
|
itemIdResolver: resolveItemId,
|
|
657
597
|
select: queryOpts.select
|
|
658
598
|
});
|
|
@@ -661,7 +601,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
661
601
|
const bulkCreateMutation = useMutationWithTransition({
|
|
662
602
|
mutationFn: (vars) => {
|
|
663
603
|
if (!api.bulkCreate) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkCreate method`));
|
|
664
|
-
const auth =
|
|
604
|
+
const auth = resolveAuth();
|
|
665
605
|
return api.bulkCreate({
|
|
666
606
|
token: vars.token ?? auth.token,
|
|
667
607
|
organizationId: vars.organizationId ?? auth.organizationId,
|
|
@@ -678,7 +618,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
678
618
|
const bulkUpdateMutation = useMutationWithTransition({
|
|
679
619
|
mutationFn: (vars) => {
|
|
680
620
|
if (!api.bulkUpdate) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkUpdate method`));
|
|
681
|
-
const auth =
|
|
621
|
+
const auth = resolveAuth();
|
|
682
622
|
return api.bulkUpdate({
|
|
683
623
|
token: vars.token ?? auth.token,
|
|
684
624
|
organizationId: vars.organizationId ?? auth.organizationId,
|
|
@@ -696,7 +636,7 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
696
636
|
const bulkDeleteMutation = useMutationWithTransition({
|
|
697
637
|
mutationFn: (vars) => {
|
|
698
638
|
if (!api.bulkDelete) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a bulkDelete method`));
|
|
699
|
-
const auth =
|
|
639
|
+
const auth = resolveAuth();
|
|
700
640
|
return api.bulkDelete({
|
|
701
641
|
token: vars.token ?? auth.token,
|
|
702
642
|
organizationId: vars.organizationId ?? auth.organizationId,
|
|
@@ -731,6 +671,191 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
731
671
|
isBulkDeleting: bulkDeleteMutation.isPending
|
|
732
672
|
};
|
|
733
673
|
}
|
|
674
|
+
function useAction(options) {
|
|
675
|
+
const queryClient = useQueryClient();
|
|
676
|
+
return useMutationWithTransition({
|
|
677
|
+
mutationFn: (vars) => {
|
|
678
|
+
if (!api.dispatchAction) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a dispatchAction method`));
|
|
679
|
+
const action = vars.action ?? options?.action;
|
|
680
|
+
if (!action) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] useAction: action name required (pass via mutate({ action }) or factory options)`));
|
|
681
|
+
const auth = resolveAuth();
|
|
682
|
+
return api.dispatchAction({
|
|
683
|
+
token: auth.token,
|
|
684
|
+
organizationId: auth.organizationId,
|
|
685
|
+
id: vars.id,
|
|
686
|
+
action,
|
|
687
|
+
data: vars.data
|
|
688
|
+
});
|
|
689
|
+
},
|
|
690
|
+
invalidateQueries: options?.invalidateQueries ?? [KEYS.lists(), KEYS.details()],
|
|
691
|
+
onSuccess: (data, vars) => {
|
|
692
|
+
const action = vars.action ?? options?.action ?? "";
|
|
693
|
+
if (vars.id) queryClient.invalidateQueries({ queryKey: KEYS.detail(vars.id) });
|
|
694
|
+
options?.onSuccess?.(data, {
|
|
695
|
+
id: vars.id,
|
|
696
|
+
action,
|
|
697
|
+
data: vars.data
|
|
698
|
+
});
|
|
699
|
+
},
|
|
700
|
+
onError: (error, vars) => {
|
|
701
|
+
const action = vars.action ?? options?.action ?? "";
|
|
702
|
+
options?.onError?.(error, {
|
|
703
|
+
id: vars.id,
|
|
704
|
+
action,
|
|
705
|
+
data: vars.data
|
|
706
|
+
});
|
|
707
|
+
},
|
|
708
|
+
onSettled: (data, error, vars) => {
|
|
709
|
+
const action = vars.action ?? options?.action ?? "";
|
|
710
|
+
options?.onSettled?.(data, error, {
|
|
711
|
+
id: vars.id,
|
|
712
|
+
action,
|
|
713
|
+
data: vars.data
|
|
714
|
+
});
|
|
715
|
+
},
|
|
716
|
+
messages: options?.messages,
|
|
717
|
+
toastHandler: instanceToast
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
function useSearchEngine(options) {
|
|
721
|
+
return useMutationWithTransition({
|
|
722
|
+
mutationFn: (vars) => {
|
|
723
|
+
if (!api.searchEngine) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a searchEngine method`));
|
|
724
|
+
const auth = resolveAuth();
|
|
725
|
+
return api.searchEngine({
|
|
726
|
+
token: auth.token,
|
|
727
|
+
organizationId: auth.organizationId,
|
|
728
|
+
query: vars.query,
|
|
729
|
+
body: vars.body,
|
|
730
|
+
path: options?.path
|
|
731
|
+
});
|
|
732
|
+
},
|
|
733
|
+
invalidateQueries: options?.invalidateQueries ?? [],
|
|
734
|
+
messages: options?.messages,
|
|
735
|
+
toastHandler: instanceToast
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function useSearchSimilar(options) {
|
|
739
|
+
return useMutationWithTransition({
|
|
740
|
+
mutationFn: (vars) => {
|
|
741
|
+
if (!api.searchSimilar) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define a searchSimilar method`));
|
|
742
|
+
const auth = resolveAuth();
|
|
743
|
+
return api.searchSimilar({
|
|
744
|
+
token: auth.token,
|
|
745
|
+
organizationId: auth.organizationId,
|
|
746
|
+
query: vars.query,
|
|
747
|
+
vector: vars.vector,
|
|
748
|
+
body: vars.body,
|
|
749
|
+
path: options?.path
|
|
750
|
+
});
|
|
751
|
+
},
|
|
752
|
+
invalidateQueries: options?.invalidateQueries ?? [],
|
|
753
|
+
messages: options?.messages,
|
|
754
|
+
toastHandler: instanceToast
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
function useEmbed(options) {
|
|
758
|
+
return useMutationWithTransition({
|
|
759
|
+
mutationFn: (vars) => {
|
|
760
|
+
if (!api.embed) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define an embed method`));
|
|
761
|
+
const auth = resolveAuth();
|
|
762
|
+
return api.embed({
|
|
763
|
+
token: auth.token,
|
|
764
|
+
organizationId: auth.organizationId,
|
|
765
|
+
input: vars.input,
|
|
766
|
+
body: vars.body,
|
|
767
|
+
path: options?.path
|
|
768
|
+
});
|
|
769
|
+
},
|
|
770
|
+
invalidateQueries: options?.invalidateQueries ?? [],
|
|
771
|
+
messages: options?.messages,
|
|
772
|
+
toastHandler: instanceToast
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Subscribe to live `<resource>.<operation>` broadcasts from arc and
|
|
777
|
+
* auto-invalidate this entity's TanStack Query cache.
|
|
778
|
+
*
|
|
779
|
+
* - `source: 'ws'` (default) → arc's `websocketPlugin` at `/ws`. Sends a
|
|
780
|
+
* `{ type: 'subscribe', resource }` handshake on connect.
|
|
781
|
+
* - `source: 'sse'` → arc's `ssePlugin` at `/events/stream`. Auto-derives
|
|
782
|
+
* patterns from the resource name.
|
|
783
|
+
*
|
|
784
|
+
* Both transports invalidate `KEYS.lists()` on `<resource>.created` and
|
|
785
|
+
* `<resource>.deleted`, and `KEYS.detail(id)` (prefix-matches scoped /
|
|
786
|
+
* parameterized variants) on `<resource>.updated` / `.deleted`.
|
|
787
|
+
*/
|
|
788
|
+
function useResourceSync(options) {
|
|
789
|
+
const queryClient = useQueryClient();
|
|
790
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
791
|
+
const source = options?.source ?? "ws";
|
|
792
|
+
const resource = options?.resource ?? entityKey;
|
|
793
|
+
const enabled = options?.enabled ?? true;
|
|
794
|
+
const path = options?.path;
|
|
795
|
+
const onEventRef = useRef(options?.onEvent);
|
|
796
|
+
onEventRef.current = options?.onEvent;
|
|
797
|
+
const onConnRef = useRef(options?.onConnectionChange);
|
|
798
|
+
onConnRef.current = options?.onConnectionChange;
|
|
799
|
+
useEffect(() => {
|
|
800
|
+
if (!enabled) return;
|
|
801
|
+
const handleBroadcast = (incomingType, payload) => {
|
|
802
|
+
const dot = incomingType.lastIndexOf(".");
|
|
803
|
+
const operation = dot >= 0 ? incomingType.slice(dot + 1) : incomingType;
|
|
804
|
+
if (operation !== "created" && operation !== "updated" && operation !== "deleted") return;
|
|
805
|
+
let doc = payload;
|
|
806
|
+
if (payload && typeof payload === "object" && !Array.isArray(payload) && "data" in payload) {
|
|
807
|
+
const inner = payload.data;
|
|
808
|
+
if (inner !== void 0) doc = inner;
|
|
809
|
+
}
|
|
810
|
+
const id = typeof doc === "object" && doc !== null ? (() => {
|
|
811
|
+
const o = doc;
|
|
812
|
+
const raw = idField ? o[idField] : o._id ?? o.id;
|
|
813
|
+
return raw != null ? String(raw) : void 0;
|
|
814
|
+
})() : void 0;
|
|
815
|
+
queryClient.invalidateQueries({ queryKey: KEYS.lists() });
|
|
816
|
+
if (id && (operation === "updated" || operation === "deleted")) queryClient.invalidateQueries({ queryKey: KEYS.detail(id) });
|
|
817
|
+
onEventRef.current?.({
|
|
818
|
+
operation,
|
|
819
|
+
id,
|
|
820
|
+
data: doc
|
|
821
|
+
});
|
|
822
|
+
};
|
|
823
|
+
if (source === "sse") {
|
|
824
|
+
const handle = subscribeToEvents({
|
|
825
|
+
resource,
|
|
826
|
+
path,
|
|
827
|
+
onConnectionChange: (c) => {
|
|
828
|
+
setIsConnected(c);
|
|
829
|
+
onConnRef.current?.(c);
|
|
830
|
+
},
|
|
831
|
+
onEvent: (event) => {
|
|
832
|
+
handleBroadcast(event.type, event.data);
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
return () => handle.close();
|
|
836
|
+
}
|
|
837
|
+
const handle = connectWs({
|
|
838
|
+
path,
|
|
839
|
+
subscribe: [resource],
|
|
840
|
+
patterns: [`${resource}.`],
|
|
841
|
+
onConnectionChange: (c) => {
|
|
842
|
+
setIsConnected(c);
|
|
843
|
+
onConnRef.current?.(c);
|
|
844
|
+
},
|
|
845
|
+
onMessage: (message) => {
|
|
846
|
+
handleBroadcast(message.type, message.data);
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
return () => handle.close();
|
|
850
|
+
}, [
|
|
851
|
+
enabled,
|
|
852
|
+
source,
|
|
853
|
+
resource,
|
|
854
|
+
path,
|
|
855
|
+
queryClient
|
|
856
|
+
]);
|
|
857
|
+
return { isConnected };
|
|
858
|
+
}
|
|
734
859
|
const resolvedRouterHook = instanceNavigation ?? useRouterHook ?? (() => ({
|
|
735
860
|
push: () => {},
|
|
736
861
|
replace: () => {}
|
|
@@ -740,7 +865,11 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
740
865
|
const router = resolvedRouterHook();
|
|
741
866
|
return useCallback((href, item, options = {}) => {
|
|
742
867
|
const id = resolveItemId(item);
|
|
743
|
-
if (id)
|
|
868
|
+
if (id) {
|
|
869
|
+
const orgId = resolveAuth().organizationId;
|
|
870
|
+
queryClient.setQueryData(KEYS.scopedDetail(id, orgId), { data: item });
|
|
871
|
+
if (orgId) queryClient.setQueryData(KEYS.detail(id), { data: item });
|
|
872
|
+
}
|
|
744
873
|
if (!router) return;
|
|
745
874
|
const { scroll = true, replace = false } = options;
|
|
746
875
|
if (replace) router.replace(href, { scroll });
|
|
@@ -759,10 +888,13 @@ function createCrudHooks({ api, entityKey, singular, plural, idField, defaults =
|
|
|
759
888
|
useDetailBySlug,
|
|
760
889
|
useTree,
|
|
761
890
|
useChildren,
|
|
762
|
-
useFindBy,
|
|
763
891
|
useUpload,
|
|
764
|
-
useSearch,
|
|
765
892
|
useCustomMutation,
|
|
893
|
+
useAction,
|
|
894
|
+
useSearchEngine,
|
|
895
|
+
useSearchSimilar,
|
|
896
|
+
useEmbed,
|
|
897
|
+
useResourceSync,
|
|
766
898
|
useNavigation
|
|
767
899
|
};
|
|
768
900
|
}
|
package/dist/mutation.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ToastHandler } from "./client.js";
|
|
2
|
-
import { QUERY_CONFIGS } from "./query.js";
|
|
3
2
|
import * as _$_tanstack_react_query0 from "@tanstack/react-query";
|
|
4
3
|
import { QueryClient, QueryKey, UseMutateAsyncFunction, UseMutateFunction } from "@tanstack/react-query";
|
|
5
4
|
|
|
@@ -34,6 +33,23 @@ interface TransitionMutationReturn<TData, TVariables> {
|
|
|
34
33
|
* configureToast({ success: toast.success, error: toast.error });
|
|
35
34
|
*/
|
|
36
35
|
declare function configureToast(handler: ToastHandler): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get the configured toast handler. Returns the console-based default when
|
|
38
|
+
* no handler has been configured yet.
|
|
39
|
+
*
|
|
40
|
+
* Use this when domain code outside the react-query lifecycle needs to fire
|
|
41
|
+
* ad-hoc success/error toasts using the same handler the SDK uses internally
|
|
42
|
+
* — avoids the need for consumer SDKs to keep a parallel cache of the handler.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // In a domain helper outside any mutation lifecycle:
|
|
46
|
+
* import { getToastHandler } from '@classytic/arc-next/mutation';
|
|
47
|
+
*
|
|
48
|
+
* function notifySaved(label: string) {
|
|
49
|
+
* getToastHandler().success(`${label} saved`);
|
|
50
|
+
* }
|
|
51
|
+
*/
|
|
52
|
+
declare function getToastHandler(): ToastHandler;
|
|
37
53
|
interface TransitionMutationConfig<TData, TVariables> {
|
|
38
54
|
mutationFn: (variables: TVariables) => Promise<TData>;
|
|
39
55
|
invalidateQueries?: QueryKey[];
|
|
@@ -107,7 +123,5 @@ declare function useOptimisticMutation<TData, TVariables>(config: CreateOptimist
|
|
|
107
123
|
data: [readonly unknown[], unknown][];
|
|
108
124
|
}[];
|
|
109
125
|
}>;
|
|
110
|
-
/** @deprecated Use `useOptimisticMutation` */
|
|
111
|
-
declare const createOptimisticMutation: typeof useOptimisticMutation;
|
|
112
126
|
//#endregion
|
|
113
|
-
export { CreateOptimisticMutationConfig, MutationCallbacks, MutationMessages, OptimisticMutationConfig,
|
|
127
|
+
export { CreateOptimisticMutationConfig, MutationCallbacks, MutationMessages, OptimisticMutationConfig, TransitionMutationConfig, TransitionMutationReturn, configureToast, getToastHandler, useMutationWithOptimistic, useMutationWithTransition, useOptimisticMutation };
|