Compare commits

...

4 Commits

Author SHA1 Message Date
fiatjaf
c362212778 make pool.publish() return a single Pub object. 2023-02-26 17:44:51 -03:00
fiatjaf
a8938a3a0f wait a second before failing to send on a not yet connected websocket. 2023-02-26 16:53:03 -03:00
fiatjaf
a21329da3f make timeouts configurable for pool. 2023-02-26 16:50:49 -03:00
fiatjaf
63f4a49a69 increase pool timeouts. 2023-02-26 15:05:26 -03:00
4 changed files with 27 additions and 149 deletions

140
magic.ts
View File

@@ -1,140 +0,0 @@
import {Relay, relayInit} from './relay'
import {Event} from './event'
import {normalizeURL} from './utils'
export default function (
writeableRelays: string[],
fallbackRelays: string[],
safeRelays: string[]
) {
return new MagicPool(fallbackRelays, writeableRelays, safeRelays)
}
class MagicPool {
private _conn: {[url: string]: Relay}
private _fallback: {[url: string]: Relay}
private _write: {[url: string]: Relay}
private _safe: {[url: string]: Relay}
private _profileRelays: {[pubkey: string]: RelayTableScore}
private _tempCache: {[id: string]: Event}
constructor(
fallbackRelays: string[],
writeableRelays: string[],
safeRelays: string[] = [
'wss://eden.nostr.land',
'wss://nostr.milou.lol',
'wss://relay.minds.com/nostr/v1/ws'
]
) {
this._conn = {}
this._write = {}
this._fallback = {}
this._profileRelays = {}
this._tempCache = {}
const hasEventId = (id: string): boolean => id in this._tempCache
const init = (url: string) => {
this._conn[normalizeURL(url)] = relayInit(normalizeURL(url), hasEventId)
}
fallbackRelays.forEach(init)
writeableRelays.forEach(init)
safeRelays.forEach(init)
this._write = Object.fromEntries(
writeableRelays.map(url => [
normalizeURL(url),
this._conn[normalizeURL(url)]
])
)
this._fallback = Object.fromEntries(
fallbackRelays.map(url => [
normalizeURL(url),
this._conn[normalizeURL(url)]
])
)
this._safe = Object.fromEntries(
safeRelays.map(url => [normalizeURL(url), this._conn[normalizeURL(url)]])
)
}
publish(event: Event) {
return Promise.all(
Object.entries(this._write).map(
([url, relay]) =>
new Promise(async resolve => {
await relay.connect()
let pub = relay.publish(event)
let to = setTimeout(() => {
let end = setTimeout(() => {
resolve({url, success: false, reason: 'timeout'})
}, 2500)
pub.on('seen', () => {
clearTimeout(end)
resolve({url, success: true, reason: 'seen'})
})
}, 2500)
pub.on('ok', () => {
clearTimeout(to)
resolve({url, success: true, reason: 'ok'})
})
pub.on('failed', (reason: string) => {
clearTimeout(to)
resolve({url, success: false, reason})
})
})
)
)
}
profile(
pubkey: string,
onUpdate: (events: Event[]) => void
): {
page(n: number): void
} {
var relays = new Set()
let rts = this._profileRelays[pubkey]
if (rts) {
relays = rts.get(3)
}
let fallback = Object.values(this._fallback)
for (let i = 0; i < fallback.length; i++) {
if (relays.size < 3) {
relays.add(fallback[Math.floor(Math.random() * fallback.length)])
} else break
}
// start subscription
for (let r in relays) {
r.
}
return {
page(n: number) {}
}
}
}
class RelayTableScore {
seen: string[] = []
hinted: string[] = []
explicit: string[] = []
get(n: number): Set<string> {
let relays = new Set<string>()
for (let i = 0; i < n; i++) {
for (let j = 0; j < 3; j++) {
let v = [this.seen, this.explicit, this.hinted][j][i]
if (v) {
relays.add(v)
if (relays.size >= n) return relays
}
}
}
return relays
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "nostr-tools",
"version": "1.6.1",
"version": "1.7.0",
"description": "Tools for making a Nostr client.",
"repository": {
"type": "git",

29
pool.ts
View File

@@ -8,8 +8,13 @@ export class SimplePool {
private _conn: {[url: string]: Relay}
private _seenOn: {[id: string]: Set<string>} = {} // a map of all events we've seen in each relay
constructor() {
private eoseSubTimeout: number
private getTimeout: number
constructor(options: {eoseSubTimeout?: number; getTimeout?: number} = {}) {
this._conn = {}
this.eoseSubTimeout = options.eoseSubTimeout || 3400
this.getTimeout = options.getTimeout || 3400
}
close(relays: string[]): void {
@@ -51,7 +56,7 @@ export class SimplePool {
let eoseTimeout = setTimeout(() => {
eoseSent = true
for (let cb of eoseListeners.values()) cb()
}, 2400)
}, this.eoseSubTimeout)
relays.forEach(async relay => {
let r
@@ -120,7 +125,7 @@ export class SimplePool {
let timeout = setTimeout(() => {
sub.unsub()
resolve(null)
}, 1500)
}, this.getTimeout)
sub.on('event', (event: Event) => {
resolve(event)
clearTimeout(timeout)
@@ -150,13 +155,23 @@ export class SimplePool {
})
}
publish(relays: string[], event: Event): Pub[] {
return relays.map(relay => {
publish(relays: string[], event: Event): Pub {
let pubs = relays.map(relay => {
let r = this._conn[normalizeURL(relay)]
if (!r) return badPub(relay)
let s = r.publish(event)
return s
return r.publish(event)
})
return {
on(type, cb) {
pubs.forEach((pub, i) => {
pub.on(type, () => cb(relays[i]))
})
},
off() {
// do nothing here, FIXME
}
}
}
seenOn(id: string): string[] {

View File

@@ -175,7 +175,10 @@ export function relayInit(url: string): Relay {
async function trySend(params: [string, ...any]) {
let msg = JSON.stringify(params)
if (!connected()) {
return
await new Promise(resolve => setTimeout(resolve, 1000))
if (!connected()) {
return
}
}
try {
ws.send(msg)