Add vitepress docs
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# Connection
|
||||
|
||||
The `Connection` class is the core building block for relay communication in `@welshman/net`. It manages the complete lifecycle of a relay connection, including socket handling, message queuing, authentication, and statistics tracking.
|
||||
|
||||
## Overview
|
||||
|
||||
A Connection handles:
|
||||
- WebSocket lifecycle
|
||||
- Message queuing and throttling
|
||||
- Connection state tracking
|
||||
- Relay authentication
|
||||
- Connection statistics
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
import {Connection} from '@welshman/net'
|
||||
|
||||
// Create connection
|
||||
const connection = new Connection("wss://relay.example.com")
|
||||
|
||||
// Listen for events
|
||||
connection.on('event', (conn, subId, event) => {
|
||||
console.log(`Got event from ${conn.url}`)
|
||||
})
|
||||
|
||||
// Send a subscription
|
||||
connection.send(["REQ", "my-sub", {kinds: [1], limit: 10}])
|
||||
|
||||
// Clean up when done
|
||||
connection.cleanup()
|
||||
```
|
||||
|
||||
## Handling Authentication
|
||||
|
||||
The `connection.open()` promise resolves when the WebSocket connection is fully established and ready for communication.
|
||||
However, it's important to understand the authentication flow:
|
||||
|
||||
```typescript
|
||||
import {Connection} from '@welshman/net'
|
||||
|
||||
const connection = new Connection("wss://relay.example.com")
|
||||
|
||||
// Basic open
|
||||
await connection.open()
|
||||
// Promise resolves when WebSocket is connected
|
||||
// BUT might not be auth-ready yet!
|
||||
|
||||
// Complete open with auth handling
|
||||
const openRelay = async (url: string) => {
|
||||
const connection = new Connection(url)
|
||||
|
||||
// Open socket
|
||||
await connection.open()
|
||||
|
||||
// Check if relay requires auth
|
||||
if (connection.auth.status === 'requested') {
|
||||
try {
|
||||
// Handle auth challenge
|
||||
await connection.auth.attempt(3000) // 3s timeout
|
||||
} catch (e) {
|
||||
console.error('Auth failed:', e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// NOW connection is fully ready
|
||||
return connection
|
||||
}
|
||||
```
|
||||
|
||||
The key states after `open()` resolves:
|
||||
- Socket is connected
|
||||
- Messages can be queued
|
||||
- BUT relay might request authentication
|
||||
- AND authentication might fail
|
||||
|
||||
Always check `connection.auth.status` if you need to ensure the connection is fully authenticated before use.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Context
|
||||
|
||||
The Context system is the backbone of `@welshman/net`, providing global configuration and shared services that are essential for the package's operation. It defines how events are handled, validated, and routed throughout the application.
|
||||
|
||||
## Overview
|
||||
|
||||
- Global connection pool (`ctx.net.pool`)
|
||||
- Event validation (`ctx.net.isValid`)
|
||||
- Event handling (`ctx.net.onEvent`)
|
||||
- Deletion tracking (`ctx.net.isDeleted`)
|
||||
- Event signing (`ctx.net.signEvent`)
|
||||
- Subscription optimization (`ctx.net.optimizeSubscriptions`)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
import {ctx, setContext} from '@welshman/lib'
|
||||
import {
|
||||
getDefaultNetContext,
|
||||
Pool,
|
||||
hasValidSignature
|
||||
} from '@welshman/net'
|
||||
|
||||
// Setup networking context
|
||||
setContext({
|
||||
net: getDefaultNetContext({
|
||||
// Use shared pool
|
||||
pool: new Pool(),
|
||||
|
||||
// Track events
|
||||
onEvent: (url, event) => {
|
||||
tracker.track(event.id, url)
|
||||
repository.publish(event)
|
||||
},
|
||||
|
||||
// Validate based on source
|
||||
isValid: (url, event) => {
|
||||
// Trust local relay
|
||||
if (url === LOCAL_RELAY_URL) return true
|
||||
|
||||
// Validate signature for remote events
|
||||
return hasValidSignature(event)
|
||||
},
|
||||
|
||||
// Check deletion status
|
||||
isDeleted: (url, event) =>
|
||||
repository.isDeleted(event),
|
||||
|
||||
// Sign with current user
|
||||
signEvent: async (event) =>
|
||||
signer.get().sign(event)
|
||||
})
|
||||
})
|
||||
|
||||
// Now all package features will use these settings
|
||||
subscribe(/*...*/) // Uses pool, validates events
|
||||
publish(/*...*/) // Uses signEvent
|
||||
```
|
||||
|
||||
The Context is used internally by most features in the package.
|
||||
Without proper context configuration, core features like subscription, publishing, and event validation won't work correctly.
|
||||
|
||||
Think of it as the central configuration that defines how your nostr networking behaves.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Executor
|
||||
|
||||
The Executor class orchestrates event delivery and subscription management across one or more [targets](/net/targets.md). It abstracts the complexity of handling multiple connections into a single interface.
|
||||
|
||||
## Overview
|
||||
|
||||
The Executor:
|
||||
- Manages subscriptions
|
||||
- Handles event publishing
|
||||
- Supports NIP-77 (negentropy)
|
||||
- Routes messages to appropriate targets
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
import {Executor, Relays} from '@welshman/net'
|
||||
|
||||
// Create executor with relay target
|
||||
const executor = new Executor(
|
||||
new Relays([
|
||||
connection1,
|
||||
connection2
|
||||
])
|
||||
)
|
||||
|
||||
// Subscribe to events
|
||||
const sub = executor.subscribe(
|
||||
[{kinds: [1], limit: 10}],
|
||||
{
|
||||
onEvent: (url, event) => {
|
||||
console.log(`Got event from ${url}`, event)
|
||||
},
|
||||
onEose: (url) => {
|
||||
console.log(`EOSE from ${url}`)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Publish event
|
||||
const pub = executor.publish(
|
||||
signedEvent,
|
||||
{
|
||||
onOk: (url, id, success, message) => {
|
||||
console.log(`Published to ${url}: ${success ? 'OK' : message}`)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Clean up
|
||||
sub.unsubscribe()
|
||||
executor.target.cleanup()
|
||||
```
|
||||
|
||||
The Executor is used internally by higher-level APIs but can be used directly when you need fine-grained control over event routing and subscription management.
|
||||
|
||||
It's particularly useful when implementing custom targets or handling special relay configurations (like local relays or relay groups).
|
||||
@@ -0,0 +1,19 @@
|
||||
# @welshman/net
|
||||
|
||||
Core networking layer for nostr applications, handling relay connections, message management, and event delivery.
|
||||
|
||||
## What's Included
|
||||
|
||||
- **Connection Management** - WebSocket lifecycle and relay connections
|
||||
- **Subscription System** - Event filtering and subscription handling
|
||||
- **Publishing Tools** - Event broadcasting with status tracking
|
||||
- **Sync Utilities** - NIP-77 (negentropy) event synchronization
|
||||
- **Connection Pool** - Shared relay connection management
|
||||
- **Targets** - Flexible message routing strategies
|
||||
- **Event Tracking** - Monitor which relays have seen events
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @welshman/net
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# Pool
|
||||
|
||||
The Pool class manages a collection of relay connections, providing a centralized way to track and reuse connections across your application.
|
||||
|
||||
## Overview
|
||||
|
||||
- Creates and caches connections
|
||||
- Ensures single connection per relay
|
||||
- Handles cleanup of unused connections
|
||||
- Provides connection lookup
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import {Pool} from '@welshman/net'
|
||||
|
||||
// Create pool
|
||||
const pool = new Pool()
|
||||
|
||||
// Get or create connection
|
||||
const connection = pool.get("wss://relay.example.com")
|
||||
|
||||
// Check if relay is in pool
|
||||
if (pool.has("wss://relay.example.com")) {
|
||||
// Use existing connection
|
||||
}
|
||||
|
||||
// Remove connection
|
||||
pool.remove("wss://relay.example.com")
|
||||
|
||||
// Clear all connections
|
||||
pool.clear()
|
||||
```
|
||||
|
||||
|
||||
The Pool is typically used internally by the router and executor, but can be used directly for custom connection management.
|
||||
It ensures efficient connection reuse across your application.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Publish
|
||||
|
||||
The `Publish` class handles event publishing to relays, managing publish status, relay responses, and error handling.
|
||||
|
||||
## Overview
|
||||
|
||||
- Sends events to relays
|
||||
- Tracks publish status per relay
|
||||
- Handles OK/Error responses
|
||||
- Manages timeouts
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
import {`Publish`, `Publish`Status} from '@welshman/net'
|
||||
|
||||
const {Pending, Success, Failure, Timeout, Aborted} = `Publish`Status
|
||||
|
||||
// Basic `Publish`
|
||||
const pub = `Publish`({
|
||||
event: signedEvent,
|
||||
relays: ["wss://relay.example.com"],
|
||||
timeout: 3000 // 3s timeout
|
||||
})
|
||||
|
||||
// Track status
|
||||
pub.emitter.on('*', (status: `Publish`Status, url: string, message?: string) => {
|
||||
switch (status) {
|
||||
case Success:
|
||||
console.log(``Publish`ed to ${url}`)
|
||||
break
|
||||
case Failure:
|
||||
console.log(`Failed on ${url}: ${message}`)
|
||||
break
|
||||
case Timeout:
|
||||
console.log(`Timeout on ${url}`)
|
||||
break
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Real World Example
|
||||
|
||||
```typescript
|
||||
const publishWithStatus = async (event: SignedEvent) => {
|
||||
const pub = `Publish`({
|
||||
event,
|
||||
relays: ctx.app.router
|
||||
.FromUser()
|
||||
.getUrls(),
|
||||
timeout: 5000
|
||||
})
|
||||
|
||||
// Track per-relay status
|
||||
const status = new Map<string, string>()
|
||||
|
||||
pub.emitter.on('*', (state: `Publish`Status, url: string) => {
|
||||
status.set(url, state)
|
||||
|
||||
// Log progress
|
||||
const counts = {
|
||||
pending: 0,
|
||||
success: 0,
|
||||
failed: 0
|
||||
}
|
||||
|
||||
for (const s of status.values()) {
|
||||
counts[s] = (counts[s] || 0) + 1
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Progress: ${counts.success}/${status.size}`,
|
||||
`(${counts.failed} failed)`
|
||||
)
|
||||
})
|
||||
|
||||
// Wait for completion
|
||||
return pub.result
|
||||
}
|
||||
```
|
||||
|
||||
Like [Subscribe](/net/subscribe.md), `Publish` uses [Pool](/net/pool.md) for connections and creates appropriate [Targets](/net/targets.md) via an [Executor](/net/executor.md), but focuses on event publishing rather than subscription management.
|
||||
|
||||
Note: The base `@welshman/net` Publish class just handles network publishing.
|
||||
For optimistic updates and repository integration, use Publish from `@welshman/app`.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Socket
|
||||
The Socket class is exclusively used by the `Connection` class as its low-level WebSocket manager. It's not meant to be used directly by other classes.
|
||||
Its sole purpose is to provide a reliable, manageable WebSocket connection with nostr-specific handling.
|
||||
|
||||
## Core Responsibilities
|
||||
|
||||
```typescript
|
||||
export class Socket {
|
||||
// Track connection state
|
||||
status: SocketStatus = "new" | "open" | "opening" | "closing" | "closed" | "error"
|
||||
|
||||
// Handle nostr message queue
|
||||
worker: Worker<Message>
|
||||
|
||||
// Core operations
|
||||
open = async () => {/* Initialize WebSocket */}
|
||||
close = async () => {/* Clean shutdown */}
|
||||
send = async (message: Message) => {/* Send with JSON serialization */}
|
||||
}
|
||||
```
|
||||
|
||||
Key features:
|
||||
- State tracking
|
||||
- Message queuing
|
||||
- JSON serialization
|
||||
- Error recovery
|
||||
- Connection lifecycle
|
||||
|
||||
Think of it as a thin wrapper that turns raw WebSocket connections into something more suitable for nostr:
|
||||
```typescript
|
||||
// Raw WebSocket
|
||||
ws.send(JSON.stringify(["REQ", "sub1", {kinds: [1]}]))
|
||||
|
||||
// With Socket
|
||||
socket.send(["REQ", "sub1", {kinds: [1]}]) // Handles serialization
|
||||
```
|
||||
|
||||
## Usage Chain
|
||||
|
||||
```typescript
|
||||
// Hierarchy
|
||||
Socket // WebSocket management
|
||||
↳ Connection // Uses Socket
|
||||
↳ Relay Target // Uses Connection
|
||||
↳ Executor // Uses Target
|
||||
↳ Subscribe // Uses Executor
|
||||
↳ Publish // Uses Executor
|
||||
|
||||
// In Connection.ts
|
||||
export class Connection extends Emitter {
|
||||
socket: Socket
|
||||
|
||||
constructor(url: string) {
|
||||
this.socket = new Socket(this)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It's an internal implementation detail that you shouldn't need to use directly - always interact with the `Connection` class instead, which provides a higher-level interface.
|
||||
|
||||
```typescript
|
||||
// DON'T use Socket directly
|
||||
const socket = new Socket(/*...*/) // ❌
|
||||
|
||||
// DO use Connection
|
||||
const connection = new Connection(url) // ✅
|
||||
```
|
||||
|
||||
This encapsulation ensures consistent connection management across the library.
|
||||
@@ -0,0 +1,115 @@
|
||||
# Subscribe
|
||||
|
||||
The Subscribe class manages nostr subscriptions, handling subscription lifecycle, event filtering, and relay responses. It provides a unified interface for subscribing to events across multiple relays.
|
||||
|
||||
## Overview
|
||||
|
||||
The Subscription:
|
||||
- Manages REQ/CLOSE lifecycle
|
||||
- Handles EOSE responses
|
||||
- Emits filtered events
|
||||
- Tracks completion state
|
||||
|
||||
```typescript
|
||||
import {subscribe, SubscriptionEvent} from '@welshman/net'
|
||||
|
||||
// Create subscription
|
||||
const sub = subscribe({
|
||||
filters: [{kinds: [1], limit: 10}],
|
||||
relays: ["wss://relay.example.com"],
|
||||
|
||||
// Optional configurations
|
||||
closeOnEose: true, // Close after all relays send EOSE
|
||||
timeout: 3000, // Max time to wait
|
||||
authTimeout: 300, // Time for auth negotiation
|
||||
delay: 50 // Delay between batched requests
|
||||
})
|
||||
|
||||
// Handle events
|
||||
sub.on(SubscriptionEvent.Event, (url, event) => {
|
||||
console.log(`Got event from ${url}:`, event)
|
||||
})
|
||||
|
||||
sub.on(SubscriptionEvent.Eose, (url) => {
|
||||
console.log(`Got EOSE from ${url}`)
|
||||
})
|
||||
|
||||
sub.on(SubscriptionEvent.Complete, () => {
|
||||
console.log('Subscription complete')
|
||||
})
|
||||
|
||||
// Close when done
|
||||
sub.close()
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```typescript
|
||||
import {subscribe, Pool, Executor, Relays} from '@welshman/net'
|
||||
|
||||
// Under the hood, subscribe:
|
||||
// 1. Gets connections from global pool
|
||||
// 2. Creates a target (usually Relays)
|
||||
// 3. Uses Executor to manage subscription
|
||||
|
||||
// This is roughly equivalent to:
|
||||
const manualSubscribe = (urls: string[]) => {
|
||||
// Get connections from pool
|
||||
const connections = urls.map(url =>
|
||||
ctx.net.pool.get(url)
|
||||
)
|
||||
|
||||
// Create target
|
||||
const target = new Relays(connections)
|
||||
|
||||
// Create executor
|
||||
const executor = new Executor(target)
|
||||
|
||||
// Subscribe via executor
|
||||
return executor.subscribe(
|
||||
[{kinds: [1], limit: 10}],
|
||||
{
|
||||
onEvent: (url, event) => {
|
||||
console.log(`Got event from ${url}`)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Real World Example
|
||||
|
||||
```typescript
|
||||
// Combine local and remote relays
|
||||
const loadProfile = async (pubkey: string) => {
|
||||
// Get optimal relays
|
||||
const relays = ctx.app.router
|
||||
.ForPubkey(pubkey)
|
||||
.getUrls()
|
||||
|
||||
const sub = subscribe({
|
||||
filters: [{
|
||||
kinds: [0],
|
||||
authors: [pubkey],
|
||||
limit: 1
|
||||
}],
|
||||
relays,
|
||||
// This creates internally:
|
||||
// 1. Connections via Pool
|
||||
// 2. Multi target with Local + Relays
|
||||
// 3. Executor to manage subscription
|
||||
})
|
||||
|
||||
return new Promise(resolve => {
|
||||
sub.on('event', (url, event) => {
|
||||
resolve(event)
|
||||
sub.close()
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
The Subscribe class abstracts away:
|
||||
- Connection management (via Pool)
|
||||
- Target creation and setup
|
||||
- Executor orchestration
|
||||
@@ -0,0 +1,70 @@
|
||||
# Sync
|
||||
|
||||
The Sync utilities in `@welshman/net` provide methods for synchronizing events between relays and repositories, primarily using NIP-77 (Negentropy) when available, with fallback to traditional sync methods.
|
||||
|
||||
## Overview
|
||||
|
||||
```typescript
|
||||
import {sync, pull, push} from '@welshman/net'
|
||||
|
||||
// Three main operations:
|
||||
// 1. pull: Get events from relays
|
||||
// 2. push: Send events to relays
|
||||
// 3. sync: Bidirectional sync
|
||||
```
|
||||
|
||||
These utilities are primarily used by:
|
||||
- `Repository` for syncing with relays
|
||||
- `FeedController` for initial feed loading
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
import {sync, pull, getFilterSelections} from '@welshman/net'
|
||||
|
||||
// Sync user profile data
|
||||
const syncProfiles = async (pubkeys: string[]) => {
|
||||
await sync({
|
||||
// What to sync
|
||||
filters: [{
|
||||
kinds: [0],
|
||||
authors: pubkeys
|
||||
}],
|
||||
|
||||
// Which relays
|
||||
relays: ctx.app.router
|
||||
.ForPubkeys(pubkeys)
|
||||
.getUrls(),
|
||||
|
||||
// Local events to consider
|
||||
events: repository.query([{
|
||||
kinds: [0],
|
||||
authors: pubkeys
|
||||
}])
|
||||
})
|
||||
}
|
||||
|
||||
// Initial feed load with negentropy
|
||||
const loadFeed = async () => {
|
||||
await pull({
|
||||
filters: [{
|
||||
kinds: [1],
|
||||
limit: 100
|
||||
}],
|
||||
relays: ctx.app.router
|
||||
.ForUser()
|
||||
.getUrls(),
|
||||
events: [], // No local events yet
|
||||
onEvent: (event) => {
|
||||
// Handle new events
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Sync operations:
|
||||
- Use NIP-77 when supported by relay
|
||||
- Fall back to traditional sync
|
||||
- Handle bidirectional sync
|
||||
- Support filtered sync
|
||||
- Track sync progress
|
||||
@@ -0,0 +1,137 @@
|
||||
# Targets
|
||||
|
||||
The targets system provides different strategies for message routing.
|
||||
Each target type implements a common interface for handling nostr messages but with different routing behaviors.
|
||||
|
||||
## Overview
|
||||
|
||||
Targets are used by the [Executor](/net/executor.md) class to:
|
||||
- Route messages to connections
|
||||
- Handle responses
|
||||
- Manage connection lifecycles
|
||||
- Combine multiple routing strategies
|
||||
|
||||
## Available Targets
|
||||
|
||||
### Echo Target
|
||||
Simple target that echoes messages back. Useful for testing.
|
||||
```typescript
|
||||
import {Echo} from '@welshman/net'
|
||||
|
||||
const echo = new Echo()
|
||||
echo.on('EVENT', (url, event) => {
|
||||
console.log('Echo received:', event)
|
||||
})
|
||||
```
|
||||
|
||||
### Local Target
|
||||
Connects to an in-memory relay implementation.
|
||||
```typescript
|
||||
import {Local} from '@welshman/net'
|
||||
import {Repository, Relay} from '@welshman/util'
|
||||
|
||||
// Create local relay
|
||||
const repository = new Repository()
|
||||
const relay = new Relay(repository)
|
||||
const local = new Local(relay)
|
||||
|
||||
// Use like any other target
|
||||
local.send(['REQ', 'sub1', {kinds: [1]}])
|
||||
```
|
||||
|
||||
### Relay Target
|
||||
Single relay connection target.
|
||||
```typescript
|
||||
import {Relay} from '@welshman/net'
|
||||
|
||||
const target = new Relay(connection)
|
||||
target.on('EVENT', (url, event) => {
|
||||
console.log(`Event from ${url}:`, event)
|
||||
})
|
||||
```
|
||||
|
||||
### Relays Target
|
||||
Manages multiple relay connections.
|
||||
```typescript
|
||||
import {Relays} from '@welshman/net'
|
||||
|
||||
const target = new Relays([
|
||||
connection1,
|
||||
connection2,
|
||||
connection3
|
||||
])
|
||||
```
|
||||
|
||||
### Multi Target
|
||||
Combines multiple targets into one.
|
||||
```typescript
|
||||
import {Multi, Local, Relays} from '@welshman/net'
|
||||
|
||||
// Create multi-target with local and remote relays
|
||||
const target = new Multi([
|
||||
new Local(localRelay),
|
||||
new Relays(remoteConnections)
|
||||
])
|
||||
```
|
||||
|
||||
## Real World Example
|
||||
|
||||
Here's how Coracle might set up its relay infrastructure:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Executor,
|
||||
Multi,
|
||||
Local,
|
||||
Relays
|
||||
} from '@welshman/net'
|
||||
import {Repository, Relay} from '@welshman/util'
|
||||
|
||||
// Setup
|
||||
const setupRelayInfrastructure = () => {
|
||||
// Create local repository & relay
|
||||
const repository = new Repository()
|
||||
const localRelay = new Relay(repository)
|
||||
|
||||
// Get remote connections from pool
|
||||
const remoteConnections = [
|
||||
pool.get("wss://relay1.example.com"),
|
||||
pool.get("wss://relay2.example.com")
|
||||
]
|
||||
|
||||
// Create multi-target executor
|
||||
const executor = new Executor(
|
||||
new Multi([
|
||||
// Local relay for immediate responses
|
||||
new Local(localRelay),
|
||||
|
||||
// Remote relays for network queries
|
||||
new Relays(remoteConnections)
|
||||
])
|
||||
)
|
||||
|
||||
// Subscribe using combined target
|
||||
const sub = executor.subscribe(
|
||||
[{kinds: [1], limit: 10}],
|
||||
{
|
||||
onEvent: (url, event) => {
|
||||
if (url === LOCAL_RELAY_URL) {
|
||||
console.log('Got from cache:', event)
|
||||
} else {
|
||||
console.log('Got from network:', url, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return {executor, sub}
|
||||
}
|
||||
```
|
||||
|
||||
The target system allows for flexible relay configurations while maintaining a consistent interface for the rest of the application. This is particularly useful for:
|
||||
- Caching with local relays
|
||||
- Load balancing across relays
|
||||
- Fallback strategies
|
||||
- Testing and simulation
|
||||
|
||||
Each target type serves a specific purpose but can be combined using `Multi` for complex routing scenarios.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Tracker
|
||||
|
||||
The Tracker is a simple but crucial class that keeps track of which relays an event was seen on or published to. It's essential for relay selection and event source tracking.
|
||||
|
||||
## Overview
|
||||
|
||||
```typescript
|
||||
import {Tracker} from '@welshman/net'
|
||||
|
||||
const tracker = new Tracker()
|
||||
|
||||
// Track event source
|
||||
tracker.track(eventId, relayUrl)
|
||||
|
||||
// Get relays for event
|
||||
const relays = tracker.getRelays(eventId) // Set<string>
|
||||
|
||||
// Get events from relay
|
||||
const events = tracker.getIds(relayUrl) // Set<string>
|
||||
|
||||
// Check specific relay
|
||||
const seen = tracker.hasRelay(eventId, relayUrl)
|
||||
```
|
||||
|
||||
## Used By
|
||||
|
||||
1. **Repository & Sync**
|
||||
```typescript
|
||||
// In sync operations
|
||||
pull({
|
||||
events,
|
||||
relays,
|
||||
onEvent: (event) => {
|
||||
tracker.track(event.id, relay)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
2. **Subscribe**
|
||||
```typescript
|
||||
// In @welshman/app subscribe
|
||||
sub.on('event', (url, event) => {
|
||||
// Track where we got the event
|
||||
tracker.track(event.id, url)
|
||||
})
|
||||
```
|
||||
|
||||
3. **Publish**
|
||||
```typescript
|
||||
// In publish operations
|
||||
pub.emitter.on('success', (url) => {
|
||||
// Track where we published
|
||||
tracker.track(event.id, url)
|
||||
})
|
||||
```
|
||||
|
||||
4. **Router**
|
||||
```typescript
|
||||
// Used for relay selection
|
||||
const relays = tracker
|
||||
.getRelays(event.id)
|
||||
.filter(url =>
|
||||
isHealthyRelay(url)
|
||||
)
|
||||
```
|
||||
|
||||
The Tracker:
|
||||
- Maps events to their source relays
|
||||
- Maps relays to their known events
|
||||
- Helps optimize relay selection
|
||||
|
||||
Think of it as a memory of where events came from, helping make better decisions about where to find or publish events.
|
||||
Reference in New Issue
Block a user