@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.
@@ -0,0 +1,492 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ReactNode } from 'react';
4
+
5
+ /**
6
+ * Universal Content Module - Type Definitions
7
+ * Supabase-powered content management with multi-language support
8
+ */
9
+ type ContentType = 'news' | 'daily' | 'blog' | 'doc' | 'event' | 'announcement' | 'tutorial' | 'case_study' | 'faq' | 'changelog' | 'thought' | 'digest' | 'briefing';
10
+ type ContentStatus = 'draft' | 'scheduled' | 'published' | 'archived';
11
+ type ContentVisibility = 'public' | 'internal' | 'unlisted';
12
+ type SupportedLanguage = 'zh' | 'en' | 'ja' | 'ko' | 'es' | 'de' | 'fr';
13
+ type I18nText = Partial<Record<SupportedLanguage, string>>;
14
+ interface Content {
15
+ id: string;
16
+ slug: string;
17
+ type: ContentType;
18
+ status: ContentStatus;
19
+ visibility: ContentVisibility;
20
+ category_id: string | null;
21
+ title: I18nText;
22
+ description: I18nText;
23
+ content: I18nText;
24
+ cover_image: string | null;
25
+ images: Array<{
26
+ url: string;
27
+ alt?: I18nText;
28
+ }>;
29
+ tags: string[];
30
+ author: string;
31
+ source: string | null;
32
+ source_url: string | null;
33
+ metadata: ContentMetadata;
34
+ seo: SeoData;
35
+ featured: boolean;
36
+ pinned: boolean;
37
+ published_at: string | null;
38
+ scheduled_at: string | null;
39
+ created_at: string;
40
+ updated_at: string;
41
+ sort_order: number;
42
+ view_count: number;
43
+ }
44
+ interface NewsMetadata {
45
+ wechat_msg_id?: string;
46
+ original_author?: string;
47
+ subtype?: 'press' | 'announcement' | 'changelog';
48
+ version?: string;
49
+ }
50
+ interface DailyMetadata {
51
+ date: string;
52
+ market?: Record<string, string>;
53
+ highlights?: string[];
54
+ }
55
+ interface DigestMetadata {
56
+ topic?: string;
57
+ highlights?: string[];
58
+ sources?: string[];
59
+ date?: string;
60
+ }
61
+ type BriefingFrequency = 'daily' | 'weekly' | 'monthly';
62
+ interface BriefingMetadata {
63
+ frequency: BriefingFrequency;
64
+ period: string;
65
+ metrics?: Record<string, number>;
66
+ highlights?: string[];
67
+ tasks_completed?: number;
68
+ tasks_pending?: number;
69
+ }
70
+ interface BlogMetadata {
71
+ reading_time?: number;
72
+ series?: string;
73
+ toc?: boolean;
74
+ }
75
+ interface DocMetadata {
76
+ version?: string;
77
+ prev_slug?: string;
78
+ next_slug?: string;
79
+ }
80
+ interface EventMetadata {
81
+ start_time: string;
82
+ end_time: string;
83
+ location?: {
84
+ type: 'online' | 'offline';
85
+ url?: string;
86
+ address?: string;
87
+ };
88
+ registration_url?: string;
89
+ max_attendees?: number;
90
+ current_attendees?: number;
91
+ }
92
+ type ThoughtType = 'observation' | 'reflection' | 'decision' | 'question' | 'insight' | 'mood';
93
+ type ThoughtMood = 'curious' | 'focused' | 'excited' | 'thoughtful' | 'creative' | 'philosophical' | 'accomplished' | 'inspired' | 'productive' | 'concerned' | 'determined' | 'grateful' | 'contemplative';
94
+ interface ThoughtMediaVideo {
95
+ url: string;
96
+ type: 'mp4' | 'youtube';
97
+ }
98
+ interface ThoughtMediaAudio {
99
+ url: string;
100
+ title?: string;
101
+ duration?: number;
102
+ }
103
+ interface ThoughtProductionLink {
104
+ slug: string;
105
+ title?: I18nText | string;
106
+ type?: 'blog' | 'video' | 'tool';
107
+ }
108
+ interface ThoughtComment {
109
+ user: string;
110
+ content: string;
111
+ time?: string;
112
+ }
113
+ interface ThoughtMetadata {
114
+ thought_type: ThoughtType;
115
+ agent_id: string;
116
+ mood?: ThoughtMood;
117
+ energy?: number;
118
+ images?: string[];
119
+ video?: ThoughtMediaVideo;
120
+ audio?: ThoughtMediaAudio;
121
+ leads_to?: string | ThoughtProductionLink;
122
+ comments?: ThoughtComment[];
123
+ ai_generated?: boolean;
124
+ generated_at?: string;
125
+ model?: string;
126
+ }
127
+ interface CaseStudyMetric {
128
+ label: I18nText;
129
+ before: string;
130
+ after: string;
131
+ }
132
+ interface CaseStudyMetadata {
133
+ product: string;
134
+ productRoute: string;
135
+ client: I18nText;
136
+ industry: I18nText;
137
+ scale: I18nText;
138
+ challenge: I18nText;
139
+ solution: I18nText;
140
+ metrics: CaseStudyMetric[];
141
+ quote: I18nText;
142
+ quotee: I18nText;
143
+ }
144
+ type ContentMetadata = NewsMetadata | DailyMetadata | DigestMetadata | BriefingMetadata | BlogMetadata | DocMetadata | EventMetadata | ThoughtMetadata | CaseStudyMetadata | Record<string, unknown>;
145
+ interface SeoData {
146
+ title?: I18nText;
147
+ description?: I18nText;
148
+ keywords?: string[];
149
+ og_image?: string;
150
+ canonical_url?: string;
151
+ }
152
+ interface Category {
153
+ id: string;
154
+ parent_id: string | null;
155
+ slug: string;
156
+ name: I18nText;
157
+ description: I18nText;
158
+ icon: string | null;
159
+ sort_order: number;
160
+ created_at: string;
161
+ children?: Category[];
162
+ }
163
+ interface ContentQueryParams {
164
+ type?: ContentType;
165
+ category_id?: string;
166
+ tags?: string[];
167
+ featured?: boolean;
168
+ visibility?: ContentVisibility;
169
+ limit?: number;
170
+ offset?: number;
171
+ orderBy?: 'published_at' | 'created_at' | 'sort_order' | 'view_count';
172
+ orderDirection?: 'asc' | 'desc';
173
+ }
174
+ interface CategoryQueryParams {
175
+ parent_id?: string | null;
176
+ includeChildren?: boolean;
177
+ }
178
+ interface CreateContentInput {
179
+ slug: string;
180
+ type: ContentType;
181
+ status?: ContentStatus;
182
+ visibility?: ContentVisibility;
183
+ category_id?: string;
184
+ title: I18nText;
185
+ description?: I18nText;
186
+ content: I18nText;
187
+ cover_image?: string;
188
+ images?: Array<{
189
+ url: string;
190
+ alt?: I18nText;
191
+ }>;
192
+ tags?: string[];
193
+ author?: string;
194
+ source?: string;
195
+ source_url?: string;
196
+ metadata?: ContentMetadata;
197
+ seo?: SeoData;
198
+ featured?: boolean;
199
+ pinned?: boolean;
200
+ published_at?: string;
201
+ scheduled_at?: string;
202
+ }
203
+ type UpdateContentInput = Partial<CreateContentInput>;
204
+ /**
205
+ * Get localized text with fallback
206
+ */
207
+ declare function getLocalizedText(text: I18nText | null | undefined, locale: string, fallback?: string): string;
208
+ /**
209
+ * Build category tree from flat list
210
+ */
211
+ declare function buildCategoryTree(categories: Category[]): Category[];
212
+
213
+ /**
214
+ * Universal Content Module - Core
215
+ *
216
+ * 工厂函数模式,支持:
217
+ * - 依赖注入(便于测试)
218
+ * - 多实例(多租户场景)
219
+ * - 框架无关(React/Vue/Node.js 通用)
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * // 创建客户端
224
+ * const ucm = createUCMClient({
225
+ * url: 'https://xxx.supabase.co',
226
+ * anonKey: 'eyJxxx',
227
+ * })
228
+ *
229
+ * // 使用 API
230
+ * const posts = await ucm.contents.list({ type: 'blog', limit: 10 })
231
+ * const post = await ucm.contents.getBySlug('hello-world')
232
+ * const categories = await ucm.categories.getTree()
233
+ * ```
234
+ */
235
+
236
+ interface UCMClientConfig {
237
+ /** Supabase project URL */
238
+ url: string;
239
+ /** Supabase anon key (for public read) or service key (for write) */
240
+ anonKey: string;
241
+ /** Optional: custom Supabase client options */
242
+ options?: {
243
+ persistSession?: boolean;
244
+ autoRefreshToken?: boolean;
245
+ };
246
+ }
247
+ interface UCMClient {
248
+ /** Raw Supabase client (for advanced use) */
249
+ supabase: SupabaseClient;
250
+ /** Content operations */
251
+ contents: {
252
+ list: (params?: ContentQueryParams) => Promise<Content[]>;
253
+ getBySlug: (slug: string) => Promise<Content | null>;
254
+ getById: (id: string) => Promise<Content | null>;
255
+ count: (params?: Pick<ContentQueryParams, 'type' | 'category_id' | 'tags' | 'featured' | 'visibility'>) => Promise<number>;
256
+ search: (query: string, type?: string, visibility?: string) => Promise<Content[]>;
257
+ getTags: (type?: string) => Promise<string[]>;
258
+ create: (input: CreateContentInput) => Promise<Content>;
259
+ update: (id: string, input: UpdateContentInput) => Promise<Content>;
260
+ delete: (id: string) => Promise<void>;
261
+ incrementView: (id: string) => Promise<void>;
262
+ };
263
+ /** Category operations */
264
+ categories: {
265
+ list: (params?: CategoryQueryParams) => Promise<Category[]>;
266
+ getTree: () => Promise<Category[]>;
267
+ getBySlug: (slug: string) => Promise<Category | null>;
268
+ getById: (id: string) => Promise<Category | null>;
269
+ getChildren: (parentId: string) => Promise<Category[]>;
270
+ getRoots: () => Promise<Category[]>;
271
+ getBreadcrumb: (categoryId: string) => Promise<Category[]>;
272
+ };
273
+ /** Storage helpers */
274
+ storage: {
275
+ getUrl: (path: string, bucket?: string) => string;
276
+ getImageUrl: (path: string, options?: {
277
+ width?: number;
278
+ height?: number;
279
+ quality?: number;
280
+ format?: string;
281
+ }) => string;
282
+ };
283
+ /** Check if client is configured */
284
+ isConfigured: () => boolean;
285
+ }
286
+ /**
287
+ * Create a UCM client instance
288
+ *
289
+ * @param config - Configuration object with Supabase credentials
290
+ * @returns UCM client with contents, categories, and storage APIs
291
+ */
292
+ declare function createUCMClient(config: UCMClientConfig): UCMClient;
293
+ /**
294
+ * Create a null client that returns empty data
295
+ * Useful for SSR or when Supabase is not configured
296
+ */
297
+ declare function createNullClient(): UCMClient;
298
+
299
+ interface UCMProviderProps {
300
+ /** UCM 配置(url + anonKey) */
301
+ config?: UCMClientConfig;
302
+ /** 或直接传入已创建的客户端(便于测试) */
303
+ client?: UCMClient;
304
+ /** 子组件 */
305
+ children: ReactNode;
306
+ }
307
+ /**
308
+ * UCM Provider
309
+ *
310
+ * 提供两种使用方式:
311
+ * 1. 传入 config,自动创建客户端
312
+ * 2. 传入 client,使用已有客户端(测试场景)
313
+ */
314
+ declare function UCMProvider({ config, client, children }: UCMProviderProps): react_jsx_runtime.JSX.Element;
315
+ /**
316
+ * 获取 UCM 客户端
317
+ *
318
+ * @throws 如果在 UCMProvider 外使用
319
+ */
320
+ declare function useUCM(): UCMClient;
321
+ /**
322
+ * 获取 UCM 客户端(可选,不抛错)
323
+ *
324
+ * 如果在 Provider 外使用,返回 null client
325
+ */
326
+ declare function useUCMOptional(): UCMClient;
327
+ /**
328
+ * 检查 UCM 是否已配置
329
+ */
330
+ declare function useUCMConfigured(): boolean;
331
+
332
+ /**
333
+ * Universal Content Module - React Hooks (v2.0 - React Query)
334
+ *
335
+ * 基于 TanStack React Query 的 Hooks,支持:
336
+ * - 自动缓存和后台刷新
337
+ * - 请求去重
338
+ * - 无限滚动分页
339
+ * - DevTools 调试
340
+ *
341
+ * @example
342
+ * ```tsx
343
+ * // 使用内容列表
344
+ * const { contents, isLoading, loadMore } = useContents({ type: 'blog' })
345
+ *
346
+ * // 使用单条内容
347
+ * const { content, isLoading } = useContent({ slug: 'hello-world' })
348
+ *
349
+ * // 使用分类树
350
+ * const { tree, isLoading } = useCategories({ asTree: true })
351
+ * ```
352
+ */
353
+
354
+ interface UseContentsOptions extends ContentQueryParams {
355
+ /** 是否自动加载(默认 true) */
356
+ autoLoad?: boolean;
357
+ /** 是否启用分页(默认 true) */
358
+ paginated?: boolean;
359
+ }
360
+ interface UseContentsReturn {
361
+ contents: Content[];
362
+ isLoading: boolean;
363
+ isLoadingMore: boolean;
364
+ error: Error | null;
365
+ total: number;
366
+ hasMore: boolean;
367
+ page: number;
368
+ load: () => Promise<void>;
369
+ loadMore: () => Promise<void>;
370
+ refresh: () => Promise<void>;
371
+ reset: () => void;
372
+ }
373
+ declare function useContents(options?: UseContentsOptions): UseContentsReturn;
374
+ interface UseContentOptions {
375
+ slug?: string;
376
+ id?: string;
377
+ autoLoad?: boolean;
378
+ trackView?: boolean;
379
+ }
380
+ interface UseContentReturn {
381
+ content: Content | null;
382
+ isLoading: boolean;
383
+ error: Error | null;
384
+ notFound: boolean;
385
+ load: () => Promise<void>;
386
+ refresh: () => Promise<void>;
387
+ }
388
+ declare function useContent(options?: UseContentOptions): UseContentReturn;
389
+ interface UseCategoriesOptions {
390
+ parentId?: string | null;
391
+ asTree?: boolean;
392
+ autoLoad?: boolean;
393
+ }
394
+ interface UseCategoriesReturn {
395
+ categories: Category[];
396
+ tree: Category[];
397
+ isLoading: boolean;
398
+ error: Error | null;
399
+ load: () => Promise<void>;
400
+ refresh: () => Promise<void>;
401
+ }
402
+ declare function useCategories(options?: UseCategoriesOptions): UseCategoriesReturn;
403
+ interface UseCategoryOptions {
404
+ slug?: string;
405
+ id?: string;
406
+ autoLoad?: boolean;
407
+ }
408
+ interface UseCategoryReturn {
409
+ category: Category | null;
410
+ isLoading: boolean;
411
+ error: Error | null;
412
+ notFound: boolean;
413
+ load: () => Promise<void>;
414
+ }
415
+ declare function useCategory(options?: UseCategoryOptions): UseCategoryReturn;
416
+ interface UseCategoryBreadcrumbReturn {
417
+ breadcrumb: Category[];
418
+ isLoading: boolean;
419
+ error: Error | null;
420
+ load: () => Promise<void>;
421
+ }
422
+ declare function useCategoryBreadcrumb(categoryId: string | null | undefined): UseCategoryBreadcrumbReturn;
423
+ interface AdjacentContents {
424
+ prev: Content | null;
425
+ next: Content | null;
426
+ }
427
+ interface UseAdjacentContentsOptions {
428
+ slug: string;
429
+ type?: string;
430
+ autoLoad?: boolean;
431
+ }
432
+ interface UseAdjacentContentsReturn {
433
+ adjacent: AdjacentContents;
434
+ isLoading: boolean;
435
+ error: Error | null;
436
+ load: () => Promise<void>;
437
+ }
438
+ declare function useAdjacentContents(options: UseAdjacentContentsOptions): UseAdjacentContentsReturn;
439
+ interface UseSearchContentsOptions {
440
+ /** 内容类型过滤 */
441
+ type?: string;
442
+ /** 防抖延迟(毫秒) */
443
+ debounceMs?: number;
444
+ }
445
+ interface UseSearchContentsReturn {
446
+ results: Content[];
447
+ isSearching: boolean;
448
+ error: Error | null;
449
+ query: string;
450
+ search: (query: string) => void;
451
+ clear: () => void;
452
+ }
453
+ declare function useSearchContents(options?: UseSearchContentsOptions): UseSearchContentsReturn;
454
+ interface UseTagsOptions {
455
+ /** 内容类型过滤 */
456
+ type?: string;
457
+ /** 是否自动加载 */
458
+ autoLoad?: boolean;
459
+ }
460
+ interface UseTagsReturn {
461
+ tags: string[];
462
+ isLoading: boolean;
463
+ error: Error | null;
464
+ load: () => Promise<void>;
465
+ refresh: () => Promise<void>;
466
+ }
467
+ declare function useTags(options?: UseTagsOptions): UseTagsReturn;
468
+
469
+ interface UseThoughtsOptions {
470
+ /** Agent ID 过滤 */
471
+ agentId?: string;
472
+ /** 思考类型过滤 */
473
+ thoughtType?: ThoughtType;
474
+ /** 限制数量 */
475
+ limit?: number;
476
+ /** 是否自动加载 */
477
+ autoLoad?: boolean;
478
+ }
479
+ interface UseThoughtsReturn {
480
+ thoughts: Content[];
481
+ isLoading: boolean;
482
+ isLoadingMore: boolean;
483
+ error: Error | null;
484
+ hasMore: boolean;
485
+ total: number;
486
+ load: () => Promise<void>;
487
+ loadMore: () => Promise<void>;
488
+ refresh: () => Promise<void>;
489
+ }
490
+ declare function useThoughts(options?: UseThoughtsOptions): UseThoughtsReturn;
491
+
492
+ export { type AdjacentContents, type BlogMetadata, type BriefingFrequency, type BriefingMetadata, type CaseStudyMetadata, type CaseStudyMetric, type Category, type CategoryQueryParams, type Content, type ContentMetadata, type ContentQueryParams, type ContentStatus, type ContentType, type ContentVisibility, type CreateContentInput, type DailyMetadata, type DigestMetadata, type DocMetadata, type EventMetadata, type I18nText, type NewsMetadata, type SeoData, type SupportedLanguage, type ThoughtComment, type ThoughtMediaAudio, type ThoughtMediaVideo, type ThoughtMetadata, type ThoughtMood, type ThoughtProductionLink, type ThoughtType, type UCMClient, type UCMClientConfig, UCMProvider, type UCMProviderProps, type UpdateContentInput, type UseAdjacentContentsOptions, type UseAdjacentContentsReturn, type UseCategoriesOptions, type UseCategoriesReturn, type UseCategoryBreadcrumbReturn, type UseCategoryOptions, type UseCategoryReturn, type UseContentOptions, type UseContentReturn, type UseContentsOptions, type UseContentsReturn, type UseSearchContentsOptions, type UseSearchContentsReturn, type UseTagsOptions, type UseTagsReturn, type UseThoughtsOptions, type UseThoughtsReturn, buildCategoryTree, createNullClient, createUCMClient, getLocalizedText, useAdjacentContents, useCategories, useCategory, useCategoryBreadcrumb, useContent, useContents, useSearchContents, useTags, useThoughts, useUCM, useUCMConfigured, useUCMOptional };