1(()=>{2 let g = globalThis, d = g.document, T = d ?? g3 if (g.hydrogen) return4 let pool = {}, n = 05 let send = (el, type, detail)=>el.dispatchEvent(new CustomEvent("nhy:" + type, {detail, cancelable:1, bubbles:1, composed:1}))6 let esc = (s)=>(s + "").replace(/[&<>"']/g, (c)=>`&#${c.charCodeAt()};`)7 let up = (r)=>r.ws.readyState == 18 let relays = (el)=>el?.closest?.("[nhy-relay]")?.getAttribute("nhy-relay").split(/\s+/) ?? g.nhyCfg?.relays ?? []9 let relay = (url)=>{10 let r = pool[url]11 if (r) return r12 r = pool[url] = {url, subs:new Map(), oks:new Map(), q:[]}13 r.send = (m)=>up(r) ? r.ws.send(JSON.stringify(m)) : r.q.push(m)14 let connect = (ws)=>{15 ws = r.ws = new WebSocket(url)16 ws.onopen = ()=>{17 r.subs.forEach((c, id)=>r.send(["REQ", id, ...c.filters]))18 r.q.splice(0).forEach(r.send)19 }20 ws.onmessage = (m)=>{21 try {22 let msg = JSON.parse(m.data), [t, a, b, c] = msg, s = r.subs.get(a)23 if (!send(T, "message", {url, relay:r, msg})) return24 if (t == "EVENT") s?.deliver(b, url)25 if (t == "EOSE") s?.oneose?.(url)26 if (t == "OK") r.oks.get(a)?.(b, c)27 } catch {}28 }29 ws.onclose = ()=>r.subs.size || r.q.length ? setTimeout(connect, 3e3) : delete pool[url]30 }31 connect()32 return r33 }34 let req = (cfg)=>{35 let id = "h" + ++n, seen = new Set(), el = cfg.elt ?? T, rs = []36 cfg.relays ??= relays(cfg.elt)37 cfg.deliver = async(event, from)=>{38 if (el.isConnected === false) cfg.close()39 if (seen.has(event.id) || !cfg.open) return40 seen.add(event.id)41 if (cfg.verify && !await cfg.verify(event)) return42 if (cfg.map && !(event = await cfg.map(event))) return43 let fields = {}44 for (let k in event) fields[k] = esc(event[k])45 for (let [k, v] of event.tags) fields["#" + k] ??= esc(v ?? "")46 if (send(el, "event", {cfg, event, from, fields})) cfg.onevent?.(event, from, fields)47 }48 cfg.close = ()=>{49 cfg.open = 050 rs.forEach((r)=>r.subs.delete(id) && up(r) && r.send(["CLOSE", id]))51 }52 if (!send(el, "config", {cfg})) return53 cfg.open = 154 for (let r of rs = cfg.relays.map(relay)){55 r.subs.set(id, cfg)56 if (up(r)) r.send(["REQ", id, ...cfg.filters])57 }58 return cfg59 }60 let pub = (event, list)=>Promise.all(list.map((url)=>new Promise((res)=>{61 let r = relay(url), t = setTimeout(()=>done(false, "timeout"), g.nhyCfg?.timeout ?? 8e3)62 let done = (ok, msg)=>res({relay:url, ok, msg}, clearTimeout(t), r.oks.delete(event.id))63 r.oks.set(event.id, done)64 r.send(["EVENT", done.event = event])65 })))66 // The family signing hook: nhy:config lets helium, chlorine or anything else replace cfg.sign.67 let sign = async(cfg)=>{68 cfg.sign = (e)=>g.nostr.signEvent(e)69 cfg.relays ??= relays(cfg.elt)70 if (send(cfg.elt ?? T, "config", {cfg})) return cfg.event = await cfg.sign(cfg.event)71 }72 g.hydrogen = {req, pub, sign, relay, relays, esc, pool}73})()