A nostr toolkit focused on creating highly a configurable client system. What paravel provides is less a library of code than a library of abstractions. Odds are you will end up creating a custom implementation of every component to suit your needs, but if you start with paravel that will be much easier than if you pile on parameters over time.
Some general-purpose utilities used in paravel.
-
Deferred
is just a promise withresolve
andreject
methods. -
Emitter
extends EventEmitter to supportemitter.on('*', ...)
. -
Fluent
is a wrapper around arrays with chained methods that modify and copy the underlying array. -
Kinds
contains kind constants and related utility functions. -
LRUCache
is an implementation of an LRU cache. -
Queue
is an implementation of an asynchronous queue. -
Relays
contains utilities related to relays. -
Router
is a utility for selecting relay urls based on user preferences and protocol hints. -
Tags
andTag
extendFluent
to provide a convenient way to access and modify tags. -
Tools
is a collection of general-purpose utility functions.
Utilities having to do with connection management and nostr messages.
-
ConnectionMeta
tracks stats for a givenConnection
. -
Connection
is a wrapper forSocket
with send and receive queues, and aConnectionMeta
instance. -
Executor
implements common nostr flows ontarget
-
Pool
is a thin wrapper aroundMap
for use withRelay
s. -
Socket
is a wrapper around isomorphic-ws that handles json parsing/serialization. -
Subscription
is a higher-level utility for making requests against multiple nostr relays.
Executor targets extend Emitter
, and have a send
method, a cleanup
method, and a connections
getter. They are intended to be passed to an Executor
for use.
-
Multi
allows you to compose multiple targets together. -
Plex
takes an array of urls and aConnection
and sends and receives wrapped nostr messages over that connection. -
Relay
takes aConnection
and provides listeners for different verbs. -
Relays
takes an array ofConnection
s and provides listeners for different verbs, merging all events into a single stream.
Functionality is split into small chunks to allow for changing out implementations as needed. This is useful when attempting to support novel use cases. Here's a simple implementation of an agent that can use a multiplexer if enabled, or can fall back to communicating directly with all relays.
class Agent {
pool = new Pool()
constructor(readonly multiplexerUrl: string) {}
getTarget(urls) {
return this.multiplexerUrl
? new Plex(urls, this.pool.get(this.multiplexerUrl))
: new Relays(urls.map(url => this.pool.get(url)))
}
subscribe(urls, filters, id, {onEvent, onEose}) {
const executor = new Executor(this.getTarget(urls))
return executor.subscribe(filters, id, {onEvent, onEose})
}
}