npm package: @nby.ai/ucm
Description: Universal Content Module - Supabase-based multilingual CMS
Author: NBY Team
Latest version: 1.1.0

# @nby.ai/ucm

> Universal Content Module - Supabase-based multilingual CMS

[![npm version](https://badge.fury.io/js/@nby.ai%2Fucm.svg)](https://www.npmjs.com/package/@nby.ai/ucm)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7+-blue.svg)](https://www.typescriptlang.org/)

## Features

- **Multi-language content** - JSONB storage, unlimited languages (zh, en, ja, ko, etc.)
- **Multiple content types** - news, blog, doc, event, thought, digest, briefing, case_study, faq
- **Hierarchical categories** - Multi-level category tree support
- **Framework-agnostic core** - Works with Node.js, scripts, any framework
- **React integration** - Provider + Hooks with TanStack React Query
- **Full TypeScript support** - Complete type definitions

## Installation

```bash
# Core package
pnpm add @nby.ai/ucm @supabase/supabase-js

# If using React hooks
pnpm add @tanstack/react-query react
```

## Quick Start

### Core API (Node.js / Scripts)

```typescript
import { createUCMClient } from '@nby.ai/ucm'

const ucm = createUCMClient({
  url: process.env.SUPABASE_URL!,
  anonKey: process.env.SUPABASE_ANON_KEY!,
})

// List blog posts
const posts = await ucm.contents.list({ type: 'blog', limit: 10 })

// Get single content by slug
const post = await ucm.contents.getBySlug('hello-world')

// Full-text search
const results = await ucm.contents.search('AI Agent')

// Get category tree
const categories = await ucm.categories.getTree()

// Create content (requires service key)
await ucm.contents.create({
  slug: 'new-post',
  type: 'blog',
  title: { zh: '新文章', en: 'New Post' },
  content: { zh: '正文...', en: 'Body...' },
})
```

### React Integration

```tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { UCMProvider, useContents, useContent, getLocalizedText } from '@nby.ai/ucm'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <UCMProvider
        config={{
          url: import.meta.env.VITE_SUPABASE_URL,
          anonKey: import.meta.env.VITE_SUPABASE_ANON_KEY,
        }}
      >
        <BlogList />
      </UCMProvider>
    </QueryClientProvider>
  )
}

function BlogList() {
  const { contents, isLoading, error, loadMore, hasMore } = useContents({
    type: 'blog',
    limit: 10,
  })

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <>
      <ul>
        {contents.map(post => (
          <li key={post.id}>
            {getLocalizedText(post.title, 'zh')}
          </li>
        ))}
      </ul>
      {hasMore && <button onClick={loadMore}>Load More</button>}
    </>
  )
}
```

## API Reference

### createUCMClient(config)

Creates a UCM client instance.

```typescript
interface UCMClientConfig {
  url: string      // Supabase project URL
  anonKey: string  // Supabase anon key (or service key for admin ops)
  options?: {
    persistSession?: boolean    // Default: false
    autoRefreshToken?: boolean  // Default: false
  }
}
```

### UCMClient Methods

#### contents

| Method | Description |
|--------|-------------|
| `list(params?)` | List contents with filtering and pagination |
| `getBySlug(slug)` | Get content by slug |
| `getById(id)` | Get content by ID |
| `count(params?)` | Count contents matching params |
| `search(query, type?)` | Full-text search |
| `getTags(type?)` | Get all unique tags |
| `create(input)` | Create content (requires service key) |
| `update(id, input)` | Update content |
| `delete(id)` | Delete content |
| `incrementView(id)` | Increment view count |

#### categories

| Method | Description |
|--------|-------------|
| `list(params?)` | List categories |
| `getTree()` | Get hierarchical category tree |
| `getBySlug(slug)` | Get category by slug |
| `getById(id)` | Get category by ID |
| `getChildren(parentId)` | Get child categories |
| `getRoots()` | Get root categories |
| `getBreadcrumb(categoryId)` | Get category breadcrumb path |

### React Hooks

| Hook | Description |
|------|-------------|
| `useContents(options)` | Fetch content list with infinite scroll |
| `useContent(options)` | Fetch single content by slug or ID |
| `useCategories(options)` | Fetch categories (flat or tree) |
| `useSearchContents(options)` | Search with debounce |
| `useTags(options)` | Fetch all tags |
| `useAdjacentContents(options)` | Get prev/next navigation |
| `useThoughts(options)` | Fetch agent thoughts |

### Utility Functions

```typescript
import { getLocalizedText, buildCategoryTree } from '@nby.ai/ucm'

// Get localized text with fallback
const title = getLocalizedText(content.title, 'zh', 'en')

// Build category tree from flat list
const tree = buildCategoryTree(flatCategories)
```

## Types

```typescript
import type {
  // Core types
  Content,
  ContentType,
  ContentStatus,
  ContentVisibility,
  Category,
  I18nText,

  // Query params
  ContentQueryParams,
  CategoryQueryParams,
  CreateContentInput,
  UpdateContentInput,

  // Metadata types
  BlogMetadata,
  NewsMetadata,
  ThoughtMetadata,
  ThoughtMood,
  ThoughtType,
  DigestMetadata,
  BriefingMetadata,
  CaseStudyMetadata,
  EventMetadata,
  DocMetadata,
} from '@nby.ai/ucm'
```

### Content Types

| Type | Description |
|------|-------------|
| `blog` | Blog posts, tutorials |
| `news` | Announcements, press releases |
| `doc` | Documentation, API docs |
| `event` | Events with time/location |
| `thought` | Agent thoughts (social feed style) |
| `digest` | Daily/weekly digests |
| `briefing` | Project briefings (daily/weekly/monthly) |
| `case_study` | Customer case studies |
| `faq` | Frequently asked questions |

### I18nText

Multi-language text stored as JSONB:

```typescript
interface I18nText {
  zh?: string  // Chinese
  en?: string  // English
  ja?: string  // Japanese
  ko?: string  // Korean
  // ... more languages
}
```

## Database Schema

Requires Supabase with the following tables:

- `contents` - Main content table
- `categories` - Hierarchical categories

See [migrations](./supabase/migrations/) for full schema.

## Peer Dependencies

| Package | Version | Required |
|---------|---------|----------|
| `@supabase/supabase-js` | ^2.0.0 | Yes |
| `@tanstack/react-query` | ^5.0.0 | For React hooks |
| `react` | ^18.0.0 \|\| ^19.0.0 | For React hooks |

## License

MIT

---

*Version 1.0.0 | Last updated: 2026-02-15*