@xhub-short/sdk
v0.1.0-beta.5
Published
Main entry point for XHub-short SDK - Short Video Feed for WebView
Readme
@xhub-short/sdk
Glue Layer - Kết nối Core Domain với React UI
📖 SDK là gì?
SDK (Software Development Kit) là package chính mà Host App cài đặt để tích hợp Short Video Feed. Nó đóng vai trò "Glue Layer" - kết nối tất cả các thành phần bên trong SDK thành một API thống nhất, dễ sử dụng.
┌─────────────────────────────────────────────────────────────────────┐
│ HOST APP (Your App) │
│ │
│ import { ShortVideoProvider, useFeed, usePlayer } from '@xhub-short/sdk';
│ import { VideoFeed, VideoPlayer } from '@xhub-short/ui'; │
│ │
└──────────────────────────────┬──────────────────────────────────────┘
│ uses
▼
┌─────────────────────────────────────────────────────────────────────┐
│ @xhub-short/ui │
│ (Lego Components - Phase 4) │
│ │
│ VideoFeed, VideoPlayer, InteractionBar, CommentSheet... │
│ │
└──────────────────────────────┬──────────────────────────────────────┘
│ imports hooks from
▼
┌─────────────────────────────────────────────────────────────────────┐
│ @xhub-short/sdk │
│ (Glue Layer - Current) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Provider │ │ Hooks │ │ Factory (createSDK) │ │
│ │ (Context) │ │ (React) │ │ (Dependency Inject) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │
└──────────────────────────────┬──────────────────────────────────────┘
│ orchestrates
┌────────────────────┴────────────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ @core │ │ @adapters │
│ (Domain) │ │ (Infra) │
└─────────────┘ └─────────────┘Lưu ý:
@sdkKHÔNG import từ@ui. Mũi tên chỉ hướng dependency (ai import ai).
🎯 Tại sao cần SDK?
| Vấn đề | Giải pháp của SDK |
|--------|-------------------|
| Host App phải hiểu kiến trúc phức tạp bên trong | SDK expose API đơn giản: hooks + provider |
| Khó setup dependency injection cho adapters | createSDK() tự động wire tất cả |
| State management phức tạp (Feed, Player, Resource...) | Hooks abstract hết complexity |
| SSR compatibility | useSyncExternalStore built-in |
| Memory leaks khi unmount | sdk.destroy() cleanup tự động |
📋 Nhiệm vụ chi tiết của SDK
SDK thực hiện 7 nhiệm vụ chính để Host App có thể tích hợp dễ dàng:
1. Dependency Injection & Wiring
SDK là nơi duy nhất thực hiện dependency injection - kết nối các adapters với core managers:
┌─────────────────────────────────────────────────────────────────────┐
│ DEPENDENCY INJECTION MAP │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Adapter Interface ───► Core Component │
│ ───────────────── ────────────── │
│ IDataSource ───► FeedManager │
│ IInteraction ───► OptimisticManager │
│ ISessionStorage ───► LifecycleManager │
│ INetworkAdapter ───► ResourceGovernor │
│ IVideoLoader + IPosterLoader ───► ResourceGovernor │
│ IAnalytics ───► PlayerEngine (events) │
│ ILogger ───► All managers (debug) │
│ │
└─────────────────────────────────────────────────────────────────────┘2. Adapter Resolution
SDK tự động resolve adapters theo thứ tự ưu tiên:
Config Priority:
┌──────────────────────────────────────────────────────────────┐
│ │
│ 1. config.adapters.dataSource ◄── Highest (User-defined)│
│ │ │
│ ▼ fallback │
│ 2. config.preset → createBrowserAdapters() │
│ │ │
│ ▼ fallback │
│ 3. MockAdapter ◄── Lowest (Development) │
│ │
└──────────────────────────────────────────────────────────────┘Điều này cho phép:
- Development: Không cần config → Mock adapters tự động
- Production với Preset: Chỉ cần
baseUrl + auth + endpoints - Full Custom: Override bất kỳ adapter nào
3. State Synchronization (Core ↔ React)
Core Domain sử dụng vanilla stores (không React). SDK bridge chúng sang React:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Core Store │ │ SDK Hook │ │ React UI │
│ (Vanilla) │ ───► │ (useSyncExternal│ ───► │ (Re-render) │
│ │ │ Store) │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ store.subscribe() │ getSnapshot()
│ store.setState() │
▼ ▼
Framework-agnostic SSR-safe subscriptionTại sao không dùng Zustand trực tiếp trong Core?
- Core phải framework-agnostic (có thể dùng với Vue, Svelte...)
- Giảm bundle size cho Core package
- Tách biệt concerns rõ ràng
4. Event Orchestration
SDK wire các events giữa managers để chúng phối hợp:
┌──────────────────────────────────────────────────────────────────────┐
│ EVENT WIRING │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ PlayerEngine │
│ │ │
│ ├── on('videoChange') ──────► Analytics.flush() │
│ │ (Flush trước khi đổi video) │
│ │ │
│ └── on('timeUpdate') ───────► LifecycleManager.setPendingSave()│
│ (Auto-save playback position) │
│ │
│ LifecycleManager │
│ │ │
│ └── on('visibilityChange') ─► Analytics.flush() │
│ (state === 'hidden') (Flush khi app background) │
│ │
│ ResourceGovernor │
│ │ │
│ └── on('focusChange') ──────► PlayerEngine.load(video) │
│ (Load video khi swipe) │
│ │
└──────────────────────────────────────────────────────────────────────┘5. Lifecycle Management
SDK quản lý toàn bộ vòng đời của các managers:
| Phase | SDK Actions | |-------|-------------| | Init | Tạo managers, inject adapters, wire events, restore session | | Active | Managers hoạt động độc lập, SDK chỉ expose hooks | | Background | Auto-save session, flush analytics | | Destroy | Cleanup timers, unsubscribe events, clear cache |
// Tự động trong Provider:
useEffect(() => {
const sdk = createSDK(config);
return () => sdk.destroy(); // Cleanup khi unmount
}, []);6. Public API Surface
SDK expose API đơn giản, ẩn đi complexity bên trong:
| Exposed to Host App | Hidden from Host App |
|---------------------|----------------------|
| useFeed() - videos, loadMore | FeedManager internals |
| usePlayer() - play, pause, seek | PlayerEngine state machine |
| useOptimistic() - like, follow | Rollback queue, pending states |
| useSwipeGesture() - onSwipe | DOM allocation logic |
| Config với preset | Adapter creation |
7. SSR Support
SDK đảm bảo hoạt động với Server-Side Rendering:
┌──────────────────────────────────────────────────────────────┐
│ SSR STRATEGY │
├──────────────────────────────────────────────────────────────┤
│ │
│ Server: │
│ ├─ ShortVideoProvider renders với empty state │
│ ├─ Hooks return initial/fallback values │
│ └─ Không tạo SDK instance │
│ │
│ Client (Hydration): │
│ ├─ useEffect tạo SDK instance │
│ ├─ Restore session từ localStorage │
│ └─ useSyncExternalStore subscribe to stores │
│ │
└──────────────────────────────────────────────────────────────┘🏗️ Kiến trúc
Vị trí trong hệ thống
┌─────────────────────────────────────────────────────────────────────┐
│ HEXAGONAL ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ @xhub-short/sdk │ │
│ │ ════════════════ │ │
│ │ │ │
│ │ ShortVideoProvider ──────┬──────── createSDK() │ │
│ │ │ │ │ │ │
│ │ │ Dependency Injection │ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Hooks │ │ Core │ │ Adapters │ │ │
│ │ │ (React) │◀────▶│ Managers │◀──│ (Inject) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ @contracts │ │ @core │ │ @adapters │ │
│ │ (Interfaces) │ │ (Domain) │ │ (Infrastructure) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘Package Dependencies
@xhub-short/sdk
├── @xhub-short/contracts (types, interfaces)
├── @xhub-short/core (domain logic)
├── @xhub-short/adapters (mock + preset adapters)
├── @xhub-short/ui (headless components)
├── react (peer dependency)
└── zustand (peer dependency)Host App Installation
npm install @xhub-short/sdkThat's it! No other dependencies needed.
Bundled internally:
zustand- state management (bundled in @xhub-short/core)clsx- class names utility (bundled in @xhub-short/ui)- Custom
useTouchDraghook - gesture handling (bundled in @xhub-short/sdk)
✨ Chức năng chính
1. Provider - Context cho toàn bộ SDK
<ShortVideoProvider config={{ preset: {...} }}>
<YourApp />
</ShortVideoProvider>2. Hooks - React API để truy cập SDK
| Hook | Mục đích |
|------|----------|
| useFeed() | Video list, pagination, loading states |
| usePlayer() | Playback control, time, volume, handlers |
| useResource() | DOM allocations, prefetch queue |
| useOptimistic() | Like/Follow với instant UI + rollback |
| useSwipeGesture() | Vertical swipe navigation |
3. Wired Components - Pre-connected UI Components
SDK exports pre-wired versions of UI components with SDK hooks already injected:
| Component | Description |
|-----------|-------------|
| VideoFeed | Wired VideoFeedHeadless with useFeed + useSwipeGesture |
| VideoSlot | Wired VideoSlotHeadless with useResourceAllocation + usePlayer |
| VideoPlayer | Wired VideoPlayerHeadless with usePlayer (auto-load, sync) |
| ProgressBar | Wired progress bar with Direct DOM Update (60fps smooth) |
VideoPlayer (Wired)
Pre-wired video player that auto-connects to SDK PlayerEngine.
import { VideoSlot, VideoPlayer } from '@xhub-short/sdk';
import { VideoSlotPoster, VideoSlotOverlay } from '@xhub-short/ui';
function FeedItem({ video }) {
return (
<VideoSlot video={video}>
<VideoPlayer
resetOnInactive={true} // TikTok-like: restart from beginning
/>
<VideoSlotPoster />
<VideoSlotOverlay>...</VideoSlotOverlay>
</VideoSlot>
);
}| Prop | Type | Default | Description |
|------|------|---------|-------------|
| video | VideoItem | from context | Video to play (optional if inside VideoSlot) |
| autoLoad | boolean | true | Auto-load video into PlayerEngine |
| autoPlay | boolean | true | Auto-play when slot becomes active |
| resetOnInactive | boolean | true | Reset video to 0:00 when slot becomes inactive |
| loop | boolean | true | Loop video playback |
| muted | boolean | true | Start muted (required for autoplay) |
resetOnInactive behavior:
true(default): TikTok-like - swiping back restarts video from beginningfalse: YouTube-like - video continues from where user left off
ProgressBar (Wired) - P11 Re-render Optimization
Pre-wired progress bar that uses Direct DOM Update for 60fps smooth animation.
import { VideoSlot, VideoPlayer, ProgressBar } from '@xhub-short/sdk';
import { VideoSlotOverlay } from '@xhub-short/ui';
function FeedItem({ video }) {
return (
<VideoSlot video={video}>
<VideoPlayer />
<VideoSlotOverlay>
<ProgressBar seekable={true} /> {/* 60fps smooth! */}
</VideoSlotOverlay>
</VideoSlot>
);
}| Prop | Type | Default | Description |
|------|------|---------|-------------|
| seekable | boolean | true | Enable click/drag to seek |
| showTime | boolean | false | Show current/duration text |
| showTooltip | boolean | true | Show tooltip on hover |
| minimal | boolean | false | Thin bar without handle |
Performance Architecture:
PlayerEngine.store ───── subscribe() ───── fillRef.style.transform
(scaleX, GPU accelerated)
NOT React re-render!This component demonstrates ADR 005: Direct DOM Manipulation for high-frequency updates.
See docs/issues/RE_RENDER_ANALYSIS.md for detailed optimization rationale.
4. Factory - Dependency Injection
const sdk = createSDK({
preset: { baseUrl, auth, endpoints }, // Auto-generate adapters
adapters: { dataSource, interaction }, // Or custom adapters
feed: { pageSize: 20 },
player: { autoplay: true },
});🔄 Cách hoạt động
Flow: Từ Config đến UI
┌──────────────────────────────────────────────────────────────────────┐
│ INITIALIZATION │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Host App mount <ShortVideoProvider config={...}> │
│ │ │
│ ▼ │
│ 2. createSDK(config) │
│ ├─ Resolve adapters (preset → browser adapters) │
│ ├─ Create Core managers: │
│ │ ├─ FeedManager (video list, deduplication, GC) │
│ │ ├─ PlayerEngine (playback state machine) │
│ │ ├─ ResourceGovernor (DOM allocation, prefetch) │
│ │ ├─ OptimisticManager (like/follow with rollback) │
│ │ └─ LifecycleManager (session persistence) │
│ └─ Wire analytics flush events │
│ │ │
│ ▼ │
│ 3. SDKContext.Provider value={sdk} │
│ │ │
│ ▼ │
│ 4. Hooks subscribe to managers via useSyncExternalStore │
│ │
└──────────────────────────────────────────────────────────────────────┘Flow: User Interaction
User swipes down
│
▼
useSwipeGesture() ─────────► ResourceGovernor.setFocusedIndex(n+1)
│
┌────────────────┼────────────────┐
▼ ▼ ▼
Deallocate Allocate Preload
video[n-1] video[n+1] video[n+2]
│
▼
PlayerEngine.load(video)
│
▼
Analytics.track('video_view')📦 Exports
Provider
export { ShortVideoProvider } from './provider';
export type { ShortVideoProviderProps, SDKConfig } from './provider';Hooks
// Feed management
export { useFeed, useFeedSelector } from './hooks';
// Player control
export { usePlayer, usePlayerSelector, usePlayerForVideo } from './hooks';
// Resource management (DOM allocation)
export { useResource, useResourceSelector, useResourceAllocation } from './hooks';
// Optimistic UI (like/follow)
export { useOptimistic, useOptimisticSelector } from './hooks';
// Swipe navigation
export { useSwipeGesture } from './hooks';
// SDK context access
export { useSDK } from './hooks';Factory
export { createSDK } from './store';
export type { SDKInstance } from './store';Types (Re-exported)
// From @xhub-short/contracts
export type { VideoItem, FeedResponse, IDataSource, ... } from '@xhub-short/contracts';
// From @xhub-short/core
export type { FeedState, PlayerState, ResourceState, ... } from '@xhub-short/core';🚀 Quick Start
Simplest Usage (Recommended)
import { ShortVideoRoot, VideoFeed, DefaultSlot } from '@xhub-short/sdk';
function App() {
return (
<ShortVideoRoot
config={{
preset: {
baseUrl: 'https://api.example.com/v1',
auth: { getAccessToken: () => localStorage.getItem('token') },
endpoints: {
feed: { list: '/videos', detail: '/videos/:id' },
interaction: { like: '/videos/:id/like' },
},
},
}}
errorMode="auto-skip-with-toast"
>
<VideoFeed renderSlot={(video, index) => (
<DefaultSlot
video={video}
index={index}
showBookmark={true}
onOpenComments={(videoId) => openSheet(videoId)}
onShare={(video) => shareVideo(video)}
/>
)} />
</ShortVideoRoot>
);
}That's it! The SDK handles everything: video loading, HLS playback, swipe navigation, like/follow actions, error handling, and more.
DefaultSlot Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| video | VideoItem | required | Video data |
| index | number | required | Index in feed |
| showPoster | boolean | true | Show poster |
| showLikeAnimation | boolean | true | Double-tap hearts |
| showAuthorInfo | boolean | true | Avatar + follow |
| showActionBar | boolean | true | Like, comment, share |
| showVideoInfo | boolean | true | Caption, hashtags |
| showProgressBar | boolean | true | Progress bar |
| showBookmark | boolean | false | Bookmark button |
| onOpenComments | (videoId) => void | - | Comment click |
| onShare | (video) => void | - | Share click |
| onOpenProfile | (author) => void | - | Author click |
| onHashtagClick | (tag) => void | - | Hashtag click |
Custom Usage (Advanced)
For full control, use individual components:
import {
ShortVideoRoot, VideoFeed, VideoSlot, VideoPlayer,
ActionBar, AuthorInfo, VideoInfo, ProgressBar
} from '@xhub-short/sdk';
import { VideoSlotOverlay, VideoSlotPoster } from '@xhub-short/ui';
<VideoFeed renderSlot={(video, index) => (
<VideoSlot video={video} index={index}>
<VideoPlayer />
<VideoSlotPoster />
<VideoSlotOverlay>
<VideoSlotOverlay.Actions>
<AuthorInfo video={video} variant="avatar-badge" />
<ActionBar video={video} />
</VideoSlotOverlay.Actions>
<VideoSlotOverlay.Bottom>
<VideoInfo video={video} />
<ProgressBar showTime />
</VideoSlotOverlay.Bottom>
</VideoSlotOverlay>
</VideoSlot>
)} />
### With Custom Adapters
```tsx
<ShortVideoProvider
config={{
adapters: {
dataSource: new MyGraphQLAdapter(),
interaction: new MyFlutterBridgeAdapter(),
},
}}
>
<App />
</ShortVideoProvider>👤 Guest Mode (Chế độ khách)
SDK hỗ trợ chế độ khách cho phép người dùng chưa đăng nhập xem video và bình luận (read-only). Các tương tác (Like, Comment, Follow) sẽ bị chặn và trigger callback để Host App xử lý (ví dụ: hiện modal đăng nhập).
Cấu hình
<ShortVideoRoot
config={{
// ...
guest: {
/**
* 'auto': Tự động detect dựa trên auth token (default)
* true: Force guest mode
* false: Disable guest mode
*/
mode: 'auto',
/**
* Callback khi guest thực hiện hành động cần auth (Like, Comment, Follow...)
*/
onAction: (action, context) => {
console.log('Guest Action:', action, context);
// VD: showLoginModal();
}
}
}}
>
<VideoFeed />
</ShortVideoRoot>Các hành động được chặn
- Like / Bookmark: Trigger
onAction - Follow: Trigger
onAction - Comment Input: Hiển thị nút "Login to comment". Khi click triggers
onAction(action='comment').
⚡ Prefetch Data (Tải trước dữ liệu)
Cho phép Host App tải trước dữ liệu Feed (Reels) để trải nghiệm hiển thị tức thì (instant loading).
Cách sử dụng
Gọi hàm prefetchFeed trước khi mount SDK components (ví dụ: khi hover vào menu Reels hoặc trong route loader).
import { prefetchFeed } from '@xhub-short/sdk';
// 1. Prefetch data (lưu vào bộ nhớ tạm)
// Có thể gọi ở bất kỳ đâu trong Host App
await prefetchFeed(sdkConfig, { ttl: 5 * 60 * 1000 });
// ... Sau đó ...
// 2. Khi User vào trang Reels, SDK sẽ tự động dùng data đã fetch
<ShortVideoRoot config={sdkConfig}>
<VideoFeed />
</ShortVideoRoot>Đặc điểm
- In-Memory Cache: Dữ liệu chỉ lưu tạm trong RAM, mất khi refresh trang (đảm bảo fresh data).
- Auto-Consume:
ShortVideoRoottự động tiêu thụ cache và xóa sau khi dùng. - Performance: Giúp giảm thời gian chờ đợi (LCP) xuống gần bằng 0 khi user chuyển trang.
🔗 Liên kết với các Packages
| Package | Quan hệ với SDK |
|---------|-----------------|
| @xhub-short/contracts | SDK re-export types/interfaces |
| @xhub-short/core | SDK wraps core managers với React hooks |
| @xhub-short/adapters | SDK inject adapters vào core managers |
| @xhub-short/ui | UI components sử dụng SDK hooks (Phase 4) |
| @xhub-short/bridge | Optional Flutter integration |
Dependency Flow
contracts ◀─────────────────────────────────────────┐
▲ │
│ │
core ◀──────────────────────────┐ │
▲ │ │
│ │ │
adapters ◀──────────┐ │ │
▲ │ │ │
│ │ │ │
└───────────────┴──────────┴────────── sdk ───┘
│
▼
Host App⚠️ Lưu ý quan trọng
SSR Safety
- Tất cả hooks sử dụng
useSyncExternalStore - Provider render fallback trên server
- SDK instance chỉ tạo trên client
Memory Management
sdk.destroy()được gọi tự động khi Provider unmount- Max 3 video DOM nodes (via ResourceGovernor)
- LRU cache eviction trong FeedManager
Deprecated APIs
// ❌ DEPRECATED - Singleton gây issues với SSR/testing
const sdk = getSDK(config);
// ✅ RECOMMENDED - Sử dụng Provider
<ShortVideoProvider config={config}>
<App />
</ShortVideoProvider>📚 Tài liệu liên quan
- ADD.md - System Architecture
- TECH_STACK.md - Technology Stack
- packages/core/README.md - Core Domain
- packages/adapters/README.md - Adapters
