@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/README.md +260 -0
- package/dist/index.cjs +797 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +492 -0
- package/dist/index.d.ts +492 -0
- package/dist/index.js +759 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +341 -0
- package/dist/react.d.cts +312 -0
- package/dist/react.d.ts +312 -0
- package/dist/react.js +313 -0
- package/package.json +65 -0
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
|