fix(nostr): update NostrSend type to Promise<void> with error handling

The NostrSend type was incorrectly typed as returning void when it actually
returns Promise<void>. This caused async errors to be silently swallowed.

Changes:
- Update NostrSend type signature to return Promise<void>
- Make NostrSender._nostrSend default to async function
- Add .catch() error handling in NostrSender.Send() to log failures
- Add logging to track event publishing status

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-01-25 14:37:56 -05:00
parent a2b7ac1673
commit be4c3bcf2a
2 changed files with 18 additions and 9 deletions

View file

@ -16,7 +16,7 @@ export type SendDataContent = { type: "content", content: string, pub: string }
export type SendDataEvent = { type: "event", event: UnsignedEvent, encrypt?: { toPub: string } } export type SendDataEvent = { type: "event", event: UnsignedEvent, encrypt?: { toPub: string } }
export type SendData = SendDataContent | SendDataEvent export type SendData = SendDataContent | SendDataEvent
export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string } export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string }
export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => void export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => Promise<void>
export type LinkedProviderInfo = { pubkey: string, clientId: string, relayUrl: string } export type LinkedProviderInfo = { pubkey: string, clientId: string, relayUrl: string }
export type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string, provider?: LinkedProviderInfo } export type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string, provider?: LinkedProviderInfo }
@ -203,21 +203,22 @@ export class NostrPool {
const signed = finalizeEvent(event, Buffer.from(keys.privateKey, 'hex')) const signed = finalizeEvent(event, Buffer.from(keys.privateKey, 'hex'))
let sent = false let sent = false
const log = getLogger({ appName: keys.name }) const log = getLogger({ appName: keys.name })
// const r = relays ? relays : this.getServiceRelays() this.log(`📤 Publishing Kind ${event.kind} event to ${relays.length} relay(s): ${relays.join(', ')}`)
const pool = new SimplePool() const pool = new SimplePool()
await Promise.all(pool.publish(relays, signed).map(async p => { await Promise.all(pool.publish(relays, signed).map(async p => {
try { try {
await p await p
sent = true sent = true
} catch (e: any) { } catch (e: any) {
console.log(e) this.log(ERROR, `Failed to publish Kind ${event.kind} event:`, e.message || e)
log(e) log(e)
} }
})) }))
if (!sent) { if (!sent) {
this.log(ERROR, `Failed to send Kind ${event.kind} event to any relay`)
log("failed to send event") log("failed to send event")
} else { } else {
//log("sent event") this.log(`✅ Kind ${event.kind} event published successfully (id: ${signed.id.slice(0, 16)}...)`)
} }
} }

View file

@ -1,7 +1,7 @@
import { NostrSend, SendData, SendInitiator } from "./nostrPool.js" import { NostrSend, SendData, SendInitiator } from "./nostrPool.js"
import { getLogger } from "../helpers/logger.js" import { ERROR, getLogger } from "../helpers/logger.js"
export class NostrSender { export class NostrSender {
private _nostrSend: NostrSend = () => { throw new Error('nostr send not initialized yet') } private _nostrSend: NostrSend = async () => { throw new Error('nostr send not initialized yet') }
private isReady: boolean = false private isReady: boolean = false
private onReadyCallbacks: (() => void)[] = [] private onReadyCallbacks: (() => void)[] = []
private pendingSends: { initiator: SendInitiator, data: SendData, relays?: string[] | undefined }[] = [] private pendingSends: { initiator: SendInitiator, data: SendData, relays?: string[] | undefined }[] = []
@ -12,7 +12,12 @@ export class NostrSender {
this.isReady = true this.isReady = true
this.onReadyCallbacks.forEach(cb => cb()) this.onReadyCallbacks.forEach(cb => cb())
this.onReadyCallbacks = [] this.onReadyCallbacks = []
this.pendingSends.forEach(send => this._nostrSend(send.initiator, send.data, send.relays)) // Process pending sends with proper error handling
this.pendingSends.forEach(send => {
this._nostrSend(send.initiator, send.data, send.relays).catch(e => {
this.log(ERROR, "failed to send pending event", e.message || e)
})
})
this.pendingSends = [] this.pendingSends = []
} }
OnReady(callback: () => void) { OnReady(callback: () => void) {
@ -22,13 +27,16 @@ export class NostrSender {
this.onReadyCallbacks.push(callback) this.onReadyCallbacks.push(callback)
} }
} }
Send(initiator: SendInitiator, data: SendData, relays?: string[] | undefined) { Send(initiator: SendInitiator, data: SendData, relays?: string[] | undefined): void {
if (!this.isReady) { if (!this.isReady) {
this.log("tried to send before nostr was ready, caching request") this.log("tried to send before nostr was ready, caching request")
this.pendingSends.push({ initiator, data, relays }) this.pendingSends.push({ initiator, data, relays })
return return
} }
this._nostrSend(initiator, data, relays) // Fire and forget but log errors
this._nostrSend(initiator, data, relays).catch(e => {
this.log(ERROR, "failed to send event", e.message || e)
})
} }
IsReady() { IsReady() {
return this.isReady return this.isReady