mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-02 21:03:09 +00:00
1792 lines
34 KiB
Markdown
1792 lines
34 KiB
Markdown
|
|
# @opchan/core API Reference
|
||
|
|
|
||
|
|
Complete API documentation for all classes, interfaces, and utilities in the OpChan Core SDK.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Table of Contents
|
||
|
|
|
||
|
|
1. [OpChanClient](#opchanclient)
|
||
|
|
2. [DelegationManager](#delegationmanager)
|
||
|
|
3. [LocalDatabase](#localdatabase)
|
||
|
|
4. [ForumActions](#forumactions)
|
||
|
|
5. [UserIdentityService](#useridentityservice)
|
||
|
|
6. [RelevanceCalculator](#relevancecalculator)
|
||
|
|
7. [MessageManager](#messagemanager)
|
||
|
|
8. [BookmarkService](#bookmarkservice)
|
||
|
|
9. [MessageValidator](#messagevalidator)
|
||
|
|
10. [Type Definitions](#type-definitions)
|
||
|
|
11. [Utility Functions](#utility-functions)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## OpChanClient
|
||
|
|
|
||
|
|
Main client class that orchestrates all services and provides a unified interface.
|
||
|
|
|
||
|
|
### Constructor
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
constructor(config: OpChanClientConfig)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `config.wakuConfig` - Waku network configuration
|
||
|
|
- `contentTopic` - Content topic for Waku messages (e.g., `/opchan/1/messages/proto`)
|
||
|
|
- `reliableChannelId` - Channel ID for reliable messaging (e.g., `opchan-messages`)
|
||
|
|
- `config.reownProjectId` - Optional Reown/WalletConnect project ID
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const client = new OpChanClient({
|
||
|
|
wakuConfig: {
|
||
|
|
contentTopic: '/opchan/1/messages/proto',
|
||
|
|
reliableChannelId: 'opchan-messages'
|
||
|
|
},
|
||
|
|
reownProjectId: 'your-project-id'
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Properties
|
||
|
|
|
||
|
|
#### `config: OpChanClientConfig`
|
||
|
|
|
||
|
|
Client configuration object passed to constructor.
|
||
|
|
|
||
|
|
#### `messageManager: DefaultMessageManager`
|
||
|
|
|
||
|
|
Manages Waku network connectivity and message transmission.
|
||
|
|
|
||
|
|
#### `database: LocalDatabase`
|
||
|
|
|
||
|
|
IndexedDB-backed local storage with in-memory caching.
|
||
|
|
|
||
|
|
#### `forumActions: ForumActions`
|
||
|
|
|
||
|
|
High-level actions for content creation and moderation.
|
||
|
|
|
||
|
|
#### `relevance: RelevanceCalculator`
|
||
|
|
|
||
|
|
Content relevance scoring algorithm.
|
||
|
|
|
||
|
|
#### `messageService: MessageService`
|
||
|
|
|
||
|
|
Low-level message signing and broadcasting.
|
||
|
|
|
||
|
|
#### `userIdentityService: UserIdentityService`
|
||
|
|
|
||
|
|
User identity resolution and profile management.
|
||
|
|
|
||
|
|
#### `delegation: DelegationManager`
|
||
|
|
|
||
|
|
Cryptographic key delegation system.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## DelegationManager
|
||
|
|
|
||
|
|
Manages browser key delegation with wallet signatures or anonymous sessions.
|
||
|
|
|
||
|
|
### Methods
|
||
|
|
|
||
|
|
#### `delegate(address, duration, signFunction)`
|
||
|
|
|
||
|
|
Create a wallet-signed delegation authorizing browser keys.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async delegate(
|
||
|
|
address: `0x${string}`,
|
||
|
|
duration: '7days' | '30days' = '7days',
|
||
|
|
signFunction: (message: string) => Promise<string>
|
||
|
|
): Promise<boolean>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `address` - Wallet address to delegate from
|
||
|
|
- `duration` - Delegation validity period ('7days' or '30days')
|
||
|
|
- `signFunction` - Function that signs the authorization message with wallet
|
||
|
|
|
||
|
|
**Returns:** `true` if delegation created successfully, `false` otherwise
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { signMessage } from 'viem/accounts';
|
||
|
|
|
||
|
|
const success = await client.delegation.delegate(
|
||
|
|
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
|
||
|
|
'7days',
|
||
|
|
async (message) => await signMessage({ message, account: walletAddress })
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Flow:**
|
||
|
|
|
||
|
|
1. Generates Ed25519 browser keypair
|
||
|
|
2. Creates authorization message with expiry timestamp and nonce
|
||
|
|
3. Signs authorization message with wallet (via `signFunction`)
|
||
|
|
4. Stores delegation in IndexedDB
|
||
|
|
5. Returns success status
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `delegateAnonymous(duration)`
|
||
|
|
|
||
|
|
Create an anonymous delegation without wallet signature.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async delegateAnonymous(
|
||
|
|
duration: '7days' | '30days' = '7days'
|
||
|
|
): Promise<string>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `duration` - Delegation validity period
|
||
|
|
|
||
|
|
**Returns:** Session ID (UUID) for the anonymous user
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const sessionId = await client.delegation.delegateAnonymous('7days');
|
||
|
|
// sessionId: "a3f5c2d1-8e9b-4c7a-b6d5-3e2f1a0b9c8d"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Flow:**
|
||
|
|
|
||
|
|
1. Generates Ed25519 browser keypair
|
||
|
|
2. Generates UUID session ID
|
||
|
|
3. Creates expiry timestamp and nonce
|
||
|
|
4. Stores anonymous delegation in IndexedDB
|
||
|
|
5. Returns session ID as user address
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `signMessage(message)`
|
||
|
|
|
||
|
|
Sign a message with the delegated browser key.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async signMessage(
|
||
|
|
message: UnsignedMessage
|
||
|
|
): Promise<OpchanMessage | null>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `message` - Unsigned message object (cell, post, comment, vote, etc.)
|
||
|
|
|
||
|
|
**Returns:** Signed message with signature and delegation proof, or `null` if delegation invalid
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const unsignedPost = {
|
||
|
|
type: MessageType.POST,
|
||
|
|
id: 'post-id',
|
||
|
|
cellId: 'cell-id',
|
||
|
|
title: 'Hello',
|
||
|
|
content: 'World',
|
||
|
|
timestamp: Date.now(),
|
||
|
|
author: userAddress
|
||
|
|
};
|
||
|
|
|
||
|
|
const signed = await client.delegation.signMessage(unsignedPost);
|
||
|
|
if (signed) {
|
||
|
|
// Message is now signed and ready to send
|
||
|
|
await client.messageManager.sendMessage(signed);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Signature Process:**
|
||
|
|
|
||
|
|
1. Retrieves cached delegation (or loads from storage)
|
||
|
|
2. Checks if delegation is valid (not expired)
|
||
|
|
3. Creates message payload (excluding signature fields)
|
||
|
|
4. Signs payload with browser private key (Ed25519)
|
||
|
|
5. Attaches signature, browser public key, and delegation proof
|
||
|
|
6. Returns signed message
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `verify(message)`
|
||
|
|
|
||
|
|
Verify a signed message's authenticity.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async verify(message: OpchanMessage): Promise<boolean>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `message` - Signed message to verify
|
||
|
|
|
||
|
|
**Returns:** `true` if message is valid, `false` otherwise
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const isValid = await client.delegation.verify(message);
|
||
|
|
if (isValid) {
|
||
|
|
await client.database.applyMessage(message);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Verification Process:**
|
||
|
|
|
||
|
|
1. Checks required fields (signature, browserPubKey, author)
|
||
|
|
2. Verifies message signature with browser public key
|
||
|
|
3. If wallet delegation: verifies delegation proof
|
||
|
|
4. If anonymous: validates session ID format (UUID)
|
||
|
|
5. Returns validation result
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `verifyWithReason(message)`
|
||
|
|
|
||
|
|
Verify message and return detailed validation reasons.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async verifyWithReason(
|
||
|
|
message: OpchanMessage
|
||
|
|
): Promise<{ isValid: boolean; reasons: string[] }>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const result = await client.delegation.verifyWithReason(message);
|
||
|
|
if (!result.isValid) {
|
||
|
|
console.error('Validation failed:', result.reasons);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `getStatus(currentAddress?)`
|
||
|
|
|
||
|
|
Get current delegation status.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async getStatus(
|
||
|
|
currentAddress?: string
|
||
|
|
): Promise<DelegationFullStatus>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `currentAddress` - Optional address to check against stored delegation
|
||
|
|
|
||
|
|
**Returns:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface DelegationFullStatus {
|
||
|
|
hasDelegation: boolean;
|
||
|
|
isValid: boolean;
|
||
|
|
timeRemaining?: number; // milliseconds
|
||
|
|
publicKey?: string;
|
||
|
|
address?: `0x${string}`;
|
||
|
|
proof?: DelegationProof;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const status = await client.delegation.getStatus(userAddress);
|
||
|
|
|
||
|
|
if (!status.isValid) {
|
||
|
|
console.log('Delegation expired or invalid');
|
||
|
|
// Re-authorize user
|
||
|
|
} else {
|
||
|
|
console.log(`Valid for ${Math.round(status.timeRemaining! / 3600000)} more hours`);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `clear()`
|
||
|
|
|
||
|
|
Clear stored delegation from IndexedDB.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async clear(): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
await client.delegation.clear();
|
||
|
|
// User needs to re-authorize
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## LocalDatabase
|
||
|
|
|
||
|
|
IndexedDB-backed local storage with in-memory caching for fast reads.
|
||
|
|
|
||
|
|
### Properties
|
||
|
|
|
||
|
|
#### `cache: LocalDatabaseCache`
|
||
|
|
|
||
|
|
In-memory cache of all content. Fast synchronous access.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface LocalDatabaseCache {
|
||
|
|
cells: { [id: string]: CellMessage };
|
||
|
|
posts: { [id: string]: PostMessage };
|
||
|
|
comments: { [id: string]: CommentMessage };
|
||
|
|
votes: { [key: string]: VoteMessage };
|
||
|
|
moderations: { [key: string]: ModerateMessage };
|
||
|
|
userIdentities: { [address: string]: UserIdentityCache };
|
||
|
|
bookmarks: { [id: string]: Bookmark };
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Synchronous access to cached data
|
||
|
|
const cells = Object.values(client.database.cache.cells);
|
||
|
|
const posts = Object.values(client.database.cache.posts);
|
||
|
|
|
||
|
|
// Filter by relationship
|
||
|
|
const cellPosts = posts.filter(p => p.cellId === 'cell-id');
|
||
|
|
```
|
||
|
|
|
||
|
|
### Methods
|
||
|
|
|
||
|
|
#### `open()`
|
||
|
|
|
||
|
|
Open IndexedDB and hydrate in-memory cache.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async open(): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const client = new OpChanClient(config);
|
||
|
|
await client.database.open(); // MUST call before use
|
||
|
|
```
|
||
|
|
|
||
|
|
**Hydration Process:**
|
||
|
|
|
||
|
|
1. Opens IndexedDB connection
|
||
|
|
2. Loads all cells, posts, comments, votes, moderations from stores
|
||
|
|
3. Populates in-memory cache
|
||
|
|
4. Loads pending message IDs
|
||
|
|
5. Ready for use
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `applyMessage(message)`
|
||
|
|
|
||
|
|
Validate and store an incoming message.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async applyMessage(message: unknown): Promise<boolean>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `message` - Message to validate and store
|
||
|
|
|
||
|
|
**Returns:** `true` if message was newly processed and stored, `false` if invalid or duplicate
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
client.messageManager.onMessageReceived(async (message) => {
|
||
|
|
const wasNew = await client.database.applyMessage(message);
|
||
|
|
if (wasNew) {
|
||
|
|
console.log('New message stored:', message.type);
|
||
|
|
updateUI();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
**Process:**
|
||
|
|
|
||
|
|
1. Validates message signature and structure
|
||
|
|
2. Checks for duplicates (message key = `type:id:timestamp`)
|
||
|
|
3. Stores in appropriate cache collection
|
||
|
|
4. Persists to IndexedDB
|
||
|
|
5. Updates last sync timestamp
|
||
|
|
6. Returns whether message was new
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `updateCache(message)`
|
||
|
|
|
||
|
|
Alias for `applyMessage()`. For backward compatibility.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `clear()`
|
||
|
|
|
||
|
|
Clear all in-memory cache (does not affect IndexedDB).
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
clear(): void
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `clearAll()`
|
||
|
|
|
||
|
|
Clear both in-memory cache and all IndexedDB stores.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async clearAll(): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Complete reset
|
||
|
|
await client.database.clearAll();
|
||
|
|
await client.database.open();
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### User Storage
|
||
|
|
|
||
|
|
#### `storeUser(user)` / `loadUser()` / `clearUser()`
|
||
|
|
|
||
|
|
Persist user authentication state.
|
||
|
|
|
||
|
|
**Signatures:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async storeUser(user: User): Promise<void>
|
||
|
|
async loadUser(): Promise<User | null>
|
||
|
|
async clearUser(): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Store current user
|
||
|
|
await client.database.storeUser(currentUser);
|
||
|
|
|
||
|
|
// Load on app start
|
||
|
|
const restoredUser = await client.database.loadUser();
|
||
|
|
if (restoredUser) {
|
||
|
|
console.log('Restored session:', restoredUser.displayName);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clear on logout
|
||
|
|
await client.database.clearUser();
|
||
|
|
```
|
||
|
|
|
||
|
|
**User Expiry:** Stored user expires after 24 hours. `loadUser()` returns `null` if expired.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Delegation Storage
|
||
|
|
|
||
|
|
#### `storeDelegation(delegation)` / `loadDelegation()` / `clearDelegation()`
|
||
|
|
|
||
|
|
Persist delegation information.
|
||
|
|
|
||
|
|
**Signatures:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async storeDelegation(delegation: DelegationInfo): Promise<void>
|
||
|
|
async loadDelegation(): Promise<DelegationInfo | null>
|
||
|
|
async clearDelegation(): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Pending State
|
||
|
|
|
||
|
|
#### `markPending(id)` / `clearPending(id)` / `isPending(id)` / `onPendingChange(listener)`
|
||
|
|
|
||
|
|
Track pending message synchronization for optimistic UI.
|
||
|
|
|
||
|
|
**Signatures:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
markPending(id: string): void
|
||
|
|
clearPending(id: string): void
|
||
|
|
isPending(id: string): boolean
|
||
|
|
onPendingChange(listener: () => void): () => void
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Mark as pending when creating
|
||
|
|
const post = await createPost(...);
|
||
|
|
client.database.markPending(post.id);
|
||
|
|
|
||
|
|
// Show pending indicator
|
||
|
|
if (client.database.isPending(post.id)) {
|
||
|
|
showSyncingBadge(post.id);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Listen for changes
|
||
|
|
const unsubscribe = client.database.onPendingChange(() => {
|
||
|
|
updateAllPendingIndicators();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Clear when confirmed via network
|
||
|
|
client.database.clearPending(post.id);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Sync State
|
||
|
|
|
||
|
|
#### `getSyncState()` / `setSyncing(isSyncing)` / `updateLastSync(timestamp)`
|
||
|
|
|
||
|
|
Manage synchronization state.
|
||
|
|
|
||
|
|
**Signatures:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
getSyncState(): { lastSync: number | null; isSyncing: boolean }
|
||
|
|
setSyncing(isSyncing: boolean): void
|
||
|
|
updateLastSync(timestamp: number): void
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { lastSync, isSyncing } = client.database.getSyncState();
|
||
|
|
|
||
|
|
if (isSyncing) {
|
||
|
|
showSyncSpinner();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (lastSync) {
|
||
|
|
console.log('Last synced:', new Date(lastSync));
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Bookmarks
|
||
|
|
|
||
|
|
#### `addBookmark(bookmark)` / `removeBookmark(id)` / `getUserBookmarks(userId)`
|
||
|
|
|
||
|
|
Manage user bookmarks.
|
||
|
|
|
||
|
|
**Signatures:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async addBookmark(bookmark: Bookmark): Promise<void>
|
||
|
|
async removeBookmark(bookmarkId: string): Promise<void>
|
||
|
|
async getUserBookmarks(userId: string): Promise<Bookmark[]>
|
||
|
|
async getUserBookmarksByType(userId: string, type: 'post' | 'comment'): Promise<Bookmark[]>
|
||
|
|
isBookmarked(userId: string, type: 'post' | 'comment', targetId: string): boolean
|
||
|
|
getBookmark(bookmarkId: string): Bookmark | undefined
|
||
|
|
getAllBookmarks(): Bookmark[]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Add bookmark
|
||
|
|
await client.database.addBookmark({
|
||
|
|
id: `post:${postId}`,
|
||
|
|
type: 'post',
|
||
|
|
targetId: postId,
|
||
|
|
userId: currentUser.address,
|
||
|
|
createdAt: Date.now(),
|
||
|
|
title: post.title,
|
||
|
|
author: post.author,
|
||
|
|
cellId: post.cellId
|
||
|
|
});
|
||
|
|
|
||
|
|
// Check if bookmarked
|
||
|
|
const isBookmarked = client.database.isBookmarked(
|
||
|
|
userId,
|
||
|
|
'post',
|
||
|
|
postId
|
||
|
|
);
|
||
|
|
|
||
|
|
// Get all user bookmarks
|
||
|
|
const bookmarks = await client.database.getUserBookmarks(userId);
|
||
|
|
|
||
|
|
// Remove bookmark
|
||
|
|
await client.database.removeBookmark(`post:${postId}`);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### User Identity Cache
|
||
|
|
|
||
|
|
#### `upsertUserIdentity(address, record)`
|
||
|
|
|
||
|
|
Update user identity in centralized cache.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async upsertUserIdentity(
|
||
|
|
address: string,
|
||
|
|
record: Partial<UserIdentityCache[string]> & { lastUpdated?: number }
|
||
|
|
): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
await client.database.upsertUserIdentity(address, {
|
||
|
|
ensName: 'alice.eth',
|
||
|
|
ensAvatar: 'https://...',
|
||
|
|
verificationStatus: 'ens-verified',
|
||
|
|
lastUpdated: Date.now()
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### UI State
|
||
|
|
|
||
|
|
#### `storeUIState(key, value)` / `loadUIState(key)` / `clearUIState(key)`
|
||
|
|
|
||
|
|
Persist arbitrary UI state to IndexedDB.
|
||
|
|
|
||
|
|
**Signatures:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async storeUIState(key: string, value: unknown): Promise<void>
|
||
|
|
async loadUIState(key: string): Promise<unknown>
|
||
|
|
async clearUIState(key: string): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Store theme preference
|
||
|
|
await client.database.storeUIState('theme', 'dark');
|
||
|
|
|
||
|
|
// Load on app start
|
||
|
|
const theme = await client.database.loadUIState('theme');
|
||
|
|
|
||
|
|
// Clear
|
||
|
|
await client.database.clearUIState('theme');
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ForumActions
|
||
|
|
|
||
|
|
High-level actions for content creation, voting, and moderation.
|
||
|
|
|
||
|
|
### Content Creation
|
||
|
|
|
||
|
|
#### `createCell(params, updateCallback)`
|
||
|
|
|
||
|
|
Create a new cell. Requires ENS-verified wallet.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async createCell(
|
||
|
|
params: {
|
||
|
|
name: string;
|
||
|
|
description: string;
|
||
|
|
icon?: string;
|
||
|
|
currentUser: User | null;
|
||
|
|
isAuthenticated: boolean;
|
||
|
|
},
|
||
|
|
updateCallback: () => void
|
||
|
|
): Promise<{ success: boolean; data?: Cell; error?: string }>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `params.name` - Cell name (required)
|
||
|
|
- `params.description` - Cell description (required)
|
||
|
|
- `params.icon` - Optional emoji/icon for cell
|
||
|
|
- `params.currentUser` - Current user object
|
||
|
|
- `params.isAuthenticated` - Whether user is authenticated
|
||
|
|
- `updateCallback` - Function called when cache is updated (for UI refresh)
|
||
|
|
|
||
|
|
**Returns:** Result object with success status, created cell, or error message
|
||
|
|
|
||
|
|
**Permissions:** Only ENS-verified users can create cells
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const result = await client.forumActions.createCell(
|
||
|
|
{
|
||
|
|
name: 'Tech Discussion',
|
||
|
|
description: 'A place for tech enthusiasts',
|
||
|
|
icon: '💻',
|
||
|
|
currentUser,
|
||
|
|
isAuthenticated: true
|
||
|
|
},
|
||
|
|
() => {
|
||
|
|
// Refresh UI
|
||
|
|
renderCells();
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
console.log('Cell created:', result.data);
|
||
|
|
} else {
|
||
|
|
console.error('Error:', result.error);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `createPost(params, updateCallback)`
|
||
|
|
|
||
|
|
Create a new post in a cell.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async createPost(
|
||
|
|
params: {
|
||
|
|
cellId: string;
|
||
|
|
title: string;
|
||
|
|
content: string;
|
||
|
|
currentUser: User | null;
|
||
|
|
isAuthenticated: boolean;
|
||
|
|
},
|
||
|
|
updateCallback: () => void
|
||
|
|
): Promise<{ success: boolean; data?: Post; error?: string }>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Permissions:** Wallet-connected or anonymous users
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const result = await client.forumActions.createPost(
|
||
|
|
{
|
||
|
|
cellId: 'cell-id',
|
||
|
|
title: 'My First Post',
|
||
|
|
content: 'Hello, OpChan!',
|
||
|
|
currentUser,
|
||
|
|
isAuthenticated: true
|
||
|
|
},
|
||
|
|
() => renderPosts()
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `createComment(params, updateCallback)`
|
||
|
|
|
||
|
|
Add a comment to a post.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async createComment(
|
||
|
|
params: {
|
||
|
|
postId: string;
|
||
|
|
content: string;
|
||
|
|
currentUser: User | null;
|
||
|
|
isAuthenticated: boolean;
|
||
|
|
},
|
||
|
|
updateCallback: () => void
|
||
|
|
): Promise<{ success: boolean; data?: Comment; error?: string }>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Permissions:** Wallet-connected or anonymous users
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const result = await client.forumActions.createComment(
|
||
|
|
{
|
||
|
|
postId: 'post-id',
|
||
|
|
content: 'Great post!',
|
||
|
|
currentUser,
|
||
|
|
isAuthenticated: true
|
||
|
|
},
|
||
|
|
() => renderComments()
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Voting
|
||
|
|
|
||
|
|
#### `vote(params, updateCallback)`
|
||
|
|
|
||
|
|
Vote on a post or comment.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async vote(
|
||
|
|
params: {
|
||
|
|
targetId: string;
|
||
|
|
isUpvote: boolean;
|
||
|
|
currentUser: User | null;
|
||
|
|
isAuthenticated: boolean;
|
||
|
|
},
|
||
|
|
updateCallback: () => void
|
||
|
|
): Promise<{ success: boolean; data?: boolean; error?: string }>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Permissions:** Wallet-connected or anonymous users
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Upvote
|
||
|
|
await client.forumActions.vote(
|
||
|
|
{
|
||
|
|
targetId: postId,
|
||
|
|
isUpvote: true,
|
||
|
|
currentUser,
|
||
|
|
isAuthenticated: true
|
||
|
|
},
|
||
|
|
() => updateVoteCount()
|
||
|
|
);
|
||
|
|
|
||
|
|
// Downvote
|
||
|
|
await client.forumActions.vote(
|
||
|
|
{
|
||
|
|
targetId: commentId,
|
||
|
|
isUpvote: false,
|
||
|
|
currentUser,
|
||
|
|
isAuthenticated: true
|
||
|
|
},
|
||
|
|
() => updateVoteCount()
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Moderation
|
||
|
|
|
||
|
|
#### `moderatePost(params, updateCallback)`
|
||
|
|
|
||
|
|
Moderate a post (hide it).
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async moderatePost(
|
||
|
|
params: {
|
||
|
|
cellId: string;
|
||
|
|
postId: string;
|
||
|
|
reason?: string;
|
||
|
|
currentUser: User | null;
|
||
|
|
isAuthenticated: boolean;
|
||
|
|
cellOwner: string;
|
||
|
|
},
|
||
|
|
updateCallback: () => void
|
||
|
|
): Promise<{ success: boolean; data?: boolean; error?: string }>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Permissions:** Cell owner only
|
||
|
|
|
||
|
|
**Similar Methods:**
|
||
|
|
|
||
|
|
- `unmoderatePost()` - Remove moderation from post
|
||
|
|
- `moderateComment()` - Moderate a comment
|
||
|
|
- `unmoderateComment()` - Remove moderation from comment
|
||
|
|
- `moderateUser()` - Moderate user in a cell
|
||
|
|
- `unmoderateUser()` - Remove user moderation
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const cell = client.database.cache.cells[cellId];
|
||
|
|
|
||
|
|
if (currentUser.address === cell.author) {
|
||
|
|
await client.forumActions.moderatePost(
|
||
|
|
{
|
||
|
|
cellId,
|
||
|
|
postId,
|
||
|
|
reason: 'Spam',
|
||
|
|
currentUser,
|
||
|
|
isAuthenticated: true,
|
||
|
|
cellOwner: cell.author
|
||
|
|
},
|
||
|
|
() => renderPosts()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## UserIdentityService
|
||
|
|
|
||
|
|
Manages user identity resolution, ENS lookup, and profile management.
|
||
|
|
|
||
|
|
### Methods
|
||
|
|
|
||
|
|
#### `getIdentity(address, opts?)`
|
||
|
|
|
||
|
|
Get user identity with ENS resolution and caching.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async getIdentity(
|
||
|
|
address: string,
|
||
|
|
opts?: { fresh?: boolean }
|
||
|
|
): Promise<UserIdentity | null>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `address` - Wallet address or session ID
|
||
|
|
- `opts.fresh` - If `true`, bypass cache and resolve fresh
|
||
|
|
|
||
|
|
**Returns:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface UserIdentity {
|
||
|
|
address: `0x${string}`;
|
||
|
|
ensName?: string;
|
||
|
|
ensAvatar?: string;
|
||
|
|
callSign?: string;
|
||
|
|
displayPreference: EDisplayPreference;
|
||
|
|
displayName: string;
|
||
|
|
lastUpdated: number;
|
||
|
|
verificationStatus: EVerificationStatus;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Cached lookup (fast)
|
||
|
|
const identity = await client.userIdentityService.getIdentity(address);
|
||
|
|
|
||
|
|
// Fresh lookup (bypasses cache)
|
||
|
|
const freshIdentity = await client.userIdentityService.getIdentity(
|
||
|
|
address,
|
||
|
|
{ fresh: true }
|
||
|
|
);
|
||
|
|
|
||
|
|
if (identity) {
|
||
|
|
console.log('Display name:', identity.displayName);
|
||
|
|
console.log('ENS name:', identity.ensName);
|
||
|
|
console.log('Call sign:', identity.callSign);
|
||
|
|
console.log('Verification:', identity.verificationStatus);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Caching Strategy:**
|
||
|
|
|
||
|
|
1. Checks LocalDatabase cache first
|
||
|
|
2. If not found or stale, resolves from ENS
|
||
|
|
3. Stores result in cache
|
||
|
|
4. Returns identity
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `setPublicClient(publicClient)`
|
||
|
|
|
||
|
|
Set viem PublicClient for ENS resolution.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
setPublicClient(publicClient: PublicClient): void
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { createPublicClient, http } from 'viem';
|
||
|
|
import { mainnet } from 'viem/chains';
|
||
|
|
|
||
|
|
const publicClient = createPublicClient({
|
||
|
|
chain: mainnet,
|
||
|
|
transport: http()
|
||
|
|
});
|
||
|
|
|
||
|
|
client.userIdentityService.setPublicClient(publicClient);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `updateProfile(address, updates)`
|
||
|
|
|
||
|
|
Update user profile (call sign and display preference).
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async updateProfile(
|
||
|
|
address: string,
|
||
|
|
updates: {
|
||
|
|
callSign?: string;
|
||
|
|
displayPreference?: EDisplayPreference;
|
||
|
|
}
|
||
|
|
): Promise<{ ok: true; identity: UserIdentity } | { ok: false; error: Error }>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const result = await client.userIdentityService.updateProfile(
|
||
|
|
userAddress,
|
||
|
|
{
|
||
|
|
callSign: 'alice',
|
||
|
|
displayPreference: EDisplayPreference.CALL_SIGN
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
if (result.ok) {
|
||
|
|
console.log('Profile updated:', result.identity);
|
||
|
|
} else {
|
||
|
|
console.error('Failed:', result.error);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Process:**
|
||
|
|
|
||
|
|
1. Creates USER_PROFILE_UPDATE message
|
||
|
|
2. Signs with delegated key
|
||
|
|
3. Broadcasts to network
|
||
|
|
4. Updates LocalDatabase
|
||
|
|
5. Returns updated identity
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `getDisplayName(params)`
|
||
|
|
|
||
|
|
Get display name for a user based on their preferences.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
getDisplayName({
|
||
|
|
address: string,
|
||
|
|
ensName?: string | null,
|
||
|
|
displayPreference?: EDisplayPreference
|
||
|
|
}): string
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const displayName = client.userIdentityService.getDisplayName({
|
||
|
|
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
|
||
|
|
ensName: 'alice.eth',
|
||
|
|
displayPreference: EDisplayPreference.CALL_SIGN
|
||
|
|
});
|
||
|
|
|
||
|
|
// If user has call sign and preference is CALL_SIGN: "alice"
|
||
|
|
// If user has ENS: "alice.eth"
|
||
|
|
// Otherwise: "0x742d...0bEb"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `subscribe(listener)`
|
||
|
|
|
||
|
|
Subscribe to identity changes.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
subscribe(
|
||
|
|
listener: (address: string, identity: UserIdentity | null) => void
|
||
|
|
): () => void
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** Unsubscribe function
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const unsubscribe = client.userIdentityService.subscribe(
|
||
|
|
(address, identity) => {
|
||
|
|
console.log('Identity updated:', address);
|
||
|
|
if (identity) {
|
||
|
|
updateUserDisplay(address, identity.displayName);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
// Clean up
|
||
|
|
unsubscribe();
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `getAll()`
|
||
|
|
|
||
|
|
Get all cached user identities.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
getAll(): UserIdentity[]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `refreshIdentity(address)`
|
||
|
|
|
||
|
|
Force refresh of a user's identity.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async refreshIdentity(address: string): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## RelevanceCalculator
|
||
|
|
|
||
|
|
Calculates content relevance scores based on multiple factors.
|
||
|
|
|
||
|
|
### Method
|
||
|
|
|
||
|
|
#### `calculatePostScore(post, votes, comments, userVerificationStatus, moderatedPosts)`
|
||
|
|
|
||
|
|
Calculate relevance score for a post.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
calculatePostScore(
|
||
|
|
post: PostMessage,
|
||
|
|
votes: VoteMessage[],
|
||
|
|
comments: CommentMessage[],
|
||
|
|
userVerificationStatus: UserVerificationStatus,
|
||
|
|
moderatedPosts: { [postId: string]: ModerateMessage }
|
||
|
|
): RelevanceScoreDetails
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `post` - Post message to score
|
||
|
|
- `votes` - All votes for this post
|
||
|
|
- `comments` - All comments on this post
|
||
|
|
- `userVerificationStatus` - Verification status of all users (for bonuses)
|
||
|
|
- `moderatedPosts` - Moderation records
|
||
|
|
|
||
|
|
**Returns:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface RelevanceScoreDetails {
|
||
|
|
baseScore: number; // 100
|
||
|
|
engagementScore: number; // upvotes*10 + comments*3
|
||
|
|
authorVerificationBonus: number; // +20 if ENS verified
|
||
|
|
verifiedUpvoteBonus: number; // +5 per verified upvoter
|
||
|
|
verifiedCommenterBonus: number; // +10 per verified commenter
|
||
|
|
timeDecayMultiplier: number; // Exponential decay (half-life 7 days)
|
||
|
|
moderationPenalty: number; // -50% if moderated
|
||
|
|
finalScore: number; // Combined total
|
||
|
|
isVerified: boolean;
|
||
|
|
upvotes: number;
|
||
|
|
comments: number;
|
||
|
|
verifiedUpvotes: number;
|
||
|
|
verifiedCommenters: number;
|
||
|
|
daysOld: number;
|
||
|
|
isModerated: boolean;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Scoring Formula:**
|
||
|
|
|
||
|
|
```
|
||
|
|
base = 100
|
||
|
|
engagement = (upvotes * 10) + (comments * 3)
|
||
|
|
verification = (author ENS ? 20 : 0) +
|
||
|
|
(verified upvoters * 5) +
|
||
|
|
(verified commenters * 10)
|
||
|
|
timeDecay = exp(-0.693 * daysOld / 7)
|
||
|
|
moderation = isModerated ? 0.5 : 1.0
|
||
|
|
|
||
|
|
finalScore = (base + engagement + verification) * timeDecay * moderation
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const post = client.database.cache.posts['post-id'];
|
||
|
|
const votes = Object.values(client.database.cache.votes)
|
||
|
|
.filter(v => v.targetId === post.id);
|
||
|
|
const comments = Object.values(client.database.cache.comments)
|
||
|
|
.filter(c => c.postId === post.id);
|
||
|
|
|
||
|
|
const userVerificationStatus = {};
|
||
|
|
for (const [addr, identity] of Object.entries(client.database.cache.userIdentities)) {
|
||
|
|
userVerificationStatus[addr] = {
|
||
|
|
isVerified: identity.verificationStatus === 'ens-verified',
|
||
|
|
hasENS: !!identity.ensName,
|
||
|
|
ensName: identity.ensName
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
const score = client.relevance.calculatePostScore(
|
||
|
|
post,
|
||
|
|
votes,
|
||
|
|
comments,
|
||
|
|
userVerificationStatus,
|
||
|
|
client.database.cache.moderations
|
||
|
|
);
|
||
|
|
|
||
|
|
console.log('Final score:', score.finalScore);
|
||
|
|
console.log('Breakdown:', {
|
||
|
|
base: score.baseScore,
|
||
|
|
engagement: score.engagementScore,
|
||
|
|
verification: score.authorVerificationBonus +
|
||
|
|
score.verifiedUpvoteBonus +
|
||
|
|
score.verifiedCommenterBonus,
|
||
|
|
timeDecay: score.timeDecayMultiplier,
|
||
|
|
moderation: score.moderationPenalty
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## MessageManager
|
||
|
|
|
||
|
|
Manages Waku network connectivity and message transmission.
|
||
|
|
|
||
|
|
### Properties
|
||
|
|
|
||
|
|
#### `isReady: boolean`
|
||
|
|
|
||
|
|
Whether Waku node is ready to send/receive messages.
|
||
|
|
|
||
|
|
#### `currentHealth: HealthStatus`
|
||
|
|
|
||
|
|
Current network health status.
|
||
|
|
|
||
|
|
### Methods
|
||
|
|
|
||
|
|
#### `sendMessage(message, statusCallback?)`
|
||
|
|
|
||
|
|
Send a message via Waku network.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async sendMessage(
|
||
|
|
message: OpchanMessage,
|
||
|
|
statusCallback?: (status: MessageStatus) => void
|
||
|
|
): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const signed = await client.delegation.signMessage(unsignedMessage);
|
||
|
|
if (signed) {
|
||
|
|
await client.messageManager.sendMessage(signed, (status) => {
|
||
|
|
console.log('Message status:', status);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `onMessageReceived(callback)`
|
||
|
|
|
||
|
|
Subscribe to incoming messages.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
onMessageReceived(
|
||
|
|
callback: (message: OpchanMessage) => void
|
||
|
|
): () => void
|
||
|
|
```
|
||
|
|
|
||
|
|
**Returns:** Unsubscribe function
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const unsubscribe = client.messageManager.onMessageReceived(
|
||
|
|
async (message) => {
|
||
|
|
await client.database.applyMessage(message);
|
||
|
|
console.log('Received:', message.type);
|
||
|
|
}
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `onHealthChange(callback)`
|
||
|
|
|
||
|
|
Monitor network health changes.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
onHealthChange(
|
||
|
|
callback: (isHealthy: boolean) => void
|
||
|
|
): () => void
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
client.messageManager.onHealthChange((isHealthy) => {
|
||
|
|
if (isHealthy) {
|
||
|
|
showConnectedIndicator();
|
||
|
|
} else {
|
||
|
|
showDisconnectedIndicator();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `onSyncStatus(callback)`
|
||
|
|
|
||
|
|
Monitor synchronization status.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
onSyncStatus(
|
||
|
|
callback: (status: SyncStatus) => void
|
||
|
|
): () => void
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## BookmarkService
|
||
|
|
|
||
|
|
Service for managing user bookmarks (posts and comments).
|
||
|
|
|
||
|
|
### Methods
|
||
|
|
|
||
|
|
#### `addPostBookmark(post, userId, cellId?)`
|
||
|
|
|
||
|
|
Add a post to bookmarks.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async addPostBookmark(
|
||
|
|
post: Post,
|
||
|
|
userId: string,
|
||
|
|
cellId?: string
|
||
|
|
): Promise<Bookmark>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `addCommentBookmark(comment, userId, postId?)`
|
||
|
|
|
||
|
|
Add a comment to bookmarks.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async addCommentBookmark(
|
||
|
|
comment: Comment,
|
||
|
|
userId: string,
|
||
|
|
postId?: string
|
||
|
|
): Promise<Bookmark>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `removeBookmark(bookmarkId)`
|
||
|
|
|
||
|
|
Remove a bookmark by ID.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async removeBookmark(bookmarkId: string): Promise<void>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { BookmarkService } from '@opchan/core';
|
||
|
|
|
||
|
|
const bookmarkService = new BookmarkService();
|
||
|
|
|
||
|
|
// Add post bookmark
|
||
|
|
await bookmarkService.addPostBookmark(post, userId, cellId);
|
||
|
|
|
||
|
|
// Check if bookmarked
|
||
|
|
const isBookmarked = client.database.isBookmarked(userId, 'post', postId);
|
||
|
|
|
||
|
|
// Remove bookmark
|
||
|
|
await bookmarkService.removeBookmark(`post:${postId}`);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## MessageValidator
|
||
|
|
|
||
|
|
Validates message signatures and structure.
|
||
|
|
|
||
|
|
### Methods
|
||
|
|
|
||
|
|
#### `isValidMessage(message)`
|
||
|
|
|
||
|
|
Check if message is valid.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async isValidMessage(message: unknown): Promise<boolean>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `getValidationReport(message)`
|
||
|
|
|
||
|
|
Get detailed validation report.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async getValidationReport(message: unknown): Promise<{
|
||
|
|
isValid: boolean;
|
||
|
|
missingFields: string[];
|
||
|
|
invalidFields: string[];
|
||
|
|
hasValidSignature: boolean;
|
||
|
|
errors: string[];
|
||
|
|
warnings: string[];
|
||
|
|
}>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { MessageValidator } from '@opchan/core';
|
||
|
|
|
||
|
|
const validator = new MessageValidator();
|
||
|
|
|
||
|
|
const report = await validator.getValidationReport(message);
|
||
|
|
|
||
|
|
if (!report.isValid) {
|
||
|
|
console.error('Validation failed:');
|
||
|
|
console.error('Missing fields:', report.missingFields);
|
||
|
|
console.error('Invalid fields:', report.invalidFields);
|
||
|
|
console.error('Errors:', report.errors);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Type Definitions
|
||
|
|
|
||
|
|
### Enums
|
||
|
|
|
||
|
|
#### `EVerificationStatus`
|
||
|
|
|
||
|
|
User verification levels.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
enum EVerificationStatus {
|
||
|
|
ANONYMOUS = 'anonymous',
|
||
|
|
WALLET_UNCONNECTED = 'wallet-unconnected',
|
||
|
|
WALLET_CONNECTED = 'wallet-connected',
|
||
|
|
ENS_VERIFIED = 'ens-verified',
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `EDisplayPreference`
|
||
|
|
|
||
|
|
User display name preference.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
enum EDisplayPreference {
|
||
|
|
CALL_SIGN = 'call-sign',
|
||
|
|
WALLET_ADDRESS = 'wallet-address',
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `MessageType`
|
||
|
|
|
||
|
|
Message types in the protocol.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
enum MessageType {
|
||
|
|
CELL = 'cell',
|
||
|
|
POST = 'post',
|
||
|
|
COMMENT = 'comment',
|
||
|
|
VOTE = 'vote',
|
||
|
|
MODERATE = 'moderate',
|
||
|
|
USER_PROFILE_UPDATE = 'user_profile_update',
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `EModerationAction`
|
||
|
|
|
||
|
|
Moderation actions.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
enum EModerationAction {
|
||
|
|
MODERATE = 'moderate',
|
||
|
|
UNMODERATE = 'unmoderate',
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Core Interfaces
|
||
|
|
|
||
|
|
#### `User`
|
||
|
|
|
||
|
|
User object representing authenticated or anonymous user.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface User {
|
||
|
|
address: string; // 0x${string} for wallet, UUID for anonymous
|
||
|
|
ensName?: string;
|
||
|
|
ensAvatar?: string;
|
||
|
|
callSign?: string;
|
||
|
|
displayPreference: EDisplayPreference;
|
||
|
|
displayName: string;
|
||
|
|
verificationStatus: EVerificationStatus;
|
||
|
|
lastChecked?: number;
|
||
|
|
browserPubKey?: string;
|
||
|
|
delegationSignature?: string;
|
||
|
|
delegationExpiry?: number;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `Cell`
|
||
|
|
|
||
|
|
Extended cell with computed fields.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface Cell extends CellMessage {
|
||
|
|
relevanceScore?: number;
|
||
|
|
activeMemberCount?: number;
|
||
|
|
recentActivity?: number;
|
||
|
|
postCount?: number;
|
||
|
|
relevanceDetails?: RelevanceScoreDetails;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `Post`
|
||
|
|
|
||
|
|
Extended post with votes and moderation.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface Post extends PostMessage {
|
||
|
|
authorAddress: string;
|
||
|
|
upvotes: VoteMessage[];
|
||
|
|
downvotes: VoteMessage[];
|
||
|
|
moderated?: boolean;
|
||
|
|
moderatedBy?: string;
|
||
|
|
moderationReason?: string;
|
||
|
|
moderationTimestamp?: number;
|
||
|
|
relevanceScore?: number;
|
||
|
|
verifiedUpvotes?: number;
|
||
|
|
verifiedCommenters?: string[];
|
||
|
|
relevanceDetails?: RelevanceScoreDetails;
|
||
|
|
voteScore?: number;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `Comment`
|
||
|
|
|
||
|
|
Extended comment with votes and moderation.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface Comment extends CommentMessage {
|
||
|
|
authorAddress: string;
|
||
|
|
upvotes: VoteMessage[];
|
||
|
|
downvotes: VoteMessage[];
|
||
|
|
moderated?: boolean;
|
||
|
|
moderatedBy?: string;
|
||
|
|
moderationReason?: string;
|
||
|
|
moderationTimestamp?: number;
|
||
|
|
relevanceScore?: number;
|
||
|
|
relevanceDetails?: RelevanceScoreDetails;
|
||
|
|
voteScore?: number;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `Bookmark`
|
||
|
|
|
||
|
|
Bookmark data structure.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface Bookmark {
|
||
|
|
id: string; // `${type}:${targetId}`
|
||
|
|
type: BookmarkType;
|
||
|
|
targetId: string;
|
||
|
|
userId: string;
|
||
|
|
createdAt: number;
|
||
|
|
title?: string;
|
||
|
|
author?: string;
|
||
|
|
cellId?: string;
|
||
|
|
postId?: string;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Message Types
|
||
|
|
|
||
|
|
#### `OpchanMessage`
|
||
|
|
|
||
|
|
Union type of all signed message types.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
type OpchanMessage = (
|
||
|
|
| CellMessage
|
||
|
|
| PostMessage
|
||
|
|
| CommentMessage
|
||
|
|
| VoteMessage
|
||
|
|
| ModerateMessage
|
||
|
|
| UserProfileUpdateMessage
|
||
|
|
) & SignedMessage;
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `SignedMessage`
|
||
|
|
|
||
|
|
Signature fields present on all messages.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface SignedMessage {
|
||
|
|
signature: string; // Ed25519 signature
|
||
|
|
browserPubKey: string; // Browser public key
|
||
|
|
delegationProof?: DelegationProof; // Optional for anonymous
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### `DelegationProof`
|
||
|
|
|
||
|
|
Proof that browser key was authorized by wallet.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface DelegationProof {
|
||
|
|
authMessage: string; // Message signed by wallet
|
||
|
|
walletSignature: string; // Wallet's signature
|
||
|
|
expiryTimestamp: number; // When delegation expires
|
||
|
|
walletAddress: string; // Wallet that signed
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Utility Functions
|
||
|
|
|
||
|
|
### `transformPost(postMessage)`
|
||
|
|
|
||
|
|
Transform raw post message to enhanced Post type.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async transformPost(postMessage: PostMessage): Promise<Post | null>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### `transformComment(commentMessage)`
|
||
|
|
|
||
|
|
Transform raw comment message to enhanced Comment type.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async transformComment(commentMessage: CommentMessage): Promise<Comment | null>
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### `transformCell(cellMessage)`
|
||
|
|
|
||
|
|
Transform raw cell message to enhanced Cell type.
|
||
|
|
|
||
|
|
**Signature:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async transformCell(cellMessage: CellMessage): Promise<Cell | null>
|
||
|
|
```
|
||
|
|
|
||
|
|
**Example:**
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { transformPost, transformComment, transformCell } from '@opchan/core';
|
||
|
|
|
||
|
|
// Transform messages
|
||
|
|
const post = await transformPost(postMessage);
|
||
|
|
const comment = await transformComment(commentMessage);
|
||
|
|
const cell = await transformCell(cellMessage);
|
||
|
|
|
||
|
|
// Use enhanced types
|
||
|
|
if (post) {
|
||
|
|
console.log('Upvotes:', post.upvotes.length);
|
||
|
|
console.log('Downvotes:', post.downvotes.length);
|
||
|
|
console.log('Vote score:', post.voteScore);
|
||
|
|
console.log('Relevance:', post.relevanceScore);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Complete Type Reference
|
||
|
|
|
||
|
|
For complete TypeScript type definitions, see:
|
||
|
|
|
||
|
|
- `packages/core/src/types/forum.ts` - Forum-specific types
|
||
|
|
- `packages/core/src/types/identity.ts` - Identity and user types
|
||
|
|
- `packages/core/src/types/waku.ts` - Message and network types
|
||
|
|
- `packages/core/src/lib/delegation/types.ts` - Delegation types
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**End of API Reference**
|
||
|
|
|