@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/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# @nby.ai/ucm
|
|
2
|
+
|
|
3
|
+
> Universal Content Module - Supabase-based multilingual CMS
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@nby.ai/ucm)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Multi-language content** - JSONB storage, unlimited languages (zh, en, ja, ko, etc.)
|
|
11
|
+
- **Multiple content types** - news, blog, doc, event, thought, digest, briefing, case_study, faq
|
|
12
|
+
- **Hierarchical categories** - Multi-level category tree support
|
|
13
|
+
- **Framework-agnostic core** - Works with Node.js, scripts, any framework
|
|
14
|
+
- **React integration** - Provider + Hooks with TanStack React Query
|
|
15
|
+
- **Full TypeScript support** - Complete type definitions
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Core package
|
|
21
|
+
pnpm add @nby.ai/ucm @supabase/supabase-js
|
|
22
|
+
|
|
23
|
+
# If using React hooks
|
|
24
|
+
pnpm add @tanstack/react-query react
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### Core API (Node.js / Scripts)
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { createUCMClient } from '@nby.ai/ucm'
|
|
33
|
+
|
|
34
|
+
const ucm = createUCMClient({
|
|
35
|
+
url: process.env.SUPABASE_URL!,
|
|
36
|
+
anonKey: process.env.SUPABASE_ANON_KEY!,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// List blog posts
|
|
40
|
+
const posts = await ucm.contents.list({ type: 'blog', limit: 10 })
|
|
41
|
+
|
|
42
|
+
// Get single content by slug
|
|
43
|
+
const post = await ucm.contents.getBySlug('hello-world')
|
|
44
|
+
|
|
45
|
+
// Full-text search
|
|
46
|
+
const results = await ucm.contents.search('AI Agent')
|
|
47
|
+
|
|
48
|
+
// Get category tree
|
|
49
|
+
const categories = await ucm.categories.getTree()
|
|
50
|
+
|
|
51
|
+
// Create content (requires service key)
|
|
52
|
+
await ucm.contents.create({
|
|
53
|
+
slug: 'new-post',
|
|
54
|
+
type: 'blog',
|
|
55
|
+
title: { zh: '新文章', en: 'New Post' },
|
|
56
|
+
content: { zh: '正文...', en: 'Body...' },
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### React Integration
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
64
|
+
import { UCMProvider, useContents, useContent, getLocalizedText } from '@nby.ai/ucm'
|
|
65
|
+
|
|
66
|
+
const queryClient = new QueryClient()
|
|
67
|
+
|
|
68
|
+
function App() {
|
|
69
|
+
return (
|
|
70
|
+
<QueryClientProvider client={queryClient}>
|
|
71
|
+
<UCMProvider
|
|
72
|
+
config={{
|
|
73
|
+
url: import.meta.env.VITE_SUPABASE_URL,
|
|
74
|
+
anonKey: import.meta.env.VITE_SUPABASE_ANON_KEY,
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
<BlogList />
|
|
78
|
+
</UCMProvider>
|
|
79
|
+
</QueryClientProvider>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function BlogList() {
|
|
84
|
+
const { contents, isLoading, error, loadMore, hasMore } = useContents({
|
|
85
|
+
type: 'blog',
|
|
86
|
+
limit: 10,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
if (isLoading) return <div>Loading...</div>
|
|
90
|
+
if (error) return <div>Error: {error.message}</div>
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<ul>
|
|
95
|
+
{contents.map(post => (
|
|
96
|
+
<li key={post.id}>
|
|
97
|
+
{getLocalizedText(post.title, 'zh')}
|
|
98
|
+
</li>
|
|
99
|
+
))}
|
|
100
|
+
</ul>
|
|
101
|
+
{hasMore && <button onClick={loadMore}>Load More</button>}
|
|
102
|
+
</>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## API Reference
|
|
108
|
+
|
|
109
|
+
### createUCMClient(config)
|
|
110
|
+
|
|
111
|
+
Creates a UCM client instance.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
interface UCMClientConfig {
|
|
115
|
+
url: string // Supabase project URL
|
|
116
|
+
anonKey: string // Supabase anon key (or service key for admin ops)
|
|
117
|
+
options?: {
|
|
118
|
+
persistSession?: boolean // Default: false
|
|
119
|
+
autoRefreshToken?: boolean // Default: false
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### UCMClient Methods
|
|
125
|
+
|
|
126
|
+
#### contents
|
|
127
|
+
|
|
128
|
+
| Method | Description |
|
|
129
|
+
|--------|-------------|
|
|
130
|
+
| `list(params?)` | List contents with filtering and pagination |
|
|
131
|
+
| `getBySlug(slug)` | Get content by slug |
|
|
132
|
+
| `getById(id)` | Get content by ID |
|
|
133
|
+
| `count(params?)` | Count contents matching params |
|
|
134
|
+
| `search(query, type?)` | Full-text search |
|
|
135
|
+
| `getTags(type?)` | Get all unique tags |
|
|
136
|
+
| `create(input)` | Create content (requires service key) |
|
|
137
|
+
| `update(id, input)` | Update content |
|
|
138
|
+
| `delete(id)` | Delete content |
|
|
139
|
+
| `incrementView(id)` | Increment view count |
|
|
140
|
+
|
|
141
|
+
#### categories
|
|
142
|
+
|
|
143
|
+
| Method | Description |
|
|
144
|
+
|--------|-------------|
|
|
145
|
+
| `list(params?)` | List categories |
|
|
146
|
+
| `getTree()` | Get hierarchical category tree |
|
|
147
|
+
| `getBySlug(slug)` | Get category by slug |
|
|
148
|
+
| `getById(id)` | Get category by ID |
|
|
149
|
+
| `getChildren(parentId)` | Get child categories |
|
|
150
|
+
| `getRoots()` | Get root categories |
|
|
151
|
+
| `getBreadcrumb(categoryId)` | Get category breadcrumb path |
|
|
152
|
+
|
|
153
|
+
### React Hooks
|
|
154
|
+
|
|
155
|
+
| Hook | Description |
|
|
156
|
+
|------|-------------|
|
|
157
|
+
| `useContents(options)` | Fetch content list with infinite scroll |
|
|
158
|
+
| `useContent(options)` | Fetch single content by slug or ID |
|
|
159
|
+
| `useCategories(options)` | Fetch categories (flat or tree) |
|
|
160
|
+
| `useSearchContents(options)` | Search with debounce |
|
|
161
|
+
| `useTags(options)` | Fetch all tags |
|
|
162
|
+
| `useAdjacentContents(options)` | Get prev/next navigation |
|
|
163
|
+
| `useThoughts(options)` | Fetch agent thoughts |
|
|
164
|
+
|
|
165
|
+
### Utility Functions
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { getLocalizedText, buildCategoryTree } from '@nby.ai/ucm'
|
|
169
|
+
|
|
170
|
+
// Get localized text with fallback
|
|
171
|
+
const title = getLocalizedText(content.title, 'zh', 'en')
|
|
172
|
+
|
|
173
|
+
// Build category tree from flat list
|
|
174
|
+
const tree = buildCategoryTree(flatCategories)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Types
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import type {
|
|
181
|
+
// Core types
|
|
182
|
+
Content,
|
|
183
|
+
ContentType,
|
|
184
|
+
ContentStatus,
|
|
185
|
+
ContentVisibility,
|
|
186
|
+
Category,
|
|
187
|
+
I18nText,
|
|
188
|
+
|
|
189
|
+
// Query params
|
|
190
|
+
ContentQueryParams,
|
|
191
|
+
CategoryQueryParams,
|
|
192
|
+
CreateContentInput,
|
|
193
|
+
UpdateContentInput,
|
|
194
|
+
|
|
195
|
+
// Metadata types
|
|
196
|
+
BlogMetadata,
|
|
197
|
+
NewsMetadata,
|
|
198
|
+
ThoughtMetadata,
|
|
199
|
+
ThoughtMood,
|
|
200
|
+
ThoughtType,
|
|
201
|
+
DigestMetadata,
|
|
202
|
+
BriefingMetadata,
|
|
203
|
+
CaseStudyMetadata,
|
|
204
|
+
EventMetadata,
|
|
205
|
+
DocMetadata,
|
|
206
|
+
} from '@nby.ai/ucm'
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Content Types
|
|
210
|
+
|
|
211
|
+
| Type | Description |
|
|
212
|
+
|------|-------------|
|
|
213
|
+
| `blog` | Blog posts, tutorials |
|
|
214
|
+
| `news` | Announcements, press releases |
|
|
215
|
+
| `doc` | Documentation, API docs |
|
|
216
|
+
| `event` | Events with time/location |
|
|
217
|
+
| `thought` | Agent thoughts (social feed style) |
|
|
218
|
+
| `digest` | Daily/weekly digests |
|
|
219
|
+
| `briefing` | Project briefings (daily/weekly/monthly) |
|
|
220
|
+
| `case_study` | Customer case studies |
|
|
221
|
+
| `faq` | Frequently asked questions |
|
|
222
|
+
|
|
223
|
+
### I18nText
|
|
224
|
+
|
|
225
|
+
Multi-language text stored as JSONB:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
interface I18nText {
|
|
229
|
+
zh?: string // Chinese
|
|
230
|
+
en?: string // English
|
|
231
|
+
ja?: string // Japanese
|
|
232
|
+
ko?: string // Korean
|
|
233
|
+
// ... more languages
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Database Schema
|
|
238
|
+
|
|
239
|
+
Requires Supabase with the following tables:
|
|
240
|
+
|
|
241
|
+
- `contents` - Main content table
|
|
242
|
+
- `categories` - Hierarchical categories
|
|
243
|
+
|
|
244
|
+
See [migrations](./supabase/migrations/) for full schema.
|
|
245
|
+
|
|
246
|
+
## Peer Dependencies
|
|
247
|
+
|
|
248
|
+
| Package | Version | Required |
|
|
249
|
+
|---------|---------|----------|
|
|
250
|
+
| `@supabase/supabase-js` | ^2.0.0 | Yes |
|
|
251
|
+
| `@tanstack/react-query` | ^5.0.0 | For React hooks |
|
|
252
|
+
| `react` | ^18.0.0 \|\| ^19.0.0 | For React hooks |
|
|
253
|
+
|
|
254
|
+
## License
|
|
255
|
+
|
|
256
|
+
MIT
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
*Version 1.0.0 | Last updated: 2026-02-15*
|