@nby.ai/ucm 1.0.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/index.cjs ADDED
@@ -0,0 +1,797 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ UCMProvider: () => UCMProvider,
24
+ buildCategoryTree: () => buildCategoryTree,
25
+ createNullClient: () => createNullClient,
26
+ createUCMClient: () => createUCMClient,
27
+ getLocalizedText: () => getLocalizedText,
28
+ useAdjacentContents: () => useAdjacentContents,
29
+ useCategories: () => useCategories,
30
+ useCategory: () => useCategory,
31
+ useCategoryBreadcrumb: () => useCategoryBreadcrumb,
32
+ useContent: () => useContent,
33
+ useContents: () => useContents,
34
+ useSearchContents: () => useSearchContents,
35
+ useTags: () => useTags,
36
+ useThoughts: () => useThoughts,
37
+ useUCM: () => useUCM,
38
+ useUCMConfigured: () => useUCMConfigured,
39
+ useUCMOptional: () => useUCMOptional
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+
43
+ // src/core.ts
44
+ var import_supabase_js = require("@supabase/supabase-js");
45
+
46
+ // src/types.ts
47
+ function getLocalizedText(text, locale, fallback = "en") {
48
+ if (!text) return "";
49
+ return text[locale] || text[fallback] || Object.values(text)[0] || "";
50
+ }
51
+ function buildCategoryTree(categories) {
52
+ const map = /* @__PURE__ */ new Map();
53
+ const roots = [];
54
+ categories.forEach((cat) => {
55
+ map.set(cat.id, { ...cat, children: [] });
56
+ });
57
+ categories.forEach((cat) => {
58
+ const node = map.get(cat.id);
59
+ if (cat.parent_id && map.has(cat.parent_id)) {
60
+ map.get(cat.parent_id).children.push(node);
61
+ } else {
62
+ roots.push(node);
63
+ }
64
+ });
65
+ const sortByOrder = (a, b) => a.sort_order - b.sort_order;
66
+ roots.sort(sortByOrder);
67
+ roots.forEach((root) => root.children?.sort(sortByOrder));
68
+ return roots;
69
+ }
70
+
71
+ // src/core.ts
72
+ function createUCMClient(config) {
73
+ const { url, anonKey, options = {} } = config;
74
+ if (!url || !anonKey) {
75
+ throw new Error("UCM: url and anonKey are required");
76
+ }
77
+ const supabase = (0, import_supabase_js.createClient)(url, anonKey, {
78
+ auth: {
79
+ persistSession: options.persistSession ?? false,
80
+ autoRefreshToken: options.autoRefreshToken ?? false
81
+ }
82
+ });
83
+ const contents = {
84
+ async list(params = {}) {
85
+ const {
86
+ type,
87
+ category_id,
88
+ tags,
89
+ featured,
90
+ visibility,
91
+ // v2.0: 可见性筛选
92
+ limit = 20,
93
+ offset = 0,
94
+ orderBy = "published_at",
95
+ orderDirection = "desc"
96
+ } = params;
97
+ let query = supabase.from("contents").select("*").order("pinned", { ascending: false }).order(orderBy, { ascending: orderDirection === "asc", nullsFirst: false }).range(offset, offset + limit - 1);
98
+ if (type) query = query.eq("type", type);
99
+ if (category_id) query = query.eq("category_id", category_id);
100
+ if (tags && tags.length > 0) query = query.overlaps("tags", tags);
101
+ if (featured !== void 0) query = query.eq("featured", featured);
102
+ if (visibility) query = query.eq("visibility", visibility);
103
+ const { data, error } = await query;
104
+ if (error) {
105
+ throw new Error(`UCM: Failed to fetch contents: ${error.message}`);
106
+ }
107
+ return data || [];
108
+ },
109
+ async getBySlug(slug) {
110
+ const { data, error } = await supabase.from("contents").select("*").eq("slug", slug).single();
111
+ if (error) {
112
+ if (error.code === "PGRST116") return null;
113
+ throw new Error(`UCM: Failed to fetch content: ${error.message}`);
114
+ }
115
+ return data;
116
+ },
117
+ async getById(id) {
118
+ const { data, error } = await supabase.from("contents").select("*").eq("id", id).single();
119
+ if (error) {
120
+ if (error.code === "PGRST116") return null;
121
+ throw new Error(`UCM: Failed to fetch content: ${error.message}`);
122
+ }
123
+ return data;
124
+ },
125
+ async count(params = {}) {
126
+ const { type, category_id, tags, featured, visibility } = params;
127
+ let query = supabase.from("contents").select("*", { count: "exact", head: true });
128
+ if (type) query = query.eq("type", type);
129
+ if (category_id) query = query.eq("category_id", category_id);
130
+ if (tags && tags.length > 0) query = query.overlaps("tags", tags);
131
+ if (featured !== void 0) query = query.eq("featured", featured);
132
+ if (visibility) query = query.eq("visibility", visibility);
133
+ const { count, error } = await query;
134
+ if (error) {
135
+ console.error("UCM: Failed to get count:", error);
136
+ return 0;
137
+ }
138
+ return count || 0;
139
+ },
140
+ async create(input) {
141
+ const { data, error } = await supabase.from("contents").insert(input).select().single();
142
+ if (error) {
143
+ throw new Error(`UCM: Failed to create content: ${error.message}`);
144
+ }
145
+ return data;
146
+ },
147
+ async update(id, input) {
148
+ const { data, error } = await supabase.from("contents").update(input).eq("id", id).select().single();
149
+ if (error) {
150
+ throw new Error(`UCM: Failed to update content: ${error.message}`);
151
+ }
152
+ return data;
153
+ },
154
+ async delete(id) {
155
+ const { error } = await supabase.from("contents").delete().eq("id", id);
156
+ if (error) {
157
+ throw new Error(`UCM: Failed to delete content: ${error.message}`);
158
+ }
159
+ },
160
+ async incrementView(id) {
161
+ try {
162
+ await supabase.rpc("increment_view_count", { content_id: id });
163
+ } catch {
164
+ }
165
+ },
166
+ async search(query, type, visibility = "public") {
167
+ if (!query || query.trim().length === 0) {
168
+ return [];
169
+ }
170
+ const { data, error } = await supabase.rpc("search_contents", {
171
+ search_query: query.trim(),
172
+ content_type: type || null,
173
+ content_visibility: visibility
174
+ });
175
+ if (error) {
176
+ throw new Error(`UCM: Failed to search contents: ${error.message}`);
177
+ }
178
+ return data || [];
179
+ },
180
+ async getTags(type) {
181
+ let query = supabase.from("contents").select("tags");
182
+ if (type) {
183
+ query = query.eq("type", type);
184
+ }
185
+ const { data, error } = await query;
186
+ if (error) {
187
+ console.error("UCM: Failed to fetch tags:", error);
188
+ return [];
189
+ }
190
+ const allTags = /* @__PURE__ */ new Set();
191
+ data?.forEach((item) => {
192
+ if (item.tags && Array.isArray(item.tags)) {
193
+ item.tags.forEach((tag) => allTags.add(tag));
194
+ }
195
+ });
196
+ return Array.from(allTags).sort((a, b) => a.localeCompare(b));
197
+ }
198
+ };
199
+ const categories = {
200
+ async list(params = {}) {
201
+ const { parent_id } = params;
202
+ let query = supabase.from("categories").select("*").order("sort_order");
203
+ if (parent_id !== void 0) {
204
+ if (parent_id === null) {
205
+ query = query.is("parent_id", null);
206
+ } else {
207
+ query = query.eq("parent_id", parent_id);
208
+ }
209
+ }
210
+ const { data, error } = await query;
211
+ if (error) {
212
+ throw new Error(`UCM: Failed to fetch categories: ${error.message}`);
213
+ }
214
+ return data || [];
215
+ },
216
+ async getTree() {
217
+ const allCategories = await this.list();
218
+ return buildCategoryTree(allCategories);
219
+ },
220
+ async getBySlug(slug) {
221
+ const { data, error } = await supabase.from("categories").select("*").eq("slug", slug).single();
222
+ if (error) {
223
+ if (error.code === "PGRST116") return null;
224
+ throw new Error(`UCM: Failed to fetch category: ${error.message}`);
225
+ }
226
+ return data;
227
+ },
228
+ async getById(id) {
229
+ const { data, error } = await supabase.from("categories").select("*").eq("id", id).single();
230
+ if (error) {
231
+ if (error.code === "PGRST116") return null;
232
+ throw new Error(`UCM: Failed to fetch category: ${error.message}`);
233
+ }
234
+ return data;
235
+ },
236
+ async getChildren(parentId) {
237
+ return this.list({ parent_id: parentId });
238
+ },
239
+ async getRoots() {
240
+ return this.list({ parent_id: null });
241
+ },
242
+ async getBreadcrumb(categoryId) {
243
+ const breadcrumb = [];
244
+ let currentId = categoryId;
245
+ while (currentId) {
246
+ const category = await this.getById(currentId);
247
+ if (!category) break;
248
+ breadcrumb.unshift(category);
249
+ currentId = category.parent_id;
250
+ }
251
+ return breadcrumb;
252
+ }
253
+ };
254
+ const storage = {
255
+ getUrl(path, bucket = "content-images") {
256
+ if (!path) return "";
257
+ if (path.startsWith("http")) return path;
258
+ return `${url}/storage/v1/object/public/${bucket}/${path}`;
259
+ },
260
+ getImageUrl(path, options2) {
261
+ const baseUrl = this.getUrl(path);
262
+ if (!baseUrl || !options2) return baseUrl;
263
+ const params = new URLSearchParams();
264
+ if (options2.width) params.set("width", options2.width.toString());
265
+ if (options2.height) params.set("height", options2.height.toString());
266
+ if (options2.quality) params.set("quality", options2.quality.toString());
267
+ if (options2.format) params.set("format", options2.format);
268
+ const queryString = params.toString();
269
+ return queryString ? `${baseUrl}?${queryString}` : baseUrl;
270
+ }
271
+ };
272
+ return {
273
+ supabase,
274
+ contents,
275
+ categories,
276
+ storage,
277
+ isConfigured: () => true
278
+ };
279
+ }
280
+ function createNullClient() {
281
+ const nullSupabase = null;
282
+ return {
283
+ supabase: nullSupabase,
284
+ contents: {
285
+ list: async () => [],
286
+ getBySlug: async () => null,
287
+ getById: async () => null,
288
+ count: async () => 0,
289
+ search: async () => [],
290
+ getTags: async () => [],
291
+ create: async () => {
292
+ throw new Error("UCM: Client not configured");
293
+ },
294
+ update: async () => {
295
+ throw new Error("UCM: Client not configured");
296
+ },
297
+ delete: async () => {
298
+ throw new Error("UCM: Client not configured");
299
+ },
300
+ incrementView: async () => {
301
+ }
302
+ },
303
+ categories: {
304
+ list: async () => [],
305
+ getTree: async () => [],
306
+ getBySlug: async () => null,
307
+ getById: async () => null,
308
+ getChildren: async () => [],
309
+ getRoots: async () => [],
310
+ getBreadcrumb: async () => []
311
+ },
312
+ storage: {
313
+ getUrl: () => "",
314
+ getImageUrl: () => ""
315
+ },
316
+ isConfigured: () => false
317
+ };
318
+ }
319
+
320
+ // src/react.tsx
321
+ var import_react = require("react");
322
+ var import_jsx_runtime = require("react/jsx-runtime");
323
+ var UCMContext = (0, import_react.createContext)(null);
324
+ function UCMProvider({ config, client, children }) {
325
+ const ucmClient = (0, import_react.useMemo)(() => {
326
+ if (client) {
327
+ return client;
328
+ }
329
+ if (config?.url && config?.anonKey) {
330
+ return createUCMClient(config);
331
+ }
332
+ console.warn("UCM: No config provided, using null client");
333
+ return createNullClient();
334
+ }, [config?.url, config?.anonKey, client]);
335
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UCMContext.Provider, { value: ucmClient, children });
336
+ }
337
+ function useUCM() {
338
+ const context = (0, import_react.useContext)(UCMContext);
339
+ if (!context) {
340
+ throw new Error("useUCM must be used within a UCMProvider");
341
+ }
342
+ return context;
343
+ }
344
+ function useUCMOptional() {
345
+ const context = (0, import_react.useContext)(UCMContext);
346
+ return context || createNullClient();
347
+ }
348
+ function useUCMConfigured() {
349
+ const ucm = useUCMOptional();
350
+ return ucm.isConfigured();
351
+ }
352
+
353
+ // src/hooks.ts
354
+ var import_react2 = require("react");
355
+ var import_react_query = require("@tanstack/react-query");
356
+ var ucmKeys = {
357
+ all: ["ucm"],
358
+ contents: () => [...ucmKeys.all, "contents"],
359
+ contentsList: (params) => [...ucmKeys.contents(), "list", params],
360
+ contentsInfinite: (params) => [...ucmKeys.contents(), "infinite", params],
361
+ content: (identifier) => [...ucmKeys.contents(), "detail", identifier],
362
+ contentsCount: (params) => [...ucmKeys.contents(), "count", params],
363
+ contentsTags: (type) => [...ucmKeys.contents(), "tags", type],
364
+ contentsSearch: (query, type) => [...ucmKeys.contents(), "search", query, type],
365
+ contentsAdjacent: (slug, type) => [...ucmKeys.contents(), "adjacent", slug, type],
366
+ categories: () => [...ucmKeys.all, "categories"],
367
+ categoriesList: (params) => [...ucmKeys.categories(), "list", params],
368
+ categoriesTree: () => [...ucmKeys.categories(), "tree"],
369
+ category: (identifier) => [...ucmKeys.categories(), "detail", identifier],
370
+ categoryBreadcrumb: (id) => [...ucmKeys.categories(), "breadcrumb", id]
371
+ };
372
+ function useContents(options = {}) {
373
+ const ucm = useUCMOptional();
374
+ const queryClient = (0, import_react_query.useQueryClient)();
375
+ const {
376
+ type,
377
+ category_id,
378
+ tags,
379
+ featured,
380
+ visibility,
381
+ limit = 20,
382
+ orderBy = "published_at",
383
+ orderDirection = "desc",
384
+ autoLoad = true,
385
+ paginated = true
386
+ } = options;
387
+ const queryParams = (0, import_react2.useMemo)(
388
+ () => ({
389
+ type,
390
+ category_id,
391
+ tags,
392
+ featured,
393
+ visibility,
394
+ limit,
395
+ orderBy,
396
+ orderDirection
397
+ }),
398
+ [
399
+ type,
400
+ category_id,
401
+ tags,
402
+ featured,
403
+ visibility,
404
+ limit,
405
+ orderBy,
406
+ orderDirection
407
+ ]
408
+ );
409
+ const { data: total = 0 } = (0, import_react_query.useQuery)({
410
+ queryKey: ucmKeys.contentsCount({
411
+ type,
412
+ category_id,
413
+ tags,
414
+ featured,
415
+ visibility
416
+ }),
417
+ queryFn: () => ucm.contents.count({ type, category_id, tags, featured, visibility }),
418
+ enabled: ucm.isConfigured() && autoLoad
419
+ });
420
+ const {
421
+ data,
422
+ isLoading,
423
+ isFetchingNextPage,
424
+ error,
425
+ hasNextPage,
426
+ fetchNextPage,
427
+ refetch
428
+ } = (0, import_react_query.useInfiniteQuery)({
429
+ queryKey: ucmKeys.contentsInfinite(queryParams),
430
+ queryFn: async ({ pageParam = 0 }) => {
431
+ return ucm.contents.list({
432
+ ...queryParams,
433
+ offset: pageParam * limit
434
+ });
435
+ },
436
+ getNextPageParam: (lastPage, allPages) => {
437
+ if (lastPage.length < limit) {
438
+ return void 0;
439
+ }
440
+ return allPages.length;
441
+ },
442
+ initialPageParam: 0,
443
+ enabled: ucm.isConfigured() && autoLoad
444
+ });
445
+ const contents = data?.pages.flat() ?? [];
446
+ const page = data?.pages.length ?? 0;
447
+ const load = (0, import_react2.useCallback)(async () => {
448
+ await refetch();
449
+ }, [refetch]);
450
+ const loadMore = (0, import_react2.useCallback)(async () => {
451
+ if (hasNextPage) {
452
+ await fetchNextPage();
453
+ }
454
+ }, [fetchNextPage, hasNextPage]);
455
+ const refresh = (0, import_react2.useCallback)(async () => {
456
+ await refetch();
457
+ }, [refetch]);
458
+ const reset = (0, import_react2.useCallback)(() => {
459
+ queryClient.removeQueries({
460
+ queryKey: ucmKeys.contentsInfinite(queryParams)
461
+ });
462
+ }, [queryClient, queryParams]);
463
+ return {
464
+ contents,
465
+ isLoading,
466
+ isLoadingMore: isFetchingNextPage,
467
+ error,
468
+ total,
469
+ hasMore: paginated ? hasNextPage ?? false : false,
470
+ page,
471
+ load,
472
+ loadMore,
473
+ refresh,
474
+ reset
475
+ };
476
+ }
477
+ function useContent(options = {}) {
478
+ const ucm = useUCMOptional();
479
+ const { slug, id, autoLoad = true, trackView = true } = options;
480
+ const viewTrackedRef = (0, import_react2.useRef)(false);
481
+ const identifier = (0, import_react2.useMemo)(() => ({ slug, id }), [slug, id]);
482
+ const incrementViewMutation = (0, import_react_query.useMutation)({
483
+ mutationFn: (contentId) => ucm.contents.incrementView(contentId)
484
+ });
485
+ const {
486
+ data: content,
487
+ isLoading,
488
+ error,
489
+ refetch
490
+ } = (0, import_react_query.useQuery)({
491
+ queryKey: ucmKeys.content(identifier),
492
+ queryFn: async () => {
493
+ if (slug) {
494
+ return ucm.contents.getBySlug(slug);
495
+ }
496
+ if (id) {
497
+ return ucm.contents.getById(id);
498
+ }
499
+ return null;
500
+ },
501
+ enabled: ucm.isConfigured() && autoLoad && !!(slug || id)
502
+ });
503
+ (0, import_react2.useEffect)(() => {
504
+ if (content && trackView && !viewTrackedRef.current) {
505
+ viewTrackedRef.current = true;
506
+ incrementViewMutation.mutate(content.id);
507
+ }
508
+ }, [content, trackView]);
509
+ (0, import_react2.useEffect)(() => {
510
+ viewTrackedRef.current = false;
511
+ }, [slug, id]);
512
+ const load = (0, import_react2.useCallback)(async () => {
513
+ await refetch();
514
+ }, [refetch]);
515
+ const refresh = (0, import_react2.useCallback)(async () => {
516
+ viewTrackedRef.current = true;
517
+ await refetch();
518
+ }, [refetch]);
519
+ return {
520
+ content: content ?? null,
521
+ isLoading,
522
+ error,
523
+ notFound: !isLoading && !error && !content && !!(slug || id),
524
+ load,
525
+ refresh
526
+ };
527
+ }
528
+ function useCategories(options = {}) {
529
+ const ucm = useUCMOptional();
530
+ const { parentId, asTree = false, autoLoad = true } = options;
531
+ const listQuery = (0, import_react_query.useQuery)({
532
+ queryKey: ucmKeys.categoriesList({ parentId }),
533
+ queryFn: async () => {
534
+ if (parentId === null) {
535
+ return ucm.categories.getRoots();
536
+ }
537
+ if (parentId) {
538
+ return ucm.categories.getChildren(parentId);
539
+ }
540
+ return ucm.categories.list();
541
+ },
542
+ enabled: ucm.isConfigured() && autoLoad
543
+ });
544
+ const treeQuery = (0, import_react_query.useQuery)({
545
+ queryKey: ucmKeys.categoriesTree(),
546
+ queryFn: () => ucm.categories.getTree(),
547
+ enabled: ucm.isConfigured() && autoLoad && asTree
548
+ });
549
+ const load = (0, import_react2.useCallback)(async () => {
550
+ await Promise.all([
551
+ listQuery.refetch(),
552
+ asTree ? treeQuery.refetch() : Promise.resolve()
553
+ ]);
554
+ }, [listQuery, treeQuery, asTree]);
555
+ const refresh = (0, import_react2.useCallback)(async () => {
556
+ await load();
557
+ }, [load]);
558
+ return {
559
+ categories: listQuery.data ?? [],
560
+ tree: treeQuery.data ?? [],
561
+ isLoading: listQuery.isLoading || asTree && treeQuery.isLoading,
562
+ error: listQuery.error || treeQuery.error,
563
+ load,
564
+ refresh
565
+ };
566
+ }
567
+ function useCategory(options = {}) {
568
+ const ucm = useUCMOptional();
569
+ const { slug, id, autoLoad = true } = options;
570
+ const identifier = (0, import_react2.useMemo)(() => ({ slug, id }), [slug, id]);
571
+ const {
572
+ data: category,
573
+ isLoading,
574
+ error,
575
+ refetch
576
+ } = (0, import_react_query.useQuery)({
577
+ queryKey: ucmKeys.category(identifier),
578
+ queryFn: async () => {
579
+ if (slug) {
580
+ return ucm.categories.getBySlug(slug);
581
+ }
582
+ if (id) {
583
+ return ucm.categories.getById(id);
584
+ }
585
+ return null;
586
+ },
587
+ enabled: ucm.isConfigured() && autoLoad && !!(slug || id)
588
+ });
589
+ const load = (0, import_react2.useCallback)(async () => {
590
+ await refetch();
591
+ }, [refetch]);
592
+ return {
593
+ category: category ?? null,
594
+ isLoading,
595
+ error,
596
+ notFound: !isLoading && !error && !category && !!(slug || id),
597
+ load
598
+ };
599
+ }
600
+ function useCategoryBreadcrumb(categoryId) {
601
+ const ucm = useUCMOptional();
602
+ const {
603
+ data: breadcrumb,
604
+ isLoading,
605
+ error,
606
+ refetch
607
+ } = (0, import_react_query.useQuery)({
608
+ queryKey: ucmKeys.categoryBreadcrumb(categoryId ?? ""),
609
+ queryFn: () => ucm.categories.getBreadcrumb(categoryId),
610
+ enabled: ucm.isConfigured() && !!categoryId
611
+ });
612
+ const load = (0, import_react2.useCallback)(async () => {
613
+ await refetch();
614
+ }, [refetch]);
615
+ return {
616
+ breadcrumb: breadcrumb ?? [],
617
+ isLoading,
618
+ error,
619
+ load
620
+ };
621
+ }
622
+ function useAdjacentContents(options) {
623
+ const ucm = useUCMOptional();
624
+ const { slug, type = "blog", autoLoad = true } = options;
625
+ const {
626
+ data: adjacent,
627
+ isLoading,
628
+ error,
629
+ refetch
630
+ } = (0, import_react_query.useQuery)({
631
+ queryKey: ucmKeys.contentsAdjacent(slug, type),
632
+ queryFn: async () => {
633
+ const current = await ucm.contents.getBySlug(slug);
634
+ if (!current || !current.published_at) {
635
+ return { prev: null, next: null };
636
+ }
637
+ const allContents = await ucm.contents.list({
638
+ type,
639
+ limit: 100,
640
+ orderBy: "published_at",
641
+ orderDirection: "desc"
642
+ });
643
+ const currentIndex = allContents.findIndex((c) => c.slug === slug);
644
+ let prev = null;
645
+ let next = null;
646
+ if (currentIndex !== -1) {
647
+ if (currentIndex > 0) {
648
+ next = allContents[currentIndex - 1];
649
+ }
650
+ if (currentIndex < allContents.length - 1) {
651
+ prev = allContents[currentIndex + 1];
652
+ }
653
+ }
654
+ return { prev, next };
655
+ },
656
+ enabled: ucm.isConfigured() && autoLoad && !!slug
657
+ });
658
+ const load = (0, import_react2.useCallback)(async () => {
659
+ await refetch();
660
+ }, [refetch]);
661
+ return {
662
+ adjacent: adjacent ?? { prev: null, next: null },
663
+ isLoading,
664
+ error,
665
+ load
666
+ };
667
+ }
668
+ function useSearchContents(options = {}) {
669
+ const ucm = useUCMOptional();
670
+ const { type, debounceMs = 300 } = options;
671
+ const [query, setQuery] = (0, import_react2.useState)("");
672
+ const [debouncedQuery, setDebouncedQuery] = (0, import_react2.useState)("");
673
+ const debounceTimerRef = (0, import_react2.useRef)(null);
674
+ (0, import_react2.useEffect)(() => {
675
+ if (debounceTimerRef.current) {
676
+ clearTimeout(debounceTimerRef.current);
677
+ }
678
+ debounceTimerRef.current = setTimeout(() => {
679
+ setDebouncedQuery(query);
680
+ }, debounceMs);
681
+ return () => {
682
+ if (debounceTimerRef.current) {
683
+ clearTimeout(debounceTimerRef.current);
684
+ }
685
+ };
686
+ }, [query, debounceMs]);
687
+ const {
688
+ data: results,
689
+ isLoading: isSearching,
690
+ error
691
+ } = (0, import_react_query.useQuery)({
692
+ queryKey: ucmKeys.contentsSearch(debouncedQuery, type),
693
+ queryFn: () => ucm.contents.search(debouncedQuery.trim(), type),
694
+ enabled: ucm.isConfigured() && debouncedQuery.trim().length > 0
695
+ });
696
+ const search = (0, import_react2.useCallback)((searchQuery) => {
697
+ setQuery(searchQuery);
698
+ }, []);
699
+ const clear = (0, import_react2.useCallback)(() => {
700
+ setQuery("");
701
+ setDebouncedQuery("");
702
+ }, []);
703
+ return {
704
+ results: results ?? [],
705
+ isSearching: isSearching || query !== debouncedQuery,
706
+ error,
707
+ query,
708
+ search,
709
+ clear
710
+ };
711
+ }
712
+ function useTags(options = {}) {
713
+ const ucm = useUCMOptional();
714
+ const { type, autoLoad = true } = options;
715
+ const {
716
+ data: tags,
717
+ isLoading,
718
+ error,
719
+ refetch
720
+ } = (0, import_react_query.useQuery)({
721
+ queryKey: ucmKeys.contentsTags(type),
722
+ queryFn: () => ucm.contents.getTags(type),
723
+ enabled: ucm.isConfigured() && autoLoad
724
+ });
725
+ const load = (0, import_react2.useCallback)(async () => {
726
+ await refetch();
727
+ }, [refetch]);
728
+ const refresh = (0, import_react2.useCallback)(async () => {
729
+ await refetch();
730
+ }, [refetch]);
731
+ return {
732
+ tags: tags ?? [],
733
+ isLoading,
734
+ error,
735
+ load,
736
+ refresh
737
+ };
738
+ }
739
+ function useThoughts(options = {}) {
740
+ const { agentId, thoughtType, limit = 10, autoLoad = true } = options;
741
+ const {
742
+ contents,
743
+ isLoading,
744
+ isLoadingMore,
745
+ error,
746
+ hasMore,
747
+ total,
748
+ load,
749
+ loadMore,
750
+ refresh
751
+ } = useContents({
752
+ type: "thought",
753
+ limit,
754
+ autoLoad,
755
+ orderBy: "created_at",
756
+ orderDirection: "desc",
757
+ paginated: true
758
+ });
759
+ const thoughts = contents.filter((c) => {
760
+ const metadata = c.metadata;
761
+ if (agentId && metadata?.agent_id !== agentId) return false;
762
+ if (thoughtType && metadata?.thought_type !== thoughtType) return false;
763
+ return true;
764
+ });
765
+ return {
766
+ thoughts,
767
+ isLoading,
768
+ isLoadingMore,
769
+ error,
770
+ hasMore,
771
+ total,
772
+ load,
773
+ loadMore,
774
+ refresh
775
+ };
776
+ }
777
+ // Annotate the CommonJS export names for ESM import in node:
778
+ 0 && (module.exports = {
779
+ UCMProvider,
780
+ buildCategoryTree,
781
+ createNullClient,
782
+ createUCMClient,
783
+ getLocalizedText,
784
+ useAdjacentContents,
785
+ useCategories,
786
+ useCategory,
787
+ useCategoryBreadcrumb,
788
+ useContent,
789
+ useContents,
790
+ useSearchContents,
791
+ useTags,
792
+ useThoughts,
793
+ useUCM,
794
+ useUCMConfigured,
795
+ useUCMOptional
796
+ });
797
+ //# sourceMappingURL=index.cjs.map