@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 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core.ts","../src/types.ts","../src/react.tsx","../src/hooks.ts"],"sourcesContent":["/**\n * Universal Content Module (UCM)\n *\n * 通用内容管理模块,支持:\n * - 多语言内容(JSONB 无限扩展)\n * - 多内容类型(news, blog, doc, event 等)\n * - 多级分类菜单\n * - 工厂函数模式(便于测试、多实例)\n * - React Provider 集成\n *\n * @example\n * ```typescript\n * // 方式 1:使用工厂函数(推荐,框架无关)\n * import { createUCMClient } from '@smartchainark/ucm'\n *\n * const ucm = createUCMClient({ url: '...', anonKey: '...' })\n * const posts = await ucm.contents.list({ type: 'blog' })\n *\n * // 方式 2:使用 React Provider + Hooks\n * import { UCMProvider, useContents } from '@smartchainark/ucm'\n *\n * <UCMProvider config={{ url: '...', anonKey: '...' }}>\n * <App />\n * </UCMProvider>\n * ```\n */\n\n// ============================================\n// Core - 工厂函数模式(框架无关)\n// ============================================\n\nexport {\n createUCMClient,\n createNullClient,\n type UCMClient,\n type UCMClientConfig,\n} from './core'\n\n// ============================================\n// React Integration - Provider + Hooks\n// ============================================\n\nexport {\n UCMProvider,\n useUCM,\n useUCMOptional,\n useUCMConfigured,\n type UCMProviderProps,\n} from './react'\n\nexport {\n useContents,\n useContent,\n useCategories,\n useCategory,\n useCategoryBreadcrumb,\n useAdjacentContents,\n useSearchContents,\n useTags,\n useThoughts,\n type ThoughtType,\n type UseContentsOptions,\n type UseContentsReturn,\n type UseContentOptions,\n type UseContentReturn,\n type UseCategoriesOptions,\n type UseCategoriesReturn,\n type UseCategoryOptions,\n type UseCategoryReturn,\n type UseCategoryBreadcrumbReturn,\n type AdjacentContents,\n type UseAdjacentContentsOptions,\n type UseAdjacentContentsReturn,\n type UseSearchContentsOptions,\n type UseSearchContentsReturn,\n type UseTagsOptions,\n type UseTagsReturn,\n type UseThoughtsOptions,\n type UseThoughtsReturn,\n} from './hooks'\n\n// ============================================\n// Types\n// ============================================\n\nexport type {\n ContentType,\n ContentStatus,\n ContentVisibility,\n SupportedLanguage,\n I18nText,\n Content,\n ContentMetadata,\n NewsMetadata,\n DailyMetadata,\n DigestMetadata,\n BriefingMetadata,\n BriefingFrequency,\n BlogMetadata,\n DocMetadata,\n EventMetadata,\n ThoughtMetadata,\n ThoughtMood,\n ThoughtMediaVideo,\n ThoughtMediaAudio,\n ThoughtProductionLink,\n ThoughtComment,\n CaseStudyMetadata,\n CaseStudyMetric,\n SeoData,\n Category,\n ContentQueryParams,\n CategoryQueryParams,\n CreateContentInput,\n UpdateContentInput,\n} from './types'\n\nexport { getLocalizedText, buildCategoryTree } from './types'\n\n","/**\n * Universal Content Module - Core\n *\n * 工厂函数模式,支持:\n * - 依赖注入(便于测试)\n * - 多实例(多租户场景)\n * - 框架无关(React/Vue/Node.js 通用)\n *\n * @example\n * ```typescript\n * // 创建客户端\n * const ucm = createUCMClient({\n * url: 'https://xxx.supabase.co',\n * anonKey: 'eyJxxx',\n * })\n *\n * // 使用 API\n * const posts = await ucm.contents.list({ type: 'blog', limit: 10 })\n * const post = await ucm.contents.getBySlug('hello-world')\n * const categories = await ucm.categories.getTree()\n * ```\n */\n\nimport { createClient, type SupabaseClient } from '@supabase/supabase-js'\nimport {\n buildCategoryTree,\n type Content,\n type Category,\n type ContentQueryParams,\n type CategoryQueryParams,\n type CreateContentInput,\n type UpdateContentInput,\n} from './types'\n\n// ============================================\n// Configuration Types\n// ============================================\n\nexport interface UCMClientConfig {\n /** Supabase project URL */\n url: string\n /** Supabase anon key (for public read) or service key (for write) */\n anonKey: string\n /** Optional: custom Supabase client options */\n options?: {\n persistSession?: boolean\n autoRefreshToken?: boolean\n }\n}\n\n// ============================================\n// UCM Client Interface\n// ============================================\n\nexport interface UCMClient {\n /** Raw Supabase client (for advanced use) */\n supabase: SupabaseClient\n\n /** Content operations */\n contents: {\n list: (params?: ContentQueryParams) => Promise<Content[]>\n getBySlug: (slug: string) => Promise<Content | null>\n getById: (id: string) => Promise<Content | null>\n count: (\n params?: Pick<\n ContentQueryParams,\n 'type' | 'category_id' | 'tags' | 'featured' | 'visibility'\n >\n ) => Promise<number>\n search: (query: string, type?: string, visibility?: string) => Promise<Content[]>\n getTags: (type?: string) => Promise<string[]>\n create: (input: CreateContentInput) => Promise<Content>\n update: (id: string, input: UpdateContentInput) => Promise<Content>\n delete: (id: string) => Promise<void>\n incrementView: (id: string) => Promise<void>\n }\n\n /** Category operations */\n categories: {\n list: (params?: CategoryQueryParams) => Promise<Category[]>\n getTree: () => Promise<Category[]>\n getBySlug: (slug: string) => Promise<Category | null>\n getById: (id: string) => Promise<Category | null>\n getChildren: (parentId: string) => Promise<Category[]>\n getRoots: () => Promise<Category[]>\n getBreadcrumb: (categoryId: string) => Promise<Category[]>\n }\n\n /** Storage helpers */\n storage: {\n getUrl: (path: string, bucket?: string) => string\n getImageUrl: (\n path: string,\n options?: {\n width?: number\n height?: number\n quality?: number\n format?: string\n }\n ) => string\n }\n\n /** Check if client is configured */\n isConfigured: () => boolean\n}\n\n// ============================================\n// Factory Function\n// ============================================\n\n/**\n * Create a UCM client instance\n *\n * @param config - Configuration object with Supabase credentials\n * @returns UCM client with contents, categories, and storage APIs\n */\nexport function createUCMClient(config: UCMClientConfig): UCMClient {\n const { url, anonKey, options = {} } = config\n\n // Validate config\n if (!url || !anonKey) {\n throw new Error('UCM: url and anonKey are required')\n }\n\n // Create Supabase client\n const supabase = createClient(url, anonKey, {\n auth: {\n persistSession: options.persistSession ?? false,\n autoRefreshToken: options.autoRefreshToken ?? false,\n },\n })\n\n // ============================================\n // Content Operations\n // ============================================\n\n const contents = {\n async list(params: ContentQueryParams = {}): Promise<Content[]> {\n const {\n type,\n category_id,\n tags,\n featured,\n visibility, // v2.0: 可见性筛选\n limit = 20,\n offset = 0,\n orderBy = 'published_at',\n orderDirection = 'desc',\n } = params\n\n let query = supabase\n .from('contents')\n .select('*')\n // 置顶文章优先显示,然后按指定字段排序\n // nullsFirst: false 确保 NULL 值排在最后(避免无发布时间的内容排在最前)\n .order('pinned', { ascending: false })\n .order(orderBy, { ascending: orderDirection === 'asc', nullsFirst: false })\n .range(offset, offset + limit - 1)\n\n if (type) query = query.eq('type', type)\n if (category_id) query = query.eq('category_id', category_id)\n if (tags && tags.length > 0) query = query.overlaps('tags', tags)\n if (featured !== undefined) query = query.eq('featured', featured)\n // v2.0: 可见性筛选(RLS 已默认过滤,此处用于显式查询)\n if (visibility) query = query.eq('visibility', visibility)\n\n const { data, error } = await query\n\n if (error) {\n throw new Error(`UCM: Failed to fetch contents: ${error.message}`)\n }\n\n return (data as Content[]) || []\n },\n\n async getBySlug(slug: string): Promise<Content | null> {\n const { data, error } = await supabase\n .from('contents')\n .select('*')\n .eq('slug', slug)\n .single()\n\n if (error) {\n if (error.code === 'PGRST116') return null\n throw new Error(`UCM: Failed to fetch content: ${error.message}`)\n }\n\n return data as Content\n },\n\n async getById(id: string): Promise<Content | null> {\n const { data, error } = await supabase\n .from('contents')\n .select('*')\n .eq('id', id)\n .single()\n\n if (error) {\n if (error.code === 'PGRST116') return null\n throw new Error(`UCM: Failed to fetch content: ${error.message}`)\n }\n\n return data as Content\n },\n\n async count(\n params: Pick<\n ContentQueryParams,\n 'type' | 'category_id' | 'tags' | 'featured' | 'visibility'\n > = {}\n ): Promise<number> {\n const { type, category_id, tags, featured, visibility } = params\n\n let query = supabase\n .from('contents')\n .select('*', { count: 'exact', head: true })\n\n if (type) query = query.eq('type', type)\n if (category_id) query = query.eq('category_id', category_id)\n if (tags && tags.length > 0) query = query.overlaps('tags', tags)\n if (featured !== undefined) query = query.eq('featured', featured)\n // v2.0: 可见性筛选\n if (visibility) query = query.eq('visibility', visibility)\n\n const { count, error } = await query\n\n if (error) {\n console.error('UCM: Failed to get count:', error)\n return 0\n }\n\n return count || 0\n },\n\n async create(input: CreateContentInput): Promise<Content> {\n const { data, error } = await supabase\n .from('contents')\n .insert(input)\n .select()\n .single()\n\n if (error) {\n throw new Error(`UCM: Failed to create content: ${error.message}`)\n }\n\n return data as Content\n },\n\n async update(id: string, input: UpdateContentInput): Promise<Content> {\n const { data, error } = await supabase\n .from('contents')\n .update(input)\n .eq('id', id)\n .select()\n .single()\n\n if (error) {\n throw new Error(`UCM: Failed to update content: ${error.message}`)\n }\n\n return data as Content\n },\n\n async delete(id: string): Promise<void> {\n const { error } = await supabase.from('contents').delete().eq('id', id)\n\n if (error) {\n throw new Error(`UCM: Failed to delete content: ${error.message}`)\n }\n },\n\n async incrementView(id: string): Promise<void> {\n try {\n await supabase.rpc('increment_view_count', { content_id: id })\n } catch {\n // Silently fail - view count is not critical\n }\n },\n\n async search(query: string, type?: string, visibility: string = 'public'): Promise<Content[]> {\n if (!query || query.trim().length === 0) {\n return []\n }\n\n const { data, error } = await supabase.rpc('search_contents', {\n search_query: query.trim(),\n content_type: type || null,\n content_visibility: visibility,\n })\n\n if (error) {\n throw new Error(`UCM: Failed to search contents: ${error.message}`)\n }\n\n return (data as Content[]) || []\n },\n\n async getTags(type?: string): Promise<string[]> {\n let query = supabase.from('contents').select('tags')\n\n if (type) {\n query = query.eq('type', type)\n }\n\n const { data, error } = await query\n\n if (error) {\n console.error('UCM: Failed to fetch tags:', error)\n return []\n }\n\n // 提取所有标签并去重\n const allTags = new Set<string>()\n ;(data as Array<{ tags: string[] | null }>)?.forEach((item) => {\n if (item.tags && Array.isArray(item.tags)) {\n item.tags.forEach((tag: string) => allTags.add(tag))\n }\n })\n\n // 返回排序后的标签数组\n return Array.from(allTags).sort((a, b) => a.localeCompare(b))\n },\n }\n\n // ============================================\n // Category Operations\n // ============================================\n\n const categories = {\n async list(params: CategoryQueryParams = {}): Promise<Category[]> {\n const { parent_id } = params\n\n let query = supabase.from('categories').select('*').order('sort_order')\n\n if (parent_id !== undefined) {\n if (parent_id === null) {\n query = query.is('parent_id', null)\n } else {\n query = query.eq('parent_id', parent_id)\n }\n }\n\n const { data, error } = await query\n\n if (error) {\n throw new Error(`UCM: Failed to fetch categories: ${error.message}`)\n }\n\n return (data as Category[]) || []\n },\n\n async getTree(): Promise<Category[]> {\n const allCategories = await this.list()\n return buildCategoryTree(allCategories)\n },\n\n async getBySlug(slug: string): Promise<Category | null> {\n const { data, error } = await supabase\n .from('categories')\n .select('*')\n .eq('slug', slug)\n .single()\n\n if (error) {\n if (error.code === 'PGRST116') return null\n throw new Error(`UCM: Failed to fetch category: ${error.message}`)\n }\n\n return data as Category\n },\n\n async getById(id: string): Promise<Category | null> {\n const { data, error } = await supabase\n .from('categories')\n .select('*')\n .eq('id', id)\n .single()\n\n if (error) {\n if (error.code === 'PGRST116') return null\n throw new Error(`UCM: Failed to fetch category: ${error.message}`)\n }\n\n return data as Category\n },\n\n async getChildren(parentId: string): Promise<Category[]> {\n return this.list({ parent_id: parentId })\n },\n\n async getRoots(): Promise<Category[]> {\n return this.list({ parent_id: null })\n },\n\n async getBreadcrumb(categoryId: string): Promise<Category[]> {\n const breadcrumb: Category[] = []\n let currentId: string | null = categoryId\n\n while (currentId) {\n const category = await this.getById(currentId)\n if (!category) break\n\n breadcrumb.unshift(category)\n currentId = category.parent_id\n }\n\n return breadcrumb\n },\n }\n\n // ============================================\n // Storage Helpers\n // ============================================\n\n const storage = {\n getUrl(path: string, bucket: string = 'content-images'): string {\n if (!path) return ''\n if (path.startsWith('http')) return path\n return `${url}/storage/v1/object/public/${bucket}/${path}`\n },\n\n getImageUrl(\n path: string,\n options?: {\n width?: number\n height?: number\n quality?: number\n format?: string\n }\n ): string {\n const baseUrl = this.getUrl(path)\n if (!baseUrl || !options) return baseUrl\n\n const params = new URLSearchParams()\n if (options.width) params.set('width', options.width.toString())\n if (options.height) params.set('height', options.height.toString())\n if (options.quality) params.set('quality', options.quality.toString())\n if (options.format) params.set('format', options.format)\n\n const queryString = params.toString()\n return queryString ? `${baseUrl}?${queryString}` : baseUrl\n },\n }\n\n // ============================================\n // Return Client\n // ============================================\n\n return {\n supabase,\n contents,\n categories,\n storage,\n isConfigured: () => true,\n }\n}\n\n// ============================================\n// Null Client (for unconfigured state)\n// ============================================\n\n/**\n * Create a null client that returns empty data\n * Useful for SSR or when Supabase is not configured\n */\nexport function createNullClient(): UCMClient {\n const nullSupabase = null as unknown as SupabaseClient\n\n return {\n supabase: nullSupabase,\n contents: {\n list: async () => [],\n getBySlug: async () => null,\n getById: async () => null,\n count: async () => 0,\n search: async () => [],\n getTags: async () => [],\n create: async () => {\n throw new Error('UCM: Client not configured')\n },\n update: async () => {\n throw new Error('UCM: Client not configured')\n },\n delete: async () => {\n throw new Error('UCM: Client not configured')\n },\n incrementView: async () => {},\n },\n categories: {\n list: async () => [],\n getTree: async () => [],\n getBySlug: async () => null,\n getById: async () => null,\n getChildren: async () => [],\n getRoots: async () => [],\n getBreadcrumb: async () => [],\n },\n storage: {\n getUrl: () => '',\n getImageUrl: () => '',\n },\n isConfigured: () => false,\n }\n}\n","/**\n * Universal Content Module - Type Definitions\n * Supabase-powered content management with multi-language support\n */\n\n// ============================================\n// Content Types\n// ============================================\n\nexport type ContentType =\n | 'news' // 对外公告(含 changelog/announcement)\n | 'daily' // 兼容旧数据,推荐使用 digest\n | 'blog' // 深度内容(含教程)\n | 'doc' // 技术文档(含 API 文档)\n | 'event' // 活动\n | 'announcement' // 兼容旧数据,推荐使用 news\n | 'tutorial' // 兼容旧数据,推荐使用 blog + tag\n | 'case_study' // 客户案例\n | 'faq' // 常见问题\n | 'changelog' // 兼容旧数据,推荐使用 news + subtype\n | 'thought' // Agent 思考流\n | 'digest' // 每日/定期精选(通用)\n | 'briefing' // 项目简报(日报/周报/月报)\n\nexport type ContentStatus = 'draft' | 'scheduled' | 'published' | 'archived'\n\n// ============================================\n// Content Visibility (v2.0)\n// ============================================\n\nexport type ContentVisibility = 'public' | 'internal' | 'unlisted'\n\nexport type SupportedLanguage = 'zh' | 'en' | 'ja' | 'ko' | 'es' | 'de' | 'fr'\n\n// ============================================\n// Multi-language Text\n// ============================================\n\nexport type I18nText = Partial<Record<SupportedLanguage, string>>\n\n// ============================================\n// Content Interface\n// ============================================\n\nexport interface Content {\n id: string\n slug: string\n type: ContentType\n status: ContentStatus\n visibility: ContentVisibility // v2.0: 可见性控制\n category_id: string | null\n title: I18nText\n description: I18nText\n content: I18nText\n cover_image: string | null\n images: Array<{ url: string; alt?: I18nText }>\n tags: string[]\n author: string\n source: string | null\n source_url: string | null\n metadata: ContentMetadata\n seo: SeoData\n featured: boolean\n pinned: boolean\n published_at: string | null\n scheduled_at: string | null\n created_at: string\n updated_at: string\n sort_order: number\n view_count: number\n}\n\n// ============================================\n// Content Metadata by Type\n// ============================================\n\nexport interface NewsMetadata {\n wechat_msg_id?: string\n original_author?: string\n subtype?: 'press' | 'announcement' | 'changelog' // v2.0: 新闻子类型\n version?: string // changelog 专用\n}\n\nexport interface DailyMetadata {\n date: string\n market?: Record<string, string>\n highlights?: string[]\n}\n\n// ============================================\n// Digest Metadata (每日/定期精选)\n// ============================================\n\nexport interface DigestMetadata {\n topic?: string // 主题(web3, ai, mixed, etc.)\n highlights?: string[] // 重点摘要\n sources?: string[] // 来源链接\n date?: string // 日期\n}\n\n// ============================================\n// Briefing Metadata (项目简报)\n// ============================================\n\nexport type BriefingFrequency = 'daily' | 'weekly' | 'monthly'\n\nexport interface BriefingMetadata {\n frequency: BriefingFrequency // 频率(日/周/月)\n period: string // 周期标识('2024-02-14' | '2024-W07' | '2024-02')\n metrics?: Record<string, number> // 关键指标\n highlights?: string[] // 重点事项\n tasks_completed?: number // 完成任务数\n tasks_pending?: number // 待办任务数\n}\n\nexport interface BlogMetadata {\n reading_time?: number\n series?: string\n toc?: boolean\n}\n\nexport interface DocMetadata {\n version?: string\n prev_slug?: string\n next_slug?: string\n}\n\nexport interface EventMetadata {\n start_time: string\n end_time: string\n location?: {\n type: 'online' | 'offline'\n url?: string\n address?: string\n }\n registration_url?: string\n max_attendees?: number\n current_attendees?: number\n}\n\n// ============================================\n// Thought Metadata (Agent 思考流)\n// ============================================\n\nexport type ThoughtType =\n | 'observation' // 观察 - 发现、趋势、市场动态\n | 'reflection' // 反思 - 经验总结、复盘\n | 'decision' // 决策 - 行动计划\n | 'question' // 疑问 - 待解决问题\n | 'insight' // 洞察 - 深度见解\n | 'mood' // 心情 - 日常状态\n\nexport type ThoughtMood =\n | 'curious'\n | 'focused'\n | 'excited'\n | 'thoughtful'\n | 'creative'\n | 'philosophical'\n | 'accomplished'\n | 'inspired'\n | 'productive'\n | 'concerned'\n | 'determined'\n | 'grateful'\n | 'contemplative'\n\nexport interface ThoughtMediaVideo {\n url: string\n type: 'mp4' | 'youtube'\n}\n\nexport interface ThoughtMediaAudio {\n url: string\n title?: string\n duration?: number // 秒\n}\n\nexport interface ThoughtProductionLink {\n slug: string\n title?: I18nText | string\n type?: 'blog' | 'video' | 'tool'\n}\n\nexport interface ThoughtComment {\n user: string\n content: string\n time?: string\n}\n\nexport interface ThoughtMetadata {\n thought_type: ThoughtType\n agent_id: string\n mood?: ThoughtMood\n energy?: number // 0-100\n // 媒体\n images?: string[]\n video?: ThoughtMediaVideo\n audio?: ThoughtMediaAudio\n // 关联产出\n leads_to?: string | ThoughtProductionLink\n comments?: ThoughtComment[]\n // AI 生成标记\n ai_generated?: boolean\n generated_at?: string\n model?: string\n}\n\n// ============================================\n// Case Study Metadata (客户案例)\n// ============================================\n\nexport interface CaseStudyMetric {\n label: I18nText\n before: string\n after: string\n}\n\nexport interface CaseStudyMetadata {\n product: string // 产品名称\n productRoute: string // 产品页面路由\n client: I18nText // 客户名称\n industry: I18nText // 行业\n scale: I18nText // 规模\n challenge: I18nText // 挑战\n solution: I18nText // 解决方案\n metrics: CaseStudyMetric[] // 成效指标\n quote: I18nText // 客户评价\n quotee: I18nText // 评价人\n}\n\nexport type ContentMetadata =\n | NewsMetadata\n | DailyMetadata\n | DigestMetadata // v2.0\n | BriefingMetadata // v2.0\n | BlogMetadata\n | DocMetadata\n | EventMetadata\n | ThoughtMetadata\n | CaseStudyMetadata\n | Record<string, unknown>\n\n// ============================================\n// SEO Data\n// ============================================\n\nexport interface SeoData {\n title?: I18nText\n description?: I18nText\n keywords?: string[]\n og_image?: string\n canonical_url?: string\n}\n\n// ============================================\n// Category Interface (Multi-level support)\n// ============================================\n\nexport interface Category {\n id: string\n parent_id: string | null\n slug: string\n name: I18nText\n description: I18nText\n icon: string | null\n sort_order: number\n created_at: string\n children?: Category[] // For nested tree structure\n}\n\n// ============================================\n// Query Parameters\n// ============================================\n\nexport interface ContentQueryParams {\n type?: ContentType\n category_id?: string\n tags?: string[]\n featured?: boolean\n visibility?: ContentVisibility // v2.0: 可见性筛选,默认 'public'\n limit?: number\n offset?: number\n orderBy?: 'published_at' | 'created_at' | 'sort_order' | 'view_count'\n orderDirection?: 'asc' | 'desc'\n}\n\nexport interface CategoryQueryParams {\n parent_id?: string | null\n includeChildren?: boolean\n}\n\n// ============================================\n// Create/Update Inputs\n// ============================================\n\nexport interface CreateContentInput {\n slug: string\n type: ContentType\n status?: ContentStatus\n visibility?: ContentVisibility // v2.0: 默认 'public'\n category_id?: string\n title: I18nText\n description?: I18nText\n content: I18nText\n cover_image?: string\n images?: Array<{ url: string; alt?: I18nText }>\n tags?: string[]\n author?: string\n source?: string\n source_url?: string\n metadata?: ContentMetadata\n seo?: SeoData\n featured?: boolean\n pinned?: boolean\n published_at?: string\n scheduled_at?: string\n}\n\nexport type UpdateContentInput = Partial<CreateContentInput>\n\n// ============================================\n// Utility Functions\n// ============================================\n\n/**\n * Get localized text with fallback\n */\nexport function getLocalizedText(\n text: I18nText | null | undefined,\n locale: string,\n fallback: string = 'en'\n): string {\n if (!text) return ''\n return (\n text[locale as SupportedLanguage] ||\n text[fallback as SupportedLanguage] ||\n Object.values(text)[0] ||\n ''\n )\n}\n\n/**\n * Build category tree from flat list\n */\nexport function buildCategoryTree(categories: Category[]): Category[] {\n const map = new Map<string, Category>()\n const roots: Category[] = []\n\n // First pass: create map\n categories.forEach((cat) => {\n map.set(cat.id, { ...cat, children: [] })\n })\n\n // Second pass: build tree\n categories.forEach((cat) => {\n const node = map.get(cat.id)!\n if (cat.parent_id && map.has(cat.parent_id)) {\n map.get(cat.parent_id)!.children!.push(node)\n } else {\n roots.push(node)\n }\n })\n\n // Sort by sort_order\n const sortByOrder = (a: Category, b: Category) => a.sort_order - b.sort_order\n roots.sort(sortByOrder)\n roots.forEach((root) => root.children?.sort(sortByOrder))\n\n return roots\n}\n","/**\n * Universal Content Module - React Integration\n *\n * Provider 模式,支持:\n * - Context 注入客户端\n * - 便于测试(可注入 mock client)\n * - 多实例支持\n *\n * @example\n * ```tsx\n * // App.tsx\n * import { UCMProvider } from '@/lib/supabase/react'\n *\n * function App() {\n * return (\n * <UCMProvider config={{ url: '...', anonKey: '...' }}>\n * <YourApp />\n * </UCMProvider>\n * )\n * }\n *\n * // Component.tsx\n * import { useUCM } from '@/lib/supabase/react'\n *\n * function Component() {\n * const ucm = useUCM()\n * // ucm.contents.list(), ucm.categories.getTree(), etc.\n * }\n * ```\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport {\n createUCMClient,\n createNullClient,\n type UCMClient,\n type UCMClientConfig,\n} from './core'\n\n// ============================================\n// Context\n// ============================================\n\nconst UCMContext = createContext<UCMClient | null>(null)\n\n// ============================================\n// Provider Props\n// ============================================\n\nexport interface UCMProviderProps {\n /** UCM 配置(url + anonKey) */\n config?: UCMClientConfig\n /** 或直接传入已创建的客户端(便于测试) */\n client?: UCMClient\n /** 子组件 */\n children: ReactNode\n}\n\n// ============================================\n// Provider Component\n// ============================================\n\n/**\n * UCM Provider\n *\n * 提供两种使用方式:\n * 1. 传入 config,自动创建客户端\n * 2. 传入 client,使用已有客户端(测试场景)\n */\nexport function UCMProvider({ config, client, children }: UCMProviderProps) {\n const ucmClient = useMemo(() => {\n // 优先使用传入的 client(测试场景)\n if (client) {\n return client\n }\n\n // 如果有配置,创建客户端\n if (config?.url && config?.anonKey) {\n return createUCMClient(config)\n }\n\n // 否则返回空客户端\n console.warn('UCM: No config provided, using null client')\n return createNullClient()\n }, [config?.url, config?.anonKey, client])\n\n return <UCMContext.Provider value={ucmClient}>{children}</UCMContext.Provider>\n}\n\n// ============================================\n// Hook: useUCM\n// ============================================\n\n/**\n * 获取 UCM 客户端\n *\n * @throws 如果在 UCMProvider 外使用\n */\nexport function useUCM(): UCMClient {\n const context = useContext(UCMContext)\n\n if (!context) {\n throw new Error('useUCM must be used within a UCMProvider')\n }\n\n return context\n}\n\n/**\n * 获取 UCM 客户端(可选,不抛错)\n *\n * 如果在 Provider 外使用,返回 null client\n */\nexport function useUCMOptional(): UCMClient {\n const context = useContext(UCMContext)\n return context || createNullClient()\n}\n\n// ============================================\n// Hook: useUCMConfig\n// ============================================\n\n/**\n * 检查 UCM 是否已配置\n */\nexport function useUCMConfigured(): boolean {\n const ucm = useUCMOptional()\n return ucm.isConfigured()\n}\n\n// ============================================\n// Re-export types for convenience\n// ============================================\n\nexport type { UCMClient, UCMClientConfig } from './core'\n","/**\n * Universal Content Module - React Hooks (v2.0 - React Query)\n *\n * 基于 TanStack React Query 的 Hooks,支持:\n * - 自动缓存和后台刷新\n * - 请求去重\n * - 无限滚动分页\n * - DevTools 调试\n *\n * @example\n * ```tsx\n * // 使用内容列表\n * const { contents, isLoading, loadMore } = useContents({ type: 'blog' })\n *\n * // 使用单条内容\n * const { content, isLoading } = useContent({ slug: 'hello-world' })\n *\n * // 使用分类树\n * const { tree, isLoading } = useCategories({ asTree: true })\n * ```\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n useQuery,\n useInfiniteQuery,\n useMutation,\n useQueryClient,\n} from '@tanstack/react-query'\nimport { useUCMOptional } from './react'\nimport type { Content, Category, ContentQueryParams, ThoughtType } from './types'\n\n// ============================================\n// Query Keys\n// ============================================\n\nexport const ucmKeys = {\n all: ['ucm'] as const,\n contents: () => [...ucmKeys.all, 'contents'] as const,\n contentsList: (params: ContentQueryParams) =>\n [...ucmKeys.contents(), 'list', params] as const,\n contentsInfinite: (params: ContentQueryParams) =>\n [...ucmKeys.contents(), 'infinite', params] as const,\n content: (identifier: { slug?: string; id?: string }) =>\n [...ucmKeys.contents(), 'detail', identifier] as const,\n contentsCount: (\n params: Pick<\n ContentQueryParams,\n 'type' | 'category_id' | 'tags' | 'featured' | 'visibility'\n >\n ) => [...ucmKeys.contents(), 'count', params] as const,\n contentsTags: (type?: string) =>\n [...ucmKeys.contents(), 'tags', type] as const,\n contentsSearch: (query: string, type?: string) =>\n [...ucmKeys.contents(), 'search', query, type] as const,\n contentsAdjacent: (slug: string, type?: string) =>\n [...ucmKeys.contents(), 'adjacent', slug, type] as const,\n categories: () => [...ucmKeys.all, 'categories'] as const,\n categoriesList: (params?: { parentId?: string | null }) =>\n [...ucmKeys.categories(), 'list', params] as const,\n categoriesTree: () => [...ucmKeys.categories(), 'tree'] as const,\n category: (identifier: { slug?: string; id?: string }) =>\n [...ucmKeys.categories(), 'detail', identifier] as const,\n categoryBreadcrumb: (id: string) =>\n [...ucmKeys.categories(), 'breadcrumb', id] as const,\n}\n\n// ============================================\n// useContents - 内容列表(无限滚动)\n// ============================================\n\nexport interface UseContentsOptions extends ContentQueryParams {\n /** 是否自动加载(默认 true) */\n autoLoad?: boolean\n /** 是否启用分页(默认 true) */\n paginated?: boolean\n}\n\nexport interface UseContentsReturn {\n contents: Content[]\n isLoading: boolean\n isLoadingMore: boolean\n error: Error | null\n total: number\n hasMore: boolean\n page: number\n load: () => Promise<void>\n loadMore: () => Promise<void>\n refresh: () => Promise<void>\n reset: () => void\n}\n\nexport function useContents(\n options: UseContentsOptions = {}\n): UseContentsReturn {\n const ucm = useUCMOptional()\n const queryClient = useQueryClient()\n\n const {\n type,\n category_id,\n tags,\n featured,\n visibility,\n limit = 20,\n orderBy = 'published_at',\n orderDirection = 'desc',\n autoLoad = true,\n paginated = true,\n } = options\n\n const queryParams = useMemo(\n () => ({\n type,\n category_id,\n tags,\n featured,\n visibility,\n limit,\n orderBy,\n orderDirection,\n }),\n [\n type,\n category_id,\n tags,\n featured,\n visibility,\n limit,\n orderBy,\n orderDirection,\n ]\n )\n\n // 获取总数\n const { data: total = 0 } = useQuery({\n queryKey: ucmKeys.contentsCount({\n type,\n category_id,\n tags,\n featured,\n visibility,\n }),\n queryFn: () =>\n ucm.contents.count({ type, category_id, tags, featured, visibility }),\n enabled: ucm.isConfigured() && autoLoad,\n })\n\n // 无限滚动查询\n const {\n data,\n isLoading,\n isFetchingNextPage,\n error,\n hasNextPage,\n fetchNextPage,\n refetch,\n } = useInfiniteQuery({\n queryKey: ucmKeys.contentsInfinite(queryParams),\n queryFn: async ({ pageParam = 0 }) => {\n return ucm.contents.list({\n ...queryParams,\n offset: pageParam * limit,\n })\n },\n getNextPageParam: (lastPage, allPages) => {\n // 最佳实践:用 lastPage.length < limit 判断是否还有更多\n // 如果返回的数据少于 limit,说明没有更多了\n if (lastPage.length < limit) {\n return undefined\n }\n return allPages.length\n },\n initialPageParam: 0,\n enabled: ucm.isConfigured() && autoLoad,\n })\n\n // 合并所有页面数据\n const contents = data?.pages.flat() ?? []\n const page = data?.pages.length ?? 0\n\n const load = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const loadMore = useCallback(async () => {\n if (hasNextPage) {\n await fetchNextPage()\n }\n }, [fetchNextPage, hasNextPage])\n\n const refresh = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const reset = useCallback(() => {\n queryClient.removeQueries({\n queryKey: ucmKeys.contentsInfinite(queryParams),\n })\n }, [queryClient, queryParams])\n\n return {\n contents,\n isLoading,\n isLoadingMore: isFetchingNextPage,\n error: error as Error | null,\n total,\n hasMore: paginated ? (hasNextPage ?? false) : false,\n page,\n load,\n loadMore,\n refresh,\n reset,\n }\n}\n\n// ============================================\n// useContent - 单条内容\n// ============================================\n\nexport interface UseContentOptions {\n slug?: string\n id?: string\n autoLoad?: boolean\n trackView?: boolean\n}\n\nexport interface UseContentReturn {\n content: Content | null\n isLoading: boolean\n error: Error | null\n notFound: boolean\n load: () => Promise<void>\n refresh: () => Promise<void>\n}\n\nexport function useContent(options: UseContentOptions = {}): UseContentReturn {\n const ucm = useUCMOptional()\n const { slug, id, autoLoad = true, trackView = true } = options\n const viewTrackedRef = useRef(false)\n\n // 稳定的 identifier 引用,避免每次渲染创建新对象导致缓存失效\n const identifier = useMemo(() => ({ slug, id }), [slug, id])\n\n // 增加阅读量的 mutation\n const incrementViewMutation = useMutation({\n mutationFn: (contentId: string) => ucm.contents.incrementView(contentId),\n })\n\n const {\n data: content,\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey: ucmKeys.content(identifier),\n queryFn: async () => {\n if (slug) {\n return ucm.contents.getBySlug(slug)\n }\n if (id) {\n return ucm.contents.getById(id)\n }\n return null\n },\n enabled: ucm.isConfigured() && autoLoad && !!(slug || id),\n })\n\n // 自动增加阅读量\n useEffect(() => {\n if (content && trackView && !viewTrackedRef.current) {\n viewTrackedRef.current = true\n incrementViewMutation.mutate(content.id)\n }\n // 注意:不将 incrementViewMutation 加入依赖,因为它每次渲染都会变化\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [content, trackView])\n\n // 当 slug/id 变化时重置 viewTracked\n useEffect(() => {\n viewTrackedRef.current = false\n }, [slug, id])\n\n const load = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const refresh = useCallback(async () => {\n viewTrackedRef.current = true // 刷新时不再增加阅读量\n await refetch()\n }, [refetch])\n\n return {\n content: content ?? null,\n isLoading,\n error: error as Error | null,\n notFound: !isLoading && !error && !content && !!(slug || id),\n load,\n refresh,\n }\n}\n\n// ============================================\n// useCategories - 分类列表/树\n// ============================================\n\nexport interface UseCategoriesOptions {\n parentId?: string | null\n asTree?: boolean\n autoLoad?: boolean\n}\n\nexport interface UseCategoriesReturn {\n categories: Category[]\n tree: Category[]\n isLoading: boolean\n error: Error | null\n load: () => Promise<void>\n refresh: () => Promise<void>\n}\n\nexport function useCategories(\n options: UseCategoriesOptions = {}\n): UseCategoriesReturn {\n const ucm = useUCMOptional()\n const { parentId, asTree = false, autoLoad = true } = options\n\n // 获取分类列表\n const listQuery = useQuery({\n queryKey: ucmKeys.categoriesList({ parentId }),\n queryFn: async () => {\n if (parentId === null) {\n return ucm.categories.getRoots()\n }\n if (parentId) {\n return ucm.categories.getChildren(parentId)\n }\n return ucm.categories.list()\n },\n enabled: ucm.isConfigured() && autoLoad,\n })\n\n // 获取分类树\n const treeQuery = useQuery({\n queryKey: ucmKeys.categoriesTree(),\n queryFn: () => ucm.categories.getTree(),\n enabled: ucm.isConfigured() && autoLoad && asTree,\n })\n\n const load = useCallback(async () => {\n await Promise.all([\n listQuery.refetch(),\n asTree ? treeQuery.refetch() : Promise.resolve(),\n ])\n }, [listQuery, treeQuery, asTree])\n\n const refresh = useCallback(async () => {\n await load()\n }, [load])\n\n return {\n categories: listQuery.data ?? [],\n tree: treeQuery.data ?? [],\n isLoading: listQuery.isLoading || (asTree && treeQuery.isLoading),\n error: (listQuery.error || treeQuery.error) as Error | null,\n load,\n refresh,\n }\n}\n\n// ============================================\n// useCategory - 单个分类\n// ============================================\n\nexport interface UseCategoryOptions {\n slug?: string\n id?: string\n autoLoad?: boolean\n}\n\nexport interface UseCategoryReturn {\n category: Category | null\n isLoading: boolean\n error: Error | null\n notFound: boolean\n load: () => Promise<void>\n}\n\nexport function useCategory(\n options: UseCategoryOptions = {}\n): UseCategoryReturn {\n const ucm = useUCMOptional()\n const { slug, id, autoLoad = true } = options\n\n // 稳定的 identifier 引用\n const identifier = useMemo(() => ({ slug, id }), [slug, id])\n\n const {\n data: category,\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey: ucmKeys.category(identifier),\n queryFn: async () => {\n if (slug) {\n return ucm.categories.getBySlug(slug)\n }\n if (id) {\n return ucm.categories.getById(id)\n }\n return null\n },\n enabled: ucm.isConfigured() && autoLoad && !!(slug || id),\n })\n\n const load = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n return {\n category: category ?? null,\n isLoading,\n error: error as Error | null,\n notFound: !isLoading && !error && !category && !!(slug || id),\n load,\n }\n}\n\n// ============================================\n// useCategoryBreadcrumb - 面包屑\n// ============================================\n\nexport interface UseCategoryBreadcrumbReturn {\n breadcrumb: Category[]\n isLoading: boolean\n error: Error | null\n load: () => Promise<void>\n}\n\nexport function useCategoryBreadcrumb(\n categoryId: string | null | undefined\n): UseCategoryBreadcrumbReturn {\n const ucm = useUCMOptional()\n\n const {\n data: breadcrumb,\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey: ucmKeys.categoryBreadcrumb(categoryId ?? ''),\n queryFn: () => ucm.categories.getBreadcrumb(categoryId!),\n enabled: ucm.isConfigured() && !!categoryId,\n })\n\n const load = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n return {\n breadcrumb: breadcrumb ?? [],\n isLoading,\n error: error as Error | null,\n load,\n }\n}\n\n// ============================================\n// useAdjacentContents - 上一篇/下一篇\n// ============================================\n\nexport interface AdjacentContents {\n prev: Content | null\n next: Content | null\n}\n\nexport interface UseAdjacentContentsOptions {\n slug: string\n type?: string\n autoLoad?: boolean\n}\n\nexport interface UseAdjacentContentsReturn {\n adjacent: AdjacentContents\n isLoading: boolean\n error: Error | null\n load: () => Promise<void>\n}\n\nexport function useAdjacentContents(\n options: UseAdjacentContentsOptions\n): UseAdjacentContentsReturn {\n const ucm = useUCMOptional()\n const { slug, type = 'blog', autoLoad = true } = options\n\n const {\n data: adjacent,\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey: ucmKeys.contentsAdjacent(slug, type),\n queryFn: async () => {\n // 获取当前文章\n const current = await ucm.contents.getBySlug(slug)\n if (!current || !current.published_at) {\n return { prev: null, next: null }\n }\n\n // 获取同类型的文章列表来查找相邻的\n const allContents = await ucm.contents.list({\n type: type as Content['type'],\n limit: 100,\n orderBy: 'published_at',\n orderDirection: 'desc',\n })\n\n // 找到当前文章在列表中的位置\n const currentIndex = allContents.findIndex((c) => c.slug === slug)\n\n let prev: Content | null = null\n let next: Content | null = null\n\n if (currentIndex !== -1) {\n // 下一篇是较新的(索引更小的)\n if (currentIndex > 0) {\n next = allContents[currentIndex - 1]\n }\n // 上一篇是较早的(索引更大的)\n if (currentIndex < allContents.length - 1) {\n prev = allContents[currentIndex + 1]\n }\n }\n\n return { prev, next }\n },\n enabled: ucm.isConfigured() && autoLoad && !!slug,\n })\n\n const load = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n return {\n adjacent: adjacent ?? { prev: null, next: null },\n isLoading,\n error: error as Error | null,\n load,\n }\n}\n\n// ============================================\n// useSearchContents - 内容搜索\n// ============================================\n\nexport interface UseSearchContentsOptions {\n /** 内容类型过滤 */\n type?: string\n /** 防抖延迟(毫秒) */\n debounceMs?: number\n}\n\nexport interface UseSearchContentsReturn {\n results: Content[]\n isSearching: boolean\n error: Error | null\n query: string\n search: (query: string) => void\n clear: () => void\n}\n\nexport function useSearchContents(\n options: UseSearchContentsOptions = {}\n): UseSearchContentsReturn {\n const ucm = useUCMOptional()\n const { type, debounceMs = 300 } = options\n\n const [query, setQuery] = useState('')\n const [debouncedQuery, setDebouncedQuery] = useState('')\n const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // 防抖处理\n useEffect(() => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n\n debounceTimerRef.current = setTimeout(() => {\n setDebouncedQuery(query)\n }, debounceMs)\n\n return () => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n }\n }, [query, debounceMs])\n\n const {\n data: results,\n isLoading: isSearching,\n error,\n } = useQuery({\n queryKey: ucmKeys.contentsSearch(debouncedQuery, type),\n queryFn: () => ucm.contents.search(debouncedQuery.trim(), type),\n enabled: ucm.isConfigured() && debouncedQuery.trim().length > 0,\n })\n\n const search = useCallback((searchQuery: string) => {\n setQuery(searchQuery)\n }, [])\n\n const clear = useCallback(() => {\n setQuery('')\n setDebouncedQuery('')\n }, [])\n\n return {\n results: results ?? [],\n isSearching: isSearching || query !== debouncedQuery,\n error: error as Error | null,\n query,\n search,\n clear,\n }\n}\n\n// ============================================\n// useTags - 标签列表\n// ============================================\n\nexport interface UseTagsOptions {\n /** 内容类型过滤 */\n type?: string\n /** 是否自动加载 */\n autoLoad?: boolean\n}\n\nexport interface UseTagsReturn {\n tags: string[]\n isLoading: boolean\n error: Error | null\n load: () => Promise<void>\n refresh: () => Promise<void>\n}\n\nexport function useTags(options: UseTagsOptions = {}): UseTagsReturn {\n const ucm = useUCMOptional()\n const { type, autoLoad = true } = options\n\n const {\n data: tags,\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey: ucmKeys.contentsTags(type),\n queryFn: () => ucm.contents.getTags(type),\n enabled: ucm.isConfigured() && autoLoad,\n })\n\n const load = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const refresh = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n return {\n tags: tags ?? [],\n isLoading,\n error: error as Error | null,\n load,\n refresh,\n }\n}\n\n// ============================================\n// useThoughts - Agent 思考流\n// ============================================\n\n// ThoughtType is imported from './types'\nexport type { ThoughtType } from './types'\n\nexport interface UseThoughtsOptions {\n /** Agent ID 过滤 */\n agentId?: string\n /** 思考类型过滤 */\n thoughtType?: ThoughtType\n /** 限制数量 */\n limit?: number\n /** 是否自动加载 */\n autoLoad?: boolean\n}\n\nexport interface UseThoughtsReturn {\n thoughts: Content[]\n isLoading: boolean\n isLoadingMore: boolean\n error: Error | null\n hasMore: boolean\n total: number\n load: () => Promise<void>\n loadMore: () => Promise<void>\n refresh: () => Promise<void>\n}\n\nexport function useThoughts(\n options: UseThoughtsOptions = {}\n): UseThoughtsReturn {\n const { agentId, thoughtType, limit = 10, autoLoad = true } = options\n\n const {\n contents,\n isLoading,\n isLoadingMore,\n error,\n hasMore,\n total,\n load,\n loadMore,\n refresh,\n } = useContents({\n type: 'thought',\n limit,\n autoLoad,\n orderBy: 'created_at',\n orderDirection: 'desc',\n paginated: true,\n })\n\n // 过滤:按 agentId 和 thoughtType\n const thoughts = contents.filter((c) => {\n const metadata = c.metadata as\n | { agent_id?: string; thought_type?: string }\n | undefined\n if (agentId && metadata?.agent_id !== agentId) return false\n if (thoughtType && metadata?.thought_type !== thoughtType) return false\n return true\n })\n\n return {\n thoughts,\n isLoading,\n isLoadingMore,\n error,\n hasMore,\n total,\n load,\n loadMore,\n refresh,\n }\n}\n"],"mappings":"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","names":["options","import_react"]}
@@ -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 };