turn .publish() into a normal async function returning a promise.

this simplifies the code and makes the API more intuitive.

we used to need the event emitter thing because we were subscribing to the same relay
to check if the event had been published, but that is not necessary now that we assume
an OK response will always come.

closes https://github.com/nbd-wtf/nostr-tools/issues/262
This commit is contained in:
fiatjaf 2023-07-30 12:19:11 -03:00 committed by fiatjaf_
parent 17590cce91
commit 9d690814ca
4 changed files with 64 additions and 101 deletions

View File

@ -108,13 +108,7 @@ let event = {
event.id = getEventHash(event)
event.sig = getSignature(event, sk)
let pub = relay.publish(event)
pub.on('ok', () => {
console.log(`${relay.url} has accepted our event`)
})
pub.on('failed', reason => {
console.log(`failed to publish to ${relay.url}: ${reason}`)
})
await relay.publish(event)
let events = await relay.list([{kinds: [0, 1]}])
let event = await relay.get({

View File

@ -17,7 +17,9 @@ export const authenticate = async ({
}: {
challenge: string
relay: Relay
sign: <K extends number = number>(e: EventTemplate<K>) => Promise<Event<K>> | Event<K>
sign: <K extends number = number>(
e: EventTemplate<K>
) => Promise<Event<K>> | Event<K>
}): Promise<void> => {
const e: EventTemplate = {
kind: Kind.ClientAuth,
@ -28,15 +30,5 @@ export const authenticate = async ({
],
content: ''
}
const pub = relay.auth(await sign(e))
return new Promise((resolve, reject) => {
pub.on('ok', function ok() {
pub.off('ok', ok)
resolve()
})
pub.on('failed', function fail(reason: string) {
pub.off('failed', fail)
reject(reason)
})
})
return relay.auth(await sign(e))
}

59
pool.ts
View File

@ -1,9 +1,8 @@
import {
relayInit,
type Pub,
type Relay,
type Sub,
type SubscriptionOptions,
type SubscriptionOptions
} from './relay.ts'
import {normalizeURL} from './utils.ts'
@ -17,7 +16,13 @@ export class SimplePool {
private getTimeout: number
private seenOnEnabled: boolean = true
constructor(options: {eoseSubTimeout?: number; getTimeout?: number; seenOnEnabled?: boolean} = {}) {
constructor(
options: {
eoseSubTimeout?: number
getTimeout?: number
seenOnEnabled?: boolean
} = {}
) {
this._conn = {}
this.eoseSubTimeout = options.eoseSubTimeout || 3400
this.getTimeout = options.getTimeout || 3400
@ -46,7 +51,11 @@ export class SimplePool {
return relay
}
sub<K extends number = number>(relays: string[], filters: Filter<K>[], opts?: SubscriptionOptions): Sub<K> {
sub<K extends number = number>(
relays: string[],
filters: Filter<K>[],
opts?: SubscriptionOptions
): Sub<K> {
let _knownIds: Set<string> = new Set()
let modifiedOpts = {...(opts || {})}
modifiedOpts.alreadyHaveEvent = (id, url) => {
@ -82,7 +91,7 @@ export class SimplePool {
}
if (!r) return
let s = r.sub(filters, modifiedOpts)
s.on('event', (event) => {
s.on('event', event => {
_knownIds.add(event.id as string)
for (let cb of eventListeners.values()) cb(event)
})
@ -138,7 +147,7 @@ export class SimplePool {
sub.unsub()
resolve(null)
}, this.getTimeout)
sub.on('event', (event) => {
sub.on('event', event => {
resolve(event)
clearTimeout(timeout)
sub.unsub()
@ -155,7 +164,7 @@ export class SimplePool {
let events: Event<K>[] = []
let sub = this.sub(relays, filters, opts)
sub.on('event', (event) => {
sub.on('event', event => {
events.push(event)
})
@ -167,39 +176,11 @@ export class SimplePool {
})
}
publish(relays: string[], event: Event<number>): Pub {
const pubPromises: Promise<Pub>[] = relays.map(async relay => {
let r
try {
r = await this.ensureRelay(relay)
return r.publish(event)
} catch (_) {
return {on() {}, off() {}}
}
publish(relays: string[], event: Event<number>): Promise<void>[] {
return relays.map(async relay => {
let r = await this.ensureRelay(relay)
return r.publish(event)
})
const callbackMap = new Map()
return {
on(type, cb) {
relays.forEach(async (relay, i) => {
let pub = await pubPromises[i]
let callback = () => cb(relay)
callbackMap.set(cb, callback)
pub.on(type, callback)
})
},
off(type, cb) {
relays.forEach(async (_, i) => {
let callback = callbackMap.get(cb)
if (callback) {
let pub = await pubPromises[i]
pub.off(type, callback)
}
})
}
}
}
seenOn(id: string): string[] {

View File

@ -3,7 +3,7 @@
import {verifySignature, validateEvent, type Event} from './event.ts'
import {matchFilters, type Filter} from './filter.ts'
import {getHex64, getSubscriptionId} from './fakejson.ts'
import { MessageQueue } from './utils.ts'
import {MessageQueue} from './utils.ts'
type RelayEvent = {
connect: () => void | Promise<void>
@ -25,15 +25,24 @@ export type Relay = {
status: number
connect: () => Promise<void>
close: () => void
sub: <K extends number = number>(filters: Filter<K>[], opts?: SubscriptionOptions) => Sub<K>
list: <K extends number = number>(filters: Filter<K>[], opts?: SubscriptionOptions) => Promise<Event<K>[]>
get: <K extends number = number>(filter: Filter<K>, opts?: SubscriptionOptions) => Promise<Event<K> | null>
sub: <K extends number = number>(
filters: Filter<K>[],
opts?: SubscriptionOptions
) => Sub<K>
list: <K extends number = number>(
filters: Filter<K>[],
opts?: SubscriptionOptions
) => Promise<Event<K>[]>
get: <K extends number = number>(
filter: Filter<K>,
opts?: SubscriptionOptions
) => Promise<Event<K> | null>
count: (
filters: Filter[],
opts?: SubscriptionOptions
) => Promise<CountPayload | null>
publish: (event: Event<number>) => Pub
auth: (event: Event<number>) => Pub
publish: (event: Event<number>) => Promise<void>
auth: (event: Event<number>) => Promise<void>
off: <T extends keyof RelayEvent, U extends RelayEvent[T]>(
event: T,
listener: U
@ -43,12 +52,11 @@ export type Relay = {
listener: U
) => void
}
export type Pub = {
on: (type: 'ok' | 'failed', cb: any) => void
off: (type: 'ok' | 'failed', cb: any) => void
}
export type Sub<K extends number = number> = {
sub: <K extends number = number>(filters: Filter<K>[], opts: SubscriptionOptions) => Sub<K>
sub: <K extends number = number>(
filters: Filter<K>[],
opts: SubscriptionOptions
) => Sub<K>
unsub: () => void
on: <T extends keyof SubEvent<K>, U extends SubEvent<K>[T]>(
event: T,
@ -93,9 +101,8 @@ export function relayInit(
} = {}
var pubListeners: {
[eventid: string]: {
ok: Array<() => void>
seen: Array<() => void>
failed: Array<(reason: string) => void>
resolve: (_: unknown) => void
reject: (err: Error) => void
}
} = {}
@ -196,10 +203,9 @@ export function relayInit(
let ok: boolean = data[2]
let reason: string = data[3] || ''
if (id in pubListeners) {
if (ok) pubListeners[id].ok.forEach(cb => cb())
else pubListeners[id].failed.forEach(cb => cb(reason))
pubListeners[id].ok = [] // 'ok' only happens once per pub, so stop listeners here
pubListeners[id].failed = []
let {resolve, reject} = pubListeners[id]
if (ok) resolve(null)
else reject(new Error(reason))
}
return
}
@ -294,26 +300,16 @@ export function relayInit(
}
function _publishEvent(event: Event<number>, type: string) {
if (!event.id) throw new Error(`event ${event} has no id`)
let id = event.id
trySend([type, event])
return {
on: (type: 'ok' | 'failed', cb: any) => {
pubListeners[id] = pubListeners[id] || {
ok: [],
failed: []
}
pubListeners[id][type].push(cb)
},
off: (type: 'ok' | 'failed', cb: any) => {
let listeners = pubListeners[id]
if (!listeners) return
let idx = listeners[type].indexOf(cb)
if (idx >= 0) listeners[type].splice(idx, 1)
return new Promise((resolve, reject) => {
if (!event.id) {
reject(new Error(`event ${event} has no id`))
return
}
}
let id = event.id
trySend([type, event])
pubListeners[id] = {resolve, reject}
})
}
return {
@ -349,7 +345,7 @@ export function relayInit(
clearTimeout(timeout)
resolve(events)
})
s.on('event', (event) => {
s.on('event', event => {
events.push(event)
})
}),
@ -360,7 +356,7 @@ export function relayInit(
s.unsub()
resolve(null)
}, getTimeout)
s.on('event', (event) => {
s.on('event', event => {
s.unsub()
clearTimeout(timeout)
resolve(event)
@ -379,11 +375,11 @@ export function relayInit(
resolve(event)
})
}),
publish(event): Pub {
return _publishEvent(event, 'EVENT')
async publish(event): Promise<void> {
await _publishEvent(event, 'EVENT')
},
auth(event): Pub {
return _publishEvent(event, 'AUTH')
async auth(event): Promise<void> {
await _publishEvent(event, 'AUTH')
},
connect,
close(): void {