Making Requests
The Network plugin (app.use(Network)) wraps the @welshman/net request and publish functions and binds them to the app's net context (its pool and repository). Cleaning up the app aborts its in-flight requests. The Sync plugin (app.use(Sync)) adds negentropy-aware reconciliation.
Loading and requesting
const net = app.use(Network)
// A one-shot request, which resolves with the matching events
const events = await net.load({
filters: [{kinds: [1], authors: [pubkey]}],
relays: ["wss://relay.example"],
timeout: 3000,
})
// Open a subscription
net.request({
filters: [{kinds: [1]}],
relays: ["wss://relay.example"],
onEvent: event => console.log(event),
})net.load is net.request with autoClose: true. A load resolves once every relay has closed or timed out, so an empty result means every relay answered nothing. The outbox model routes on that answer. A timeout caps how long a caller waits, and threshold: 0.5 resolves once half the relays have closed and closes the rest. A load whose results are read reactively from the repository needs neither. Requests made at the same time share a relay subscription through socketPolicyMultiplex.
net.publish(options) sends a signed event directly. App publishing goes through the Publisher, which adds the optimistic write and status tracking.
The outbox model: loadUsingOutbox
Most data plugins load through loadUsingOutbox. Given an author's pubkey, it resolves [...relays(hints), outbox(pubkey)] (the relay hints plus the author's NIP-65 write relays) through the Router, requests the resulting urls once with a 3s timeout, and returns the most recent matching event.
const latestProfile = await net.loadUsingOutbox(pubkey, {kinds: [0]})
// With relay hints to try first
const note = await net.loadUsingOutbox(pubkey, {kinds: [1], limit: 1}, ["wss://hint.example"])The filter is always constrained to authors: [pubkey]. Lazy loading in Profiles, FollowLists, MuteLists and the other outbox-backed plugins goes through it.
loadAllUsingOutbox(pubkey, filter?, hints?) routes the same way and returns every matching event instead of the newest one.
Negentropy sync
Sync reconciles the repository with relays one relay at a time. A relay whose NIP-11 document advertises negentropy (app.use(Relays).hasNegentropy(url)) gets NIP-77 set reconciliation through @welshman/net's pull and push. Any other relay gets a plain request (pull) or one publish per event (push).
type AppSyncOpts = {relays: string[]; filters: Filter[]}
const sync = app.use(Sync)
// Pull missing events from relays into the repository
await sync.pull({relays, filters: [{kinds: [3], authors: [pubkey]}]})
// Push local events up to relays
await sync.push({relays, filters: [{authors: [pubkey]}]})
// Query the repository's signed events
const local = await sync.query([{kinds: [1]}])Querying the repository: Events
app.use(Network) fetches events, and app.use(Events) reads the ones already in the repository. Every method binds this app's repository and returns a Projection. Every view but isDeleted is an AsyncProjection, whose load() reads the repository fresh.
import {Events} from "@welshman/app"
const events = app.use(Events)
events.byId(filters).$ // Map<id, TrustedEvent>
events.all(filters).$ // store order
events.asc(filters).$ // oldest first
events.desc(filters).$ // newest first
events.one(idOrAddress, hints) // a single event, loaded on first read if missing
events.isDeleted(event).$Scoped to a relay, via the tracker:
events.byIdForUrl(url, filters).$
events.forUrl(url, filters).$
events.byIdByUrl(filters).$ // Map<url, Map<id, TrustedEvent>>
events.relaySignedForUrl(url, filters).$ // only events the relay itself signedrelaySignedForUrl drops any event whose pubkey isn't the relay's NIP-11 self, as RelaySignedDerivedPlugin does. Relay-generated kinds (NIP-29 room state, relay membership) mean nothing from another author.