@qntx/nostr 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/builder-BphJa-Y-.d.mts +67 -0
- package/dist/builder-q0VsvnYE.mjs +158 -0
- package/dist/builder-q0VsvnYE.mjs.map +1 -0
- package/dist/client-Bd8B1mYm.mjs +835 -0
- package/dist/client-Bd8B1mYm.mjs.map +1 -0
- package/dist/client.d.mts +2 -0
- package/dist/client.mjs +2 -0
- package/dist/core.d.mts +8 -0
- package/dist/core.mjs +9 -0
- package/dist/error-B49rbDzT.d.mts +7 -0
- package/dist/error-CwBxle6N.mjs +13 -0
- package/dist/error-CwBxle6N.mjs.map +1 -0
- package/dist/error-DZEs5OuZ.mjs +24 -0
- package/dist/error-DZEs5OuZ.mjs.map +1 -0
- package/dist/error-WEFS-R-4.d.mts +20 -0
- package/dist/event-B5D2jxt_.d.mts +108 -0
- package/dist/event-CzoQEFMF.mjs +188 -0
- package/dist/event-CzoQEFMF.mjs.map +1 -0
- package/dist/filter-BxwjEkZp.mjs +113 -0
- package/dist/filter-BxwjEkZp.mjs.map +1 -0
- package/dist/filter-DUEKP3q1.d.mts +40 -0
- package/dist/gossip-3xkRRCm_.mjs +220 -0
- package/dist/gossip-3xkRRCm_.mjs.map +1 -0
- package/dist/gossip-C5RVB1UB.d.mts +71 -0
- package/dist/gossip.d.mts +3 -0
- package/dist/gossip.mjs +2 -0
- package/dist/http-6vevbBlu.d.mts +22 -0
- package/dist/http-D2rhokZo.mjs +24 -0
- package/dist/http-D2rhokZo.mjs.map +1 -0
- package/dist/index-BYMnhRIC.d.mts +161 -0
- package/dist/index-C0C4e-Gn.d.mts +55 -0
- package/dist/index-CaxrMD1A.d.mts +1 -0
- package/dist/index-DaRyjo7O.d.mts +77 -0
- package/dist/index-DgOugrpF.d.mts +275 -0
- package/dist/index-EwL6fHLo.d.mts +267 -0
- package/dist/index-k2YhCgsh.d.mts +57 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.mjs +23 -0
- package/dist/key-BiGxGLhg.d.mts +37 -0
- package/dist/key-Dn-aX4x4.mjs +130 -0
- package/dist/key-Dn-aX4x4.mjs.map +1 -0
- package/dist/kind-BLl4cLOc.mjs +59 -0
- package/dist/kind-BLl4cLOc.mjs.map +1 -0
- package/dist/loaders-BNQszDAn.mjs +767 -0
- package/dist/loaders-BNQszDAn.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -0
- package/dist/loaders.mjs +2 -0
- package/dist/memory-BCmhZPek.mjs +461 -0
- package/dist/memory-BCmhZPek.mjs.map +1 -0
- package/dist/message-C_Uj-Y-c.mjs +179 -0
- package/dist/message-C_Uj-Y-c.mjs.map +1 -0
- package/dist/message-DJC-Jz6T.d.mts +32 -0
- package/dist/nip17-BWq-Kfq5.d.mts +38 -0
- package/dist/nip19-D7905oIv.d.mts +63 -0
- package/dist/nip46-C_DoxiS_.d.mts +51 -0
- package/dist/nip59-DljW_sYh.d.mts +76 -0
- package/dist/nip65-Di5hZRuV.d.mts +21 -0
- package/dist/nip77-ByxlgoQO.d.mts +55 -0
- package/dist/nips/blossom.d.mts +93 -0
- package/dist/nips/blossom.mjs +325 -0
- package/dist/nips/blossom.mjs.map +1 -0
- package/dist/nips/nip04.d.mts +8 -0
- package/dist/nips/nip04.mjs +41 -0
- package/dist/nips/nip04.mjs.map +1 -0
- package/dist/nips/nip05.d.mts +89 -0
- package/dist/nips/nip05.mjs +147 -0
- package/dist/nips/nip05.mjs.map +1 -0
- package/dist/nips/nip10.d.mts +43 -0
- package/dist/nips/nip10.mjs +194 -0
- package/dist/nips/nip10.mjs.map +1 -0
- package/dist/nips/nip11.d.mts +43 -0
- package/dist/nips/nip11.mjs +99 -0
- package/dist/nips/nip11.mjs.map +1 -0
- package/dist/nips/nip13.d.mts +23 -0
- package/dist/nips/nip13.mjs +88 -0
- package/dist/nips/nip13.mjs.map +1 -0
- package/dist/nips/nip17.d.mts +2 -0
- package/dist/nips/nip17.mjs +138 -0
- package/dist/nips/nip17.mjs.map +1 -0
- package/dist/nips/nip19.d.mts +2 -0
- package/dist/nips/nip19.mjs +174 -0
- package/dist/nips/nip19.mjs.map +1 -0
- package/dist/nips/nip21.d.mts +17 -0
- package/dist/nips/nip21.mjs +29 -0
- package/dist/nips/nip21.mjs.map +1 -0
- package/dist/nips/nip27.d.mts +42 -0
- package/dist/nips/nip27.mjs +175 -0
- package/dist/nips/nip27.mjs.map +1 -0
- package/dist/nips/nip42.d.mts +12 -0
- package/dist/nips/nip42.mjs +22 -0
- package/dist/nips/nip42.mjs.map +1 -0
- package/dist/nips/nip44.d.mts +12 -0
- package/dist/nips/nip44.mjs +125 -0
- package/dist/nips/nip44.mjs.map +1 -0
- package/dist/nips/nip46.d.mts +2 -0
- package/dist/nips/nip46.mjs +132 -0
- package/dist/nips/nip46.mjs.map +1 -0
- package/dist/nips/nip49.d.mts +12 -0
- package/dist/nips/nip49.mjs +80 -0
- package/dist/nips/nip49.mjs.map +1 -0
- package/dist/nips/nip51.d.mts +88 -0
- package/dist/nips/nip51.mjs +246 -0
- package/dist/nips/nip51.mjs.map +1 -0
- package/dist/nips/nip57.d.mts +43 -0
- package/dist/nips/nip57.mjs +230 -0
- package/dist/nips/nip57.mjs.map +1 -0
- package/dist/nips/nip59.d.mts +2 -0
- package/dist/nips/nip59.mjs +171 -0
- package/dist/nips/nip59.mjs.map +1 -0
- package/dist/nips/nip65.d.mts +2 -0
- package/dist/nips/nip65.mjs +76 -0
- package/dist/nips/nip65.mjs.map +1 -0
- package/dist/nips/nip77.d.mts +2 -0
- package/dist/nips/nip77.mjs +421 -0
- package/dist/nips/nip77.mjs.map +1 -0
- package/dist/nips/nip96.d.mts +31 -0
- package/dist/nips/nip96.mjs +95 -0
- package/dist/nips/nip96.mjs.map +1 -0
- package/dist/nips/nip98.d.mts +28 -0
- package/dist/nips/nip98.mjs +91 -0
- package/dist/nips/nip98.mjs.map +1 -0
- package/dist/nostr_crypto_wasm_bg-hi5aXi5P.mjs +4 -0
- package/dist/nostr_crypto_wasm_bg.wasm +0 -0
- package/dist/pool-C_j1OmBB.mjs +1881 -0
- package/dist/pool-C_j1OmBB.mjs.map +1 -0
- package/dist/pool-DhRi7l-b.d.mts +297 -0
- package/dist/relay.d.mts +4 -0
- package/dist/relay.mjs +2 -0
- package/dist/signer-CZ6K6FUc.mjs +473 -0
- package/dist/signer-CZ6K6FUc.mjs.map +1 -0
- package/dist/signer.d.mts +3 -0
- package/dist/signer.mjs +2 -0
- package/dist/storage-DOy0iLRE.mjs +870 -0
- package/dist/storage-DOy0iLRE.mjs.map +1 -0
- package/dist/storage.d.mts +4 -0
- package/dist/storage.mjs +4 -0
- package/dist/types-BzyADqnw.d.mts +19 -0
- package/dist/types-Dn24TPFs.d.mts +43 -0
- package/dist/util-6tfLQ9JF.mjs +73 -0
- package/dist/util-6tfLQ9JF.mjs.map +1 -0
- package/dist/wasm.d.mts +19 -0
- package/dist/wasm.mjs +237 -0
- package/dist/wasm.mjs.map +1 -0
- package/package.json +204 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool-C_j1OmBB.mjs","names":["#abort","#opts","#timer","#pingpong","#abortCurrentPing","#waiters","#gen","#nativePing","#waitForNativePing","#waitForDummyPing","#WS","#verify","#publishTimeoutMs","#connectTimeoutMs","#enableReconnect","#backoff","#enablePing","#authSigner","#ping","#send","#ws","#live","#liveByFp","#liveBySubId","#subs","#connected","#scheduleReconnect","#acceptEvent","#challenge","#status","#gen","#connecting","#intentionalClose","#skipReconnect","#deathHandled","#clearReconnectTimer","#reconnectAttempts","#socketHandlers","#handleSocketDeath","#connectTimer","#connectFinish","#authPromise","#authedChallenge","#onMessage","#rejectPublishes","#rejectCounts","#rejectNeg","#detachSocketHandlers","#teardownSocket","#reconnectTimer","#publishes","#counts","#neg","#authThenRecount","#authThenResubscribe","#authThenRepublish","#verifyDead","#ensureAuthed","#serial","#relays","#opts","#lastActivity","#idleTimeoutMs","#allowInsecure","#trustedInsecure","#idleTimer","#rejectInsecure","#touch","#stopIdleCleanup"],"sources":["../src/relay/error.ts","../src/relay/websocket.ts","../src/relay/url.ts","../src/relay/subscription.ts","../src/relay/subscribe.ts","../src/relay/neg-session.ts","../src/relay/ping.ts","../src/relay/relay.ts","../src/relay/fan-in.ts","../src/relay/pool.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n\nexport class RelayError extends NostrError {\n readonly url?: string;\n\n constructor(message: string, url?: string) {\n super(url ? `${message} (${url})` : message);\n this.url = url;\n }\n}\n\nexport class RelayConnectionError extends RelayError {}\nexport class RelayPublishError extends RelayError {}\nexport class RelayClosedError extends RelayError {}\n","import { RelayConnectionError } from \"./error.ts\";\n\n/** Minimal WebSocket surface used by Relay (browser or `ws`). */\nexport type WebSocketLike = {\n readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: string, listener: (ev: unknown) => void): void;\n removeEventListener(type: string, listener: (ev: unknown) => void): void;\n ping?(): void;\n /** Node `ws` emits `pong` on the EventEmitter; `addEventListener(\"pong\")` is a no-op. */\n once?(event: string, listener: (...args: unknown[]) => void): void;\n on?(event: string, listener: (...args: unknown[]) => void): void;\n off?(event: string, listener: (...args: unknown[]) => void): void;\n};\n\nexport type WebSocketConstructor = {\n new (url: string): WebSocketLike;\n readonly OPEN: number;\n readonly CONNECTING: number;\n readonly CLOSING: number;\n readonly CLOSED: number;\n};\n\nlet impl: WebSocketConstructor | undefined;\n\ntry {\n if (typeof globalThis.WebSocket !== \"undefined\") {\n impl = globalThis.WebSocket as unknown as WebSocketConstructor;\n }\n} catch {\n // no global WebSocket\n}\n\n/** Inject a WebSocket implementation (required on Node unless undici/global is present). */\nexport function useWebSocketImplementation(websocketImplementation: WebSocketConstructor): void {\n impl = websocketImplementation;\n}\n\nexport function getWebSocketImplementation(): WebSocketConstructor {\n if (!impl) {\n throw new RelayConnectionError(\n \"No WebSocket implementation available; call useWebSocketImplementation() or run in a browser\",\n );\n }\n return impl;\n}\n","/**\n * True for `ws:` / `http:` URLs that are not `.onion`.\n * Local-network `ws://` is still insecure; allow those via a trusted set.\n */\nexport function isInsecureRelayUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n if (parsed.hostname.endsWith(\".onion\")) return false;\n return parsed.protocol === \"ws:\" || parsed.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n","import type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { createSubscriptionId } from \"../core/message.ts\";\nimport { RelayClosedError } from \"./error.ts\";\n\nexport type SubscriptionHandlers = {\n onevent?: (event: Event) => void;\n oneose?: () => void;\n onclose?: (reason: string) => void;\n /** Skip verify + onevent when true. Evaluated after parse, before verify. */\n alreadyHaveEvent?: (id: string) => boolean;\n /**\n * Fired for every EVENT id this sub sees, including duplicates and\n * alreadyHaveEvent hits, after parse and before the verify skip.\n */\n receivedEvent?: (id: string) => void;\n};\n\nexport type SubscribeOptions = SubscriptionHandlers & {\n id?: string;\n /**\n * If set, fire `oneose` once after this many ms if EOSE has not arrived.\n * Does not close the REQ. `Relay.fetch` is the one-shot closer.\n */\n eoseTimeoutMs?: number;\n /**\n * One-shot REQ: do not join a live coalescing group, and close on EOSE.\n * Default false (live). `Relay.fetch` passes true.\n */\n closeOnEose?: boolean;\n signal?: AbortSignal;\n};\n\nexport class Subscription {\n readonly id: string;\n readonly filters: Filter[];\n readonly handlers: SubscriptionHandlers;\n readonly closeOnEose: boolean;\n eosed = false;\n closed = false;\n /** True after one CLOSED `auth-required:` retry. */\n authRetried = false;\n /** Inclusive NIP-01 `since` watermark from verified EVENTs. */\n lastCreatedAt: number | undefined;\n /** Event ids at `lastCreatedAt` (same-second reconnect dedup). Not all seen ids. */\n readonly idsAtWatermark = new Set<string>();\n #abort: (() => void) | undefined;\n\n constructor(\n filters: Filter[],\n opts: SubscribeOptions,\n private readonly sendClose: (id: string) => void,\n ) {\n this.id = createSubscriptionId(opts.id);\n this.filters = filters;\n this.closeOnEose = opts.closeOnEose === true;\n this.handlers = {\n onevent: opts.onevent,\n oneose: opts.oneose,\n onclose: opts.onclose,\n alreadyHaveEvent: opts.alreadyHaveEvent,\n receivedEvent: opts.receivedEvent,\n };\n\n if (opts.signal) {\n if (opts.signal.aborted) {\n this.close(\"aborted\");\n } else {\n const onAbort = () => this.close(\"aborted\");\n opts.signal.addEventListener(\"abort\", onAbort, { once: true });\n this.#abort = () => opts.signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n }\n\n close(reason = \"closed by client\"): void {\n if (this.closed) return;\n this.closed = true;\n this.#abort?.();\n this.sendClose(this.id);\n this.handlers.onclose?.(reason);\n }\n\n /** Advance the reconnect watermark after a verified EVENT. */\n noteVerified(event: Event): void {\n if (this.lastCreatedAt === undefined || event.created_at > this.lastCreatedAt) {\n this.lastCreatedAt = event.created_at;\n this.idsAtWatermark.clear();\n this.idsAtWatermark.add(event.id);\n return;\n }\n if (event.created_at === this.lastCreatedAt) this.idsAtWatermark.add(event.id);\n }\n\n /**\n * Filters for re-REQ. Original `filters` stay unchanged.\n * NIP-01 `since` is inclusive — never `lastCreatedAt + 1`.\n */\n replayFilters(): Filter[] {\n const since = this.lastCreatedAt;\n if (since === undefined) return this.filters;\n return this.filters.map((f) => ({\n ...f,\n since: f.since === undefined ? since : Math.max(f.since, since),\n }));\n }\n}\n\n/** AsyncIterable wrapper over a subscription's events until EOSE or close. */\nexport function subscriptionToAsyncIterable(\n start: (handlers: SubscriptionHandlers) => { close: (reason?: string) => void },\n opts?: { signal?: AbortSignal; includeEose?: boolean },\n): AsyncIterable<Event> & { close: (reason?: string) => void } {\n const queue: Event[] = [];\n let done = false;\n let error: Error | undefined;\n let wake: (() => void) | undefined;\n let closer: { close: (reason?: string) => void } | undefined;\n\n const notify = () => {\n wake?.();\n wake = undefined;\n };\n\n closer = start({\n onevent(event) {\n queue.push(event);\n notify();\n },\n oneose() {\n if (opts?.includeEose === false) {\n done = true;\n closer?.close(\"eose\");\n notify();\n }\n },\n onclose(reason) {\n if (reason && reason !== \"eose\" && reason !== \"closed by client\" && reason !== \"aborted\") {\n error = new RelayClosedError(reason);\n }\n done = true;\n notify();\n },\n });\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n closer.close(\"aborted\");\n done = true;\n } else {\n opts.signal.addEventListener(\n \"abort\",\n () => {\n closer?.close(\"aborted\");\n done = true;\n notify();\n },\n { once: true },\n );\n }\n }\n\n return {\n close(reason?: string) {\n closer?.close(reason);\n done = true;\n notify();\n },\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<Event>> {\n while (true) {\n if (queue.length > 0) {\n return { value: queue.shift()!, done: false };\n }\n if (error) throw error;\n if (done) return { value: undefined, done: true };\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n },\n async return(): Promise<IteratorResult<Event>> {\n closer?.close(\"iterator returned\");\n done = true;\n return { value: undefined, done: true };\n },\n };\n },\n };\n}\n","/** REQ subscription runtime: exclusive one-shot, live coalescing, dispatch, reconnect replay. */\nimport type { Event } from \"../core/event.ts\";\nimport { filterFingerprint, type Filter } from \"../core/filter.ts\";\nimport type { ClientMessage, SubscriptionId } from \"../core/message.ts\";\nimport { RelayConnectionError } from \"./error.ts\";\nimport {\n Subscription,\n subscriptionToAsyncIterable,\n type SubscribeOptions,\n} from \"./subscription.ts\";\n\nexport type LiveGroup = {\n fp: string;\n sub: Subscription;\n attachments: Set<Subscription>;\n};\n\nexport type LiveCtx = {\n liveByFp: Map<string, LiveGroup>;\n liveBySubId: Map<SubscriptionId, LiveGroup>;\n subs: Map<SubscriptionId, Subscription>;\n connected: () => boolean;\n enableReconnect: () => boolean;\n send: (message: ClientMessage) => void;\n scheduleReconnect: () => void;\n acceptEvent: (event: Event) => boolean;\n armEoseTimeout: (sub: Subscription, ms: number) => void;\n};\n\n// Independent live attachments must not abort sibling delivery or skip watermark.\nexport function captureListenerError(errors: unknown[], fn: () => void): void {\n try {\n fn();\n } catch (err) {\n errors.push(err);\n }\n}\n\nexport function flushListenerErrors(errors: unknown[]): void {\n if (errors.length === 0) return;\n if (errors.length === 1) throw errors[0];\n throw new AggregateError(errors);\n}\n\nexport function openExclusive(\n ctx: LiveCtx,\n filters: Filter[],\n opts: SubscribeOptions,\n): Subscription {\n const sub = new Subscription(filters, opts, (id) => {\n ctx.subs.delete(id);\n try {\n if (ctx.connected()) ctx.send([\"CLOSE\", id]);\n } catch {\n // ignore\n }\n });\n\n ctx.subs.set(sub.id, sub);\n if (ctx.connected()) {\n ctx.send([\"REQ\", sub.id, ...filters]);\n } else if (ctx.enableReconnect()) {\n ctx.scheduleReconnect();\n }\n\n if (opts.eoseTimeoutMs !== undefined) ctx.armEoseTimeout(sub, opts.eoseTimeoutMs);\n return sub;\n}\n\nexport function subscribeLive(\n ctx: LiveCtx,\n filters: Filter[],\n opts: SubscribeOptions,\n): Subscription {\n const fp = filterFingerprint(filters);\n let group = ctx.liveByFp.get(fp);\n let created = false;\n if (!group) {\n created = true;\n const wire = new Subscription(filters, { id: opts.id }, () => {\n endLiveGroup(ctx, fp, { sendClose: true, reason: \"closed by client\" });\n });\n group = { fp, sub: wire, attachments: new Set() };\n ctx.liveByFp.set(fp, group);\n ctx.liveBySubId.set(wire.id, group);\n ctx.subs.set(wire.id, wire);\n }\n\n const slot: { handle: Subscription | undefined } = { handle: undefined };\n const handle = new Subscription(filters, { ...opts, id: group.sub.id }, () => {\n // constructor may close on an already-aborted signal before `handle` is assigned\n const current = slot.handle;\n if (!current) return;\n detachLive(ctx, fp, current);\n });\n slot.handle = handle;\n\n if (handle.closed) {\n if (created && group.attachments.size === 0) {\n forgetLiveGroup(ctx, group);\n }\n return handle;\n }\n\n group.attachments.add(handle);\n\n if (created) {\n if (ctx.connected()) {\n ctx.send([\"REQ\", group.sub.id, ...filters]);\n } else if (ctx.enableReconnect()) {\n ctx.scheduleReconnect();\n }\n }\n\n if (opts.eoseTimeoutMs !== undefined) ctx.armEoseTimeout(handle, opts.eoseTimeoutMs);\n\n if (group.sub.eosed) {\n queueMicrotask(() => {\n if (handle.closed || handle.eosed) return;\n handle.eosed = true;\n handle.handlers.oneose?.();\n });\n }\n\n return handle;\n}\n\nexport function forgetLiveGroup(ctx: LiveCtx, group: LiveGroup): void {\n ctx.liveByFp.delete(group.fp);\n ctx.liveBySubId.delete(group.sub.id);\n ctx.subs.delete(group.sub.id);\n group.sub.closed = true;\n}\n\nexport function detachLive(ctx: LiveCtx, fp: string, handle: Subscription): void {\n const group = ctx.liveByFp.get(fp);\n if (!group) return;\n if (!group.attachments.delete(handle)) return;\n if (group.attachments.size > 0) return;\n endLiveGroup(ctx, fp, { sendClose: true, reason: \"closed by client\" });\n}\n\nexport function endLiveGroup(\n ctx: LiveCtx,\n fp: string,\n opts: { sendClose: boolean; reason: string },\n): void {\n const group = ctx.liveByFp.get(fp);\n if (!group) return;\n const id = group.sub.id;\n const remaining = [...group.attachments];\n group.attachments.clear();\n forgetLiveGroup(ctx, group);\n if (opts.sendClose) {\n try {\n if (ctx.connected()) ctx.send([\"CLOSE\", id]);\n } catch {\n // ignore\n }\n }\n const errors: unknown[] = [];\n for (const att of remaining) {\n captureListenerError(errors, () => {\n att.close(opts.reason);\n });\n }\n flushListenerErrors(errors);\n}\n\nexport function deliverLiveEvent(ctx: LiveCtx, group: LiveGroup, event: Event): void {\n const sub = group.sub;\n const attachments = [...group.attachments];\n const errors: unknown[] = [];\n for (const att of attachments) {\n captureListenerError(errors, () => {\n att.handlers.receivedEvent?.(event.id);\n });\n }\n if (sub.idsAtWatermark.has(event.id)) {\n flushListenerErrors(errors);\n return;\n }\n\n const recipients: Subscription[] = [];\n for (const att of attachments) {\n if (att.closed) continue;\n let skip = false;\n captureListenerError(errors, () => {\n skip = Boolean(att.handlers.alreadyHaveEvent?.(event.id));\n });\n if (!skip) recipients.push(att);\n }\n if (recipients.length === 0) {\n flushListenerErrors(errors);\n return;\n }\n if (!ctx.acceptEvent(event)) {\n flushListenerErrors(errors);\n return;\n }\n\n sub.noteVerified(event);\n for (const att of recipients) att.noteVerified(event);\n for (const att of recipients) {\n captureListenerError(errors, () => {\n att.handlers.onevent?.(event);\n });\n }\n flushListenerErrors(errors);\n}\n\nexport function deliverLiveEose(group: LiveGroup): void {\n const attachments = Array.from(group.attachments);\n const errors: unknown[] = [];\n for (const att of attachments) {\n if (att.closed || att.eosed) continue;\n att.eosed = true;\n captureListenerError(errors, () => {\n att.handlers.oneose?.();\n });\n }\n flushListenerErrors(errors);\n}\n\nexport function closeAllSubscriptions(ctx: LiveCtx, reason: string): void {\n const errors: unknown[] = [];\n const fps = Array.from(ctx.liveByFp.keys());\n for (const fp of fps) {\n captureListenerError(errors, () => {\n endLiveGroup(ctx, fp, { sendClose: false, reason });\n });\n }\n for (const sub of ctx.subs.values()) {\n if (!sub.closed) {\n sub.closed = true;\n captureListenerError(errors, () => {\n sub.handlers.onclose?.(reason);\n });\n }\n }\n ctx.subs.clear();\n ctx.liveByFp.clear();\n ctx.liveBySubId.clear();\n flushListenerErrors(errors);\n}\n\nexport function dropSubscription(ctx: LiveCtx, sub: Subscription, reason: string): void {\n const group = ctx.liveBySubId.get(sub.id);\n if (group) {\n endLiveGroup(ctx, group.fp, { sendClose: false, reason });\n return;\n }\n ctx.subs.delete(sub.id);\n sub.closed = true;\n sub.handlers.onclose?.(reason);\n}\n\nexport function onSubEvent(ctx: LiveCtx, subId: string, event: Event): void {\n const sub = ctx.subs.get(subId);\n if (!sub || sub.closed) return;\n const group = ctx.liveBySubId.get(subId);\n if (group) {\n deliverLiveEvent(ctx, group, event);\n return;\n }\n sub.handlers.receivedEvent?.(event.id);\n if (sub.idsAtWatermark.has(event.id)) return;\n if (sub.handlers.alreadyHaveEvent?.(event.id)) return;\n if (!ctx.acceptEvent(event)) return;\n sub.noteVerified(event);\n sub.handlers.onevent?.(event);\n}\n\nexport function onSubEose(ctx: LiveCtx, subId: string): void {\n const sub = ctx.subs.get(subId);\n if (!sub || sub.closed) return;\n if (sub.eosed) return;\n sub.eosed = true;\n const group = ctx.liveBySubId.get(subId);\n if (group) {\n deliverLiveEose(group);\n } else {\n sub.handlers.oneose?.();\n if (sub.closeOnEose) sub.close(\"eose\");\n }\n}\n\nexport function resubscribeAll(ctx: LiveCtx): boolean {\n for (const sub of ctx.subs.values()) {\n if (sub.closed) continue;\n sub.eosed = false;\n sub.authRetried = false;\n const group = ctx.liveBySubId.get(sub.id);\n if (group) {\n for (const att of group.attachments) att.eosed = false;\n }\n try {\n ctx.send([\"REQ\", sub.id, ...sub.replayFilters()]);\n } catch {\n return false;\n }\n }\n return true;\n}\n\nexport function streamFilters(\n subscribe: (filters: Filter[], opts: SubscribeOptions) => Subscription,\n filters: Filter[],\n opts?: { signal?: AbortSignal; id?: string },\n): AsyncIterable<Event> & { close: (reason?: string) => void } {\n return subscriptionToAsyncIterable(\n (handlers) => subscribe(filters, { ...handlers, id: opts?.id, signal: opts?.signal }),\n { signal: opts?.signal },\n );\n}\n\nexport async function fetchFilters(\n subscribe: (filters: Filter[], opts: SubscribeOptions) => Subscription,\n filters: Filter[],\n opts: { timeoutMs: number; signal?: AbortSignal; id?: string; url: string },\n): Promise<Event[]> {\n const events: Event[] = [];\n const seen = new Set<string>();\n\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const done = (err?: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n sub.close(err ? err.message : \"fetch complete\");\n if (err) reject(err);\n else resolve();\n };\n\n const timer = setTimeout(() => done(), opts.timeoutMs);\n\n const sub = subscribe(filters, {\n id: opts.id,\n signal: opts.signal,\n closeOnEose: true,\n onevent(event) {\n if (seen.has(event.id)) return;\n seen.add(event.id);\n events.push(event);\n },\n oneose() {\n done();\n },\n onclose() {\n if (!settled) done();\n },\n });\n\n opts.signal?.addEventListener(\n \"abort\",\n () => done(new RelayConnectionError(\"fetch aborted\", opts.url)),\n { once: true },\n );\n });\n\n return events;\n}\n\nexport function armEoseTimeout(sub: Subscription, eoseTimeoutMs: number): void {\n const timer = setTimeout(() => {\n if (sub.eosed || sub.closed) return;\n sub.eosed = true;\n sub.handlers.oneose?.();\n }, eoseTimeoutMs);\n const prevClose = sub.handlers.onclose;\n sub.handlers.onclose = (reason) => {\n clearTimeout(timer);\n prevClose?.(reason);\n };\n const prevEose = sub.handlers.oneose;\n sub.handlers.oneose = () => {\n clearTimeout(timer);\n prevEose?.();\n };\n}\n","import type { Filter } from \"../core/filter.ts\";\nimport type { ClientMessage } from \"../core/message.ts\";\nimport { Nip77Error, runNegSession, type NegentropyStorageVector } from \"../nips/nip77.ts\";\nimport { RelayConnectionError, RelayPublishError } from \"./error.ts\";\n\nexport type NegSession = {\n queue: string[];\n waiter:\n | {\n resolve: (hex: string) => void;\n reject: (err: Error) => void;\n }\n | undefined;\n error: Error | undefined;\n};\n\nexport function createNegSession(): NegSession {\n return { queue: [], waiter: undefined, error: undefined };\n}\n\nexport function pushNegMsg(session: NegSession, hex: string): void {\n if (session.waiter) {\n const waiter = session.waiter;\n session.waiter = undefined;\n waiter.resolve(hex);\n } else {\n session.queue.push(hex);\n }\n}\n\nexport function failNegSession(session: NegSession, err: Error): void {\n session.error = err;\n if (session.waiter) {\n const waiter = session.waiter;\n session.waiter = undefined;\n waiter.reject(err);\n }\n}\n\nexport function failNegErr(session: NegSession, reason: string): void {\n failNegSession(session, new Nip77Error(reason));\n}\n\n/** Wire queue/timeout/abort around `runNegSession`. Not a second session class. */\nexport async function runWiredNegSession(opts: {\n session: NegSession;\n storage: NegentropyStorageVector;\n filter: Filter;\n id: string;\n timeoutMs: number;\n signal?: AbortSignal;\n send: (message: ClientMessage) => void;\n url: string;\n}): Promise<{ have: string[]; need: string[] }> {\n const { session, storage, filter, id, timeoutMs, signal, send, url } = opts;\n const deadline = Date.now() + timeoutMs;\n\n const timedOut = (): RelayPublishError => new RelayPublishError(\"negentropy timed out\", url);\n\n const remainingMs = (): number => deadline - Date.now();\n\n const next = (): Promise<string> => {\n if (session.error) return Promise.reject(session.error);\n if (remainingMs() <= 0) return Promise.reject(timedOut());\n const queued = session.queue.shift();\n if (queued !== undefined) return Promise.resolve(queued);\n return new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n session.waiter = undefined;\n reject(timedOut());\n }, remainingMs());\n const fail = (err: Error): void => {\n clearTimeout(timer);\n session.waiter = undefined;\n reject(err);\n };\n const onAbort = (): void => fail(new RelayConnectionError(\"negentropy aborted\", url));\n session.waiter = {\n resolve: (hex) => {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onAbort);\n resolve(hex);\n },\n reject: (err) => fail(err),\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n };\n\n return await runNegSession({\n storage,\n openingSend: (hex) => {\n send([\"NEG-OPEN\", id, filter, hex]);\n },\n msgSend: (hex) => {\n send([\"NEG-MSG\", id, hex]);\n },\n next,\n });\n}\n","import type { Filter } from \"../core/filter.ts\";\nimport type { ClientMessage, SubscriptionId } from \"../core/message.ts\";\nimport type { WebSocketLike } from \"./websocket.ts\";\n\nexport const DEFAULT_PING_INTERVAL_MS = 29_000;\nexport const DEFAULT_PING_TIMEOUT_MS = 20_000;\n\nconst PING_DUMMY_ID = \"a\".repeat(64);\nconst PING_DUMMY_FILTER: Filter = { ids: [PING_DUMMY_ID], limit: 0 };\n/** Browser WebSocket and `ws` both use 1 for OPEN. */\nconst WS_OPEN = 1;\n\ntype PingWaiter = {\n resolve: (alive: boolean) => void;\n};\n\nexport type PingLoopOpts = {\n send: (message: ClientMessage) => void;\n nextSubId: (prefix: string) => string;\n getWs: () => WebSocketLike | undefined;\n closeWs: () => void;\n pingIntervalMs: number;\n pingTimeoutMs: number;\n};\n\n/** node `ws` delivers `pong` on the EventEmitter, not via addEventListener. */\nexport function canNativePing(ws: WebSocketLike): boolean {\n return (\n typeof ws.ping === \"function\" && (typeof ws.once === \"function\" || typeof ws.on === \"function\")\n );\n}\n\n/** REQ-dummy or native ping/pong keepalive. Does not import class Relay. */\nexport class PingLoop {\n #opts: PingLoopOpts;\n #timer: ReturnType<typeof setInterval> | undefined;\n #waiters = new Map<SubscriptionId, PingWaiter>();\n #gen = 0;\n #nativePing: { abort: () => void } | undefined;\n\n constructor(opts: PingLoopOpts) {\n this.#opts = opts;\n }\n\n start(): void {\n this.stop();\n this.#timer = setInterval(() => {\n void this.#pingpong();\n }, this.#opts.pingIntervalMs);\n }\n\n stop(): void {\n if (this.#timer !== undefined) {\n clearInterval(this.#timer);\n this.#timer = undefined;\n }\n this.#abortCurrentPing();\n }\n\n hasWaiter(id: string): boolean {\n return this.#waiters.has(id);\n }\n\n finishDummyPing(id: string): boolean {\n const waiter = this.#waiters.get(id);\n if (!waiter) return false;\n this.#waiters.delete(id);\n waiter.resolve(true);\n try {\n this.#opts.send([\"CLOSE\", id]);\n } catch {\n // socket already gone\n }\n return true;\n }\n\n #abortCurrentPing(): void {\n this.#gen += 1;\n const native = this.#nativePing;\n this.#nativePing = undefined;\n native?.abort();\n for (const waiter of this.#waiters.values()) waiter.resolve(false);\n this.#waiters.clear();\n }\n\n async #pingpong(): Promise<void> {\n const ws = this.#opts.getWs();\n if (!ws || ws.readyState !== WS_OPEN) return;\n if (this.#nativePing || this.#waiters.size > 0) return;\n\n const gen = this.#gen;\n const ping = canNativePing(ws) ? this.#waitForNativePing(ws) : this.#waitForDummyPing();\n const ok = await new Promise<boolean>((resolve) => {\n let settled = false;\n const done = (alive: boolean) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(alive);\n };\n const timer = setTimeout(() => {\n if (this.#nativePing || this.#waiters.size > 0) done(false);\n }, this.#opts.pingTimeoutMs);\n void ping.then(done);\n });\n\n if (gen !== this.#gen) return;\n\n if (!ok) {\n this.#abortCurrentPing();\n this.#opts.closeWs();\n }\n }\n\n #waitForNativePing(ws: WebSocketLike): Promise<boolean> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (alive: boolean) => {\n if (settled) return;\n settled = true;\n this.#nativePing = undefined;\n ws.off?.(\"pong\", onPong);\n resolve(alive);\n };\n const onPong = () => finish(true);\n this.#nativePing = { abort: () => finish(false) };\n\n // node `ws` once() wraps the listener; off(fn) would miss it.\n if (typeof ws.on === \"function\" && typeof ws.off === \"function\") {\n ws.on(\"pong\", onPong);\n } else if (typeof ws.once === \"function\") {\n ws.once(\"pong\", onPong);\n } else {\n ws.on!(\"pong\", onPong);\n }\n try {\n ws.ping!();\n } catch {\n finish(false);\n }\n });\n }\n\n #waitForDummyPing(): Promise<boolean> {\n return new Promise((resolve) => {\n const id = this.#opts.nextSubId(\"__ping__\");\n this.#waiters.set(id, { resolve });\n try {\n this.#opts.send([\"REQ\", id, PING_DUMMY_FILTER]);\n } catch {\n this.#waiters.delete(id);\n resolve(false);\n }\n });\n }\n}\n","import type { Event, EventTemplate } from \"../core/event.ts\";\nimport { verifyEvent } from \"../core/key.ts\";\nimport { canonicalizeFilter, canonicalizeFilters, type Filter } from \"../core/filter.ts\";\nimport { MessageError, WasmVerifyPoisonedError } from \"../core/error.ts\";\nimport {\n assertSubscriptionId,\n createSubscriptionId,\n encodeClientMessage,\n parseRelayMessage,\n type ClientMessage,\n type CountResult,\n type SubscriptionId,\n} from \"../core/message.ts\";\nimport { Nip77Error, type NegentropyStorageVector } from \"../nips/nip77.ts\";\nimport { isAuthRequired, makeAuthEvent } from \"../nips/nip42.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { RelayClosedError, RelayConnectionError, RelayError, RelayPublishError } from \"./error.ts\";\nimport {\n armEoseTimeout,\n closeAllSubscriptions,\n dropSubscription,\n fetchFilters,\n onSubEose,\n onSubEvent,\n openExclusive,\n resubscribeAll,\n streamFilters,\n subscribeLive,\n type LiveCtx,\n type LiveGroup,\n} from \"./subscribe.ts\";\nimport {\n createNegSession,\n failNegErr,\n failNegSession,\n pushNegMsg,\n runWiredNegSession,\n type NegSession,\n} from \"./neg-session.ts\";\nimport { DEFAULT_PING_INTERVAL_MS, DEFAULT_PING_TIMEOUT_MS, PingLoop } from \"./ping.ts\";\nimport type { SubscribeOptions, Subscription, SubscriptionHandlers } from \"./subscription.ts\";\nimport {\n getWebSocketImplementation,\n type WebSocketConstructor,\n type WebSocketLike,\n} from \"./websocket.ts\";\n\nexport const RelayStatus = {\n Initialized: \"initialized\",\n Connecting: \"connecting\",\n Connected: \"connected\",\n Disconnected: \"disconnected\",\n Closed: \"closed\",\n} as const;\nexport type RelayStatusName = (typeof RelayStatus)[keyof typeof RelayStatus];\n\nexport type RelayOptions = {\n websocketImplementation?: WebSocketConstructor;\n verifyEvent?: (event: Event) => boolean;\n publishTimeoutMs?: number;\n connectTimeoutMs?: number;\n /**\n * When true, unexpected disconnects schedule reconnect with backoff and\n * re-fire open subscriptions. Default false.\n */\n enableReconnect?: boolean;\n /** Backoff delays in ms between reconnect attempts. */\n reconnectBackoffMs?: number[];\n enablePing?: boolean;\n pingIntervalMs?: number;\n pingTimeoutMs?: number;\n /** When set, CLOSED/OK `auth-required:` triggers AUTH then retries the REQ/EVENT/COUNT. */\n authSigner?: (template: EventTemplate) => Promise<Event>;\n};\n\nexport type PublishResult = {\n ok: boolean;\n message: string;\n};\n\nexport type { CountResult };\n\ntype PublishWaiter = {\n resolve: (result: PublishResult) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout> | undefined;\n event?: Event;\n authRetried?: boolean;\n timeoutMs: number;\n};\n\ntype CountWaiter = {\n resolve: (result: CountResult) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout> | undefined;\n filters: Filter[];\n authRetried: boolean;\n timeoutMs: number;\n};\n\ntype SocketHandlers = {\n onOpen: () => void;\n onError: () => void;\n onClose: () => void;\n onMessage: (ev: unknown) => void;\n ws: WebSocketLike;\n};\n\nconst DEFAULT_BACKOFF = [1_000, 2_000, 5_000, 10_000, 20_000, 30_000, 60_000];\n\n/**\n * Single-relay NIP-01 client.\n * Connection lifecycle, REQ/CLOSE, EVENT publish ACK, AUTH, COUNT, NIP-77, optional reconnect.\n */\nexport class Relay {\n readonly url: string;\n #ws: WebSocketLike | undefined;\n #connected = false;\n #gen = 0;\n #status: RelayStatusName = RelayStatus.Initialized;\n #connecting: Promise<void> | undefined;\n #connectFinish: ((err?: unknown) => void) | undefined;\n #connectTimer: ReturnType<typeof setTimeout> | undefined;\n #socketHandlers: SocketHandlers | undefined;\n #subs = new Map<SubscriptionId, Subscription>();\n #liveByFp = new Map<string, LiveGroup>();\n #liveBySubId = new Map<SubscriptionId, LiveGroup>();\n #live: LiveCtx;\n #publishes = new Map<string, PublishWaiter>();\n #counts = new Map<string, CountWaiter>();\n #neg = new Map<SubscriptionId, NegSession>();\n #WS: WebSocketConstructor;\n #verify: (event: Event) => boolean;\n #verifyDead = false;\n #publishTimeoutMs: number;\n #connectTimeoutMs: number;\n #enableReconnect: boolean;\n #backoff: number[];\n #serial = 0;\n #challenge: string | undefined;\n #authedChallenge: string | undefined;\n #authPromise: Promise<PublishResult> | undefined;\n #authSigner: ((template: EventTemplate) => Promise<Event>) | undefined;\n #intentionalClose = false;\n #reconnectAttempts = 0;\n #reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n #skipReconnect = false;\n /** Collapses error+close pairs into a single terminal/reconnect action. */\n #deathHandled = false;\n #enablePing: boolean;\n #ping: PingLoop;\n\n onnotice: ((msg: string) => void) | null = null;\n onclose: (() => void) | null = null;\n /** Fired when the relay sends a NIP-42 AUTH challenge. */\n onauth: ((challenge: string) => void) | null = null;\n /** Fired after a successful reconnect (not the initial connect). */\n onreconnect: (() => void) | null = null;\n\n constructor(url: string, opts: RelayOptions = {}) {\n this.url = normalizeURL(url);\n this.#WS = opts.websocketImplementation ?? getWebSocketImplementation();\n this.#verify = opts.verifyEvent ?? verifyEvent;\n this.#publishTimeoutMs = opts.publishTimeoutMs ?? 4400;\n this.#connectTimeoutMs = opts.connectTimeoutMs ?? 5000;\n this.#enableReconnect = opts.enableReconnect ?? false;\n this.#backoff = opts.reconnectBackoffMs ?? DEFAULT_BACKOFF;\n this.#enablePing = opts.enablePing ?? false;\n this.#authSigner = opts.authSigner;\n this.#ping = new PingLoop({\n send: (message) => this.#send(message),\n nextSubId: (prefix) => this.nextSubId(prefix),\n getWs: () => this.#ws,\n closeWs: () => {\n const ws = this.#ws;\n if (ws && ws.readyState === this.#WS.OPEN) {\n try {\n ws.close();\n } catch {\n // ignore\n }\n }\n },\n pingIntervalMs: opts.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,\n pingTimeoutMs: opts.pingTimeoutMs ?? DEFAULT_PING_TIMEOUT_MS,\n });\n this.#live = {\n liveByFp: this.#liveByFp,\n liveBySubId: this.#liveBySubId,\n subs: this.#subs,\n connected: () => this.#connected,\n enableReconnect: () => this.#enableReconnect,\n send: (message) => this.#send(message),\n scheduleReconnect: () => this.#scheduleReconnect(),\n acceptEvent: (event) => this.#acceptEvent(event),\n armEoseTimeout,\n };\n }\n\n /** Latest NIP-42 challenge, if any. */\n get challenge(): string | undefined {\n return this.#challenge;\n }\n\n get connected(): boolean {\n return this.#connected;\n }\n\n get status(): RelayStatusName {\n return this.#status;\n }\n\n get generation(): number {\n return this.#gen;\n }\n\n get reconnectEnabled(): boolean {\n return this.#enableReconnect;\n }\n\n /** User subscriptions only; dummy ping REQs are not counted. */\n get subscriptionCount(): number {\n return this.#subs.size;\n }\n\n static async connect(\n url: string,\n opts?: RelayOptions & { signal?: AbortSignal },\n ): Promise<Relay> {\n const relay = new Relay(url, opts);\n await relay.connect({ signal: opts?.signal });\n return relay;\n }\n\n async connect(opts?: { signal?: AbortSignal; timeoutMs?: number }): Promise<void> {\n if (this.#connected) return;\n if (this.#connecting) return await this.#connecting;\n\n const gen = ++this.#gen;\n this.#intentionalClose = false;\n this.#skipReconnect = false;\n this.#deathHandled = false;\n this.#clearReconnectTimer();\n this.#status = RelayStatus.Connecting;\n\n const timeoutMs = opts?.timeoutMs ?? this.#connectTimeoutMs;\n const isReconnect = this.#reconnectAttempts > 0;\n\n let resolveConnect = (): void => {};\n let rejectConnect = (_err: Error): void => {};\n let settled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let ws: WebSocketLike | undefined;\n let handlers: SocketHandlers | undefined;\n\n const connecting = new Promise<void>((resolve, reject) => {\n resolveConnect = resolve;\n rejectConnect = reject;\n });\n\n const release = (): void => {\n if (!ws) return;\n if (handlers) {\n try {\n ws.removeEventListener(\"open\", handlers.onOpen);\n ws.removeEventListener(\"error\", handlers.onError);\n ws.removeEventListener(\"close\", handlers.onClose);\n ws.removeEventListener(\"message\", handlers.onMessage);\n } catch {\n // ignore\n }\n if (this.#socketHandlers === handlers) this.#socketHandlers = undefined;\n }\n try {\n if (ws.readyState !== this.#WS.CLOSED && ws.readyState !== this.#WS.CLOSING) {\n ws.close();\n }\n } catch {\n // ignore\n }\n if (this.#ws === ws) this.#ws = undefined;\n };\n\n const onAbort = (): void => {\n if (gen !== this.#gen) {\n release();\n return;\n }\n this.#intentionalClose = true;\n this.#skipReconnect = true;\n release();\n finish(new RelayConnectionError(\"connection aborted\", this.url));\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"connection aborted\", { fromConnectAttempt: true, gen });\n }\n };\n\n const finish = (err?: unknown): void => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n if (this.#connectTimer === timer) this.#connectTimer = undefined;\n opts?.signal?.removeEventListener(\"abort\", onAbort);\n if (this.#connectFinish === finish) this.#connectFinish = undefined;\n if (this.#connecting === connecting) this.#connecting = undefined;\n if (err) {\n rejectConnect(\n err instanceof Error ? err : new RelayConnectionError(\"connection failed\", this.url),\n );\n } else {\n resolveConnect();\n }\n };\n\n this.#connecting = connecting;\n this.#connectFinish = finish;\n\n timer = setTimeout(() => {\n if (gen !== this.#gen) {\n release();\n return;\n }\n if (!isReconnect && !this.#enableReconnect) this.#skipReconnect = true;\n release();\n finish(new RelayConnectionError(\"connection timed out\", this.url));\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"connection timed out\", { fromConnectAttempt: true, gen });\n }\n }, timeoutMs);\n this.#connectTimer = timer;\n\n opts?.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n ws = new this.#WS(this.url);\n } catch (err) {\n if (!isReconnect && !this.#enableReconnect) this.#skipReconnect = true;\n finish(err);\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"connection failed\", { fromConnectAttempt: true, gen });\n }\n await connecting;\n return;\n }\n this.#ws = ws;\n\n const onOpen = (): void => {\n if (gen !== this.#gen) {\n release();\n return;\n }\n this.#connected = true;\n this.#status = RelayStatus.Connected;\n this.#deathHandled = false;\n const wasReconnect = this.#reconnectAttempts > 0;\n this.#challenge = undefined;\n this.#authPromise = undefined;\n this.#authedChallenge = undefined;\n if (!resubscribeAll(this.#live)) {\n this.#connected = false;\n this.#status = RelayStatus.Disconnected;\n if (!isReconnect && !this.#enableReconnect) this.#skipReconnect = true;\n release();\n finish(new RelayConnectionError(\"connection failed\", this.url));\n if (\n this.#enableReconnect &&\n !this.#intentionalClose &&\n !this.#skipReconnect &&\n this.#subs.size > 0\n ) {\n this.#scheduleReconnect();\n }\n return;\n }\n this.#reconnectAttempts = 0;\n if (this.#enablePing) this.#ping.start();\n else this.#ping.stop();\n if (wasReconnect) this.onreconnect?.();\n finish();\n };\n const onError = (): void => {\n if (gen !== this.#gen) {\n release();\n return;\n }\n this.#connected = false;\n const fromConnectAttempt = !settled;\n if (!settled && !isReconnect && !this.#enableReconnect) this.#skipReconnect = true;\n release();\n finish(new RelayConnectionError(\"connection failed\", this.url));\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"connection failed\", { fromConnectAttempt, gen });\n }\n };\n const onClose = (): void => {\n if (gen !== this.#gen) {\n release();\n return;\n }\n this.#connected = false;\n const fromConnectAttempt = !settled;\n if (!settled && !isReconnect && !this.#enableReconnect) this.#skipReconnect = true;\n release();\n if (fromConnectAttempt) {\n finish(new RelayConnectionError(\"websocket closed\", this.url));\n }\n if (gen === this.#gen) {\n this.#handleSocketDeath(\"websocket closed\", { fromConnectAttempt, gen });\n }\n };\n const onMessage = (ev: unknown): void => {\n if (gen !== this.#gen) return;\n const data =\n typeof ev === \"object\" && ev !== null && \"data\" in ev ? (ev as { data: unknown }).data : ev;\n this.#onMessage(String(data));\n };\n\n handlers = { onOpen, onError, onClose, onMessage, ws };\n this.#socketHandlers = handlers;\n ws.addEventListener(\"open\", onOpen);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n ws.addEventListener(\"message\", onMessage);\n\n await connecting;\n }\n\n #detachSocketHandlers(): void {\n const h = this.#socketHandlers;\n if (!h) return;\n try {\n h.ws.removeEventListener(\"open\", h.onOpen);\n h.ws.removeEventListener(\"error\", h.onError);\n h.ws.removeEventListener(\"close\", h.onClose);\n h.ws.removeEventListener(\"message\", h.onMessage);\n } catch {\n // ignore\n }\n this.#socketHandlers = undefined;\n }\n\n /** Graceful shutdown: disables reconnect and closes all subscriptions. */\n close(): void {\n this.#gen += 1;\n this.#status = RelayStatus.Closed;\n this.#intentionalClose = true;\n this.#skipReconnect = true;\n this.#clearReconnectTimer();\n if (this.#connectTimer !== undefined) {\n clearTimeout(this.#connectTimer);\n this.#connectTimer = undefined;\n }\n this.#ping.stop();\n this.#connectFinish?.(new RelayClosedError(\"relay closed\", this.url));\n try {\n closeAllSubscriptions(this.#live, \"relay closed\");\n } finally {\n this.#rejectPublishes(new RelayClosedError(\"relay closed\", this.url));\n this.#rejectCounts(new RelayClosedError(\"relay closed\", this.url));\n this.#rejectNeg(new RelayClosedError(\"relay closed\", this.url));\n this.#detachSocketHandlers();\n this.#teardownSocket();\n this.#connected = false;\n this.onclose?.();\n }\n }\n\n #teardownSocket(): void {\n try {\n this.#ws?.close();\n } catch {\n // ignore\n }\n this.#ws = undefined;\n }\n\n #clearReconnectTimer(): void {\n if (this.#reconnectTimer !== undefined) {\n clearTimeout(this.#reconnectTimer);\n this.#reconnectTimer = undefined;\n }\n }\n\n #rejectPublishes(err: Error): void {\n for (const [, waiter] of this.#publishes) {\n clearTimeout(waiter.timer);\n waiter.reject(err);\n }\n this.#publishes.clear();\n }\n\n #rejectCounts(err: Error): void {\n for (const [, waiter] of this.#counts) {\n clearTimeout(waiter.timer);\n waiter.reject(err);\n }\n this.#counts.clear();\n }\n\n #rejectNeg(err: Error): void {\n for (const session of this.#neg.values()) failNegSession(session, err);\n this.#neg.clear();\n }\n\n /**\n * Unexpected socket death. Keep subscriptions if reconnecting.\n */\n #handleSocketDeath(reason: string, opts: { fromConnectAttempt?: boolean; gen: number }): void {\n if (opts.gen !== this.#gen) return;\n if (this.#deathHandled) return;\n this.#deathHandled = true;\n this.#ping.stop();\n this.#detachSocketHandlers();\n this.#connected = false;\n this.#ws = undefined;\n this.#rejectPublishes(new RelayClosedError(reason, this.url));\n this.#rejectCounts(new RelayClosedError(reason, this.url));\n this.#rejectNeg(new RelayClosedError(reason, this.url));\n\n const canReconnect =\n this.#enableReconnect &&\n !this.#intentionalClose &&\n !this.#skipReconnect &&\n this.#subs.size > 0;\n\n if (canReconnect) {\n this.#status = RelayStatus.Disconnected;\n this.#scheduleReconnect();\n return;\n }\n\n if (!this.#intentionalClose) this.#status = RelayStatus.Disconnected;\n\n if (!opts.fromConnectAttempt || this.#subs.size > 0) {\n closeAllSubscriptions(this.#live, reason);\n if (!this.#intentionalClose) this.onclose?.();\n }\n\n this.#status = RelayStatus.Closed;\n }\n\n #scheduleReconnect(): void {\n if (this.#reconnectTimer !== undefined) return;\n if (this.#connected) return;\n const delay =\n this.#backoff[Math.min(this.#reconnectAttempts, this.#backoff.length - 1)] ?? 60_000;\n this.#reconnectAttempts += 1;\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = undefined;\n if (this.#intentionalClose || this.#connected) return;\n void this.connect().catch(() => {\n if (\n this.#enableReconnect &&\n !this.#intentionalClose &&\n !this.#skipReconnect &&\n this.#subs.size > 0 &&\n !this.#connected\n ) {\n this.#scheduleReconnect();\n }\n });\n }, delay);\n }\n\n #send(message: ClientMessage | string): void {\n if (!this.#ws || !this.#connected) {\n throw new RelayClosedError(\"not connected\", this.url);\n }\n const raw = typeof message === \"string\" ? message : encodeClientMessage(message);\n this.#ws.send(raw);\n }\n\n #onMessage(raw: string): void {\n let msg;\n try {\n msg = parseRelayMessage(raw);\n } catch {\n return;\n }\n\n switch (msg[0]) {\n case \"EVENT\": {\n const [, subId, event] = msg;\n if (this.#ping.hasWaiter(subId)) return;\n onSubEvent(this.#live, subId, event);\n break;\n }\n case \"EOSE\": {\n const [, subId] = msg;\n if (this.#ping.finishDummyPing(subId)) return;\n onSubEose(this.#live, subId);\n break;\n }\n case \"CLOSED\": {\n const [, subId, reason] = msg;\n if (this.#ping.finishDummyPing(subId)) return;\n const countWaiter = this.#counts.get(subId);\n if (countWaiter) {\n if (isAuthRequired(reason) && this.#authSigner && !countWaiter.authRetried) {\n countWaiter.authRetried = true;\n void this.#authThenRecount(subId, countWaiter, reason);\n return;\n }\n countWaiter.reject(new RelayClosedError(reason || \"COUNT closed\", this.url));\n return;\n }\n const sub = this.#subs.get(subId);\n if (!sub) return;\n if (isAuthRequired(reason) && this.#authSigner && !sub.authRetried) {\n sub.authRetried = true;\n void this.#authThenResubscribe(sub, reason);\n return;\n }\n dropSubscription(this.#live, sub, reason);\n break;\n }\n case \"OK\": {\n const [, eventId, ok, message] = msg;\n const waiter = this.#publishes.get(eventId);\n if (!waiter) return;\n if (\n !ok &&\n isAuthRequired(message) &&\n waiter.event &&\n !waiter.authRetried &&\n this.#authSigner\n ) {\n waiter.authRetried = true;\n void this.#authThenRepublish(waiter, eventId, message);\n return;\n }\n clearTimeout(waiter.timer);\n this.#publishes.delete(eventId);\n waiter.resolve({ ok, message });\n break;\n }\n case \"COUNT\": {\n const [, countId, payload] = msg;\n const waiter = this.#counts.get(countId);\n if (!waiter) return;\n waiter.resolve(payload);\n break;\n }\n case \"NEG-MSG\": {\n const [, negId, hex] = msg;\n const session = this.#neg.get(negId);\n if (!session) return;\n pushNegMsg(session, hex);\n break;\n }\n case \"NEG-ERR\": {\n const [, negId, reason] = msg;\n const session = this.#neg.get(negId);\n if (!session) return;\n failNegErr(session, reason);\n break;\n }\n case \"NOTICE\": {\n this.onnotice?.(msg[1]);\n break;\n }\n case \"AUTH\": {\n this.#challenge = msg[1];\n this.#authPromise = undefined;\n this.#authedChallenge = undefined;\n this.onauth?.(msg[1]);\n break;\n }\n default:\n break;\n }\n }\n\n /** Low-level REQ with callbacks. Survives reconnect when enableReconnect is on. */\n subscribe(filters: Filter[], opts: SubscribeOptions = {}): Subscription {\n if (filters.length === 0) throw new MessageError(\"REQ requires at least one filter\");\n if (!this.#connected && !this.#enableReconnect) {\n throw new RelayClosedError(\"not connected\", this.url);\n }\n const canonical = canonicalizeFilters(filters);\n if (opts.closeOnEose === true) return openExclusive(this.#live, canonical, opts);\n return subscribeLive(this.#live, canonical, opts);\n }\n\n #acceptEvent(event: Event): boolean {\n if (this.#verifyDead) return false;\n try {\n return this.#verify(event);\n } catch (e) {\n if (e instanceof WasmVerifyPoisonedError || e instanceof WebAssembly.RuntimeError) {\n this.#verifyDead = true;\n this.onnotice?.(\"verify-poisoned: wasm instance aborted\");\n return false;\n }\n throw e;\n }\n }\n\n /** AsyncIterable of events for filters until the subscription is closed. */\n stream(\n filters: Filter[],\n opts?: { signal?: AbortSignal; id?: string },\n ): AsyncIterable<Event> & {\n close: (reason?: string) => void;\n } {\n return streamFilters((f, o) => this.subscribe(f, o), filters, opts);\n }\n\n /**\n * One-shot query: collect events until EOSE or timeout, then close.\n */\n async fetch(\n filters: Filter[],\n opts?: { timeoutMs?: number; signal?: AbortSignal; id?: string },\n ): Promise<Event[]> {\n if (filters.length === 0) throw new MessageError(\"REQ requires at least one filter\");\n filters = canonicalizeFilters(filters);\n if (!this.#connected) {\n await this.connect({ signal: opts?.signal });\n }\n return fetchFilters((f, o) => this.subscribe(f, o), filters, {\n timeoutMs: opts?.timeoutMs ?? 4400,\n signal: opts?.signal,\n id: opts?.id,\n url: this.url,\n });\n }\n\n /** Publish an event and wait for OK. */\n async publish(event: Event, opts?: { timeoutMs?: number }): Promise<PublishResult> {\n if (!this.#connected) throw new RelayClosedError(\"not connected\", this.url);\n const timeoutMs = opts?.timeoutMs ?? this.#publishTimeoutMs;\n\n const result = await new Promise<PublishResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#publishes.delete(event.id);\n reject(new RelayPublishError(\"publish timed out\", this.url));\n }, timeoutMs);\n this.#publishes.set(event.id, { resolve, reject, timer, event, timeoutMs });\n try {\n this.#send([\"EVENT\", event]);\n } catch (err) {\n clearTimeout(timer);\n this.#publishes.delete(event.id);\n reject(err instanceof Error ? err : new RelayPublishError(\"publish failed\", this.url));\n }\n });\n\n return result;\n }\n\n /**\n * NIP-45 COUNT: ask the relay how many events match `filters`.\n * Resolves with the COUNT payload (count / optional approximate / optional hll).\n */\n async count(\n filters: Filter[],\n opts?: { id?: string; timeoutMs?: number; signal?: AbortSignal },\n ): Promise<CountResult> {\n if (!this.#connected) throw new RelayClosedError(\"not connected\", this.url);\n if (filters.length === 0) throw new RelayError(\"COUNT requires at least one filter\", this.url);\n if (opts?.signal?.aborted) {\n throw new RelayConnectionError(\"count aborted\", this.url);\n }\n filters = canonicalizeFilters(filters);\n\n const id = opts?.id !== undefined ? createSubscriptionId(opts.id) : this.nextSubId(\"count\");\n const timeoutMs = opts?.timeoutMs ?? this.#publishTimeoutMs;\n\n return await new Promise<CountResult>((resolve, reject) => {\n const cleanup = () => {\n opts?.signal?.removeEventListener(\"abort\", onAbort);\n };\n const waiter: CountWaiter = {\n resolve: (result) => {\n if (waiter.timer !== undefined) clearTimeout(waiter.timer);\n waiter.timer = undefined;\n this.#counts.delete(id);\n cleanup();\n resolve(result);\n },\n reject: (err) => {\n if (waiter.timer !== undefined) clearTimeout(waiter.timer);\n waiter.timer = undefined;\n this.#counts.delete(id);\n cleanup();\n reject(err);\n },\n timer: undefined,\n filters: [...filters],\n authRetried: false,\n timeoutMs,\n };\n const onAbort = () => waiter.reject(new RelayConnectionError(\"count aborted\", this.url));\n waiter.timer = setTimeout(() => {\n waiter.reject(new RelayPublishError(\"count timed out\", this.url));\n }, timeoutMs);\n this.#counts.set(id, waiter);\n\n opts?.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n try {\n this.#send([\"COUNT\", id, ...filters]);\n } catch (err) {\n waiter.reject(err instanceof Error ? err : new RelayPublishError(\"count failed\", this.url));\n }\n });\n }\n\n /**\n * NIP-42 AUTH: sign the current challenge and wait for OK.\n */\n async auth(\n sign: (template: EventTemplate) => Promise<Event>,\n opts?: { timeoutMs?: number },\n ): Promise<PublishResult> {\n const challenge = this.#challenge;\n if (!challenge) {\n throw new RelayError(\"no AUTH challenge received from relay\", this.url);\n }\n if (this.#authPromise) return this.#authPromise;\n\n const pending = (async () => {\n const template = makeAuthEvent(this.url, challenge);\n const event = await sign(template);\n if (!this.#connected) throw new RelayClosedError(\"not connected\", this.url);\n const timeoutMs = opts?.timeoutMs ?? this.#publishTimeoutMs;\n\n return await new Promise<PublishResult>((resolve, reject) => {\n const timer = setTimeout(() => {\n this.#publishes.delete(event.id);\n reject(new RelayPublishError(\"auth timed out\", this.url));\n }, timeoutMs);\n this.#publishes.set(event.id, { resolve, reject, timer, timeoutMs });\n try {\n this.#send([\"AUTH\", event]);\n } catch (err) {\n clearTimeout(timer);\n this.#publishes.delete(event.id);\n reject(err instanceof Error ? err : new RelayPublishError(\"auth send failed\", this.url));\n }\n });\n })();\n this.#authPromise = pending;\n try {\n const result = await pending;\n if (result.ok && this.#challenge === challenge) this.#authedChallenge = challenge;\n return result;\n } finally {\n if (this.#authPromise === pending) this.#authPromise = undefined;\n }\n }\n\n async #ensureAuthed(): Promise<boolean> {\n const signer = this.#authSigner;\n if (!signer) return false;\n for (let i = 0; i < 3; i++) {\n if (!this.#challenge) return false;\n if (this.#authedChallenge === this.#challenge) return true;\n const signed = this.#challenge;\n const result = await this.auth(signer);\n if (this.#authedChallenge === this.#challenge) return true;\n if (!result.ok) {\n if (!this.#challenge || this.#challenge === signed) return false;\n continue;\n }\n }\n return this.#authedChallenge === this.#challenge;\n }\n\n async #authThenResubscribe(sub: Subscription, reason: string): Promise<void> {\n try {\n if (!(await this.#ensureAuthed())) {\n dropSubscription(this.#live, sub, reason);\n return;\n }\n if (sub.closed || !this.#connected) {\n dropSubscription(this.#live, sub, reason);\n return;\n }\n sub.eosed = false;\n const group = this.#liveBySubId.get(sub.id);\n if (group) {\n for (const att of group.attachments) att.eosed = false;\n }\n this.#send([\"REQ\", sub.id, ...sub.replayFilters()]);\n } catch {\n dropSubscription(this.#live, sub, reason);\n }\n }\n\n async #authThenRecount(id: string, waiter: CountWaiter, reason: string): Promise<void> {\n if (waiter.timer !== undefined) {\n clearTimeout(waiter.timer);\n waiter.timer = undefined;\n }\n const fail = () => {\n waiter.reject(new RelayClosedError(reason || \"COUNT closed\", this.url));\n };\n try {\n const ok = await this.#ensureAuthed();\n if (this.#counts.get(id) !== waiter) return;\n if (!ok) {\n fail();\n return;\n }\n if (!this.#connected) {\n fail();\n return;\n }\n waiter.timer = setTimeout(() => {\n waiter.reject(new RelayPublishError(\"count timed out\", this.url));\n }, waiter.timeoutMs);\n this.#send([\"COUNT\", id, ...waiter.filters]);\n } catch (err) {\n if (!this.#counts.has(id)) return;\n waiter.reject(\n err instanceof Error ? err : new RelayClosedError(reason || \"COUNT closed\", this.url),\n );\n }\n }\n\n async #authThenRepublish(waiter: PublishWaiter, eventId: string, message: string): Promise<void> {\n if (waiter.timer !== undefined) {\n clearTimeout(waiter.timer);\n waiter.timer = undefined;\n }\n const finish = (result: PublishResult) => {\n if (waiter.timer !== undefined) clearTimeout(waiter.timer);\n this.#publishes.delete(eventId);\n waiter.resolve(result);\n };\n try {\n if (!waiter.event) {\n finish({ ok: false, message });\n return;\n }\n const ok = await this.#ensureAuthed();\n if (this.#publishes.get(eventId) !== waiter) return;\n if (!ok) {\n finish({ ok: false, message });\n return;\n }\n if (!this.#connected) {\n finish({ ok: false, message });\n return;\n }\n waiter.timer = setTimeout(() => {\n this.#publishes.delete(eventId);\n waiter.reject(new RelayPublishError(\"publish timed out\", this.url));\n }, waiter.timeoutMs);\n this.#send([\"EVENT\", waiter.event]);\n } catch {\n if (!this.#publishes.has(eventId)) return;\n finish({ ok: false, message });\n }\n }\n\n /**\n * NIP-77: run Negentropy reconciliation against this relay.\n * Returns the local-only (`have`) and remote-only (`need`) event ids.\n * Does not upload or download events.\n *\n * `timeoutMs` is a single wall-clock deadline for the whole session\n * (`NEG-OPEN` through the last `NEG-MSG`), not a per-message budget.\n * Default: {@link RelayOptions.publishTimeoutMs}.\n */\n async negReconcile(\n filter: Filter,\n storage: NegentropyStorageVector,\n opts?: { id?: string; timeoutMs?: number; signal?: AbortSignal },\n ): Promise<{ have: string[]; need: string[] }> {\n if (!this.#connected) throw new RelayClosedError(\"not connected\", this.url);\n if (opts?.signal?.aborted) {\n throw new RelayConnectionError(\"negentropy aborted\", this.url);\n }\n filter = canonicalizeFilter(filter);\n\n const id = opts?.id !== undefined ? assertSubscriptionId(opts.id) : this.nextSubId(\"neg\");\n const timeoutMs = opts?.timeoutMs ?? this.#publishTimeoutMs;\n const prev = this.#neg.get(id);\n if (prev) failNegSession(prev, new Nip77Error(\"closed: replaced by new NEG-OPEN\"));\n const session = createNegSession();\n this.#neg.set(id, session);\n\n try {\n return await runWiredNegSession({\n session,\n storage,\n filter,\n id,\n timeoutMs,\n signal: opts?.signal,\n send: (message) => this.#send(message),\n url: this.url,\n });\n } finally {\n if (this.#neg.get(id) === session) {\n this.#neg.delete(id);\n try {\n this.#send([\"NEG-CLOSE\", id]);\n } catch {\n // connection already gone\n }\n }\n }\n }\n\n /** Generate a unique subscription id for this relay instance. */\n nextSubId(prefix = \"sub\"): string {\n this.#serial += 1;\n return `${prefix}:${this.#serial}`;\n }\n}\n\nexport type { SubscriptionHandlers, SubscribeOptions };\n","import type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { RelayClosedError } from \"./error.ts\";\nimport type { Pool } from \"./pool.ts\";\nimport type { Relay } from \"./relay.ts\";\n\nexport type RoutedJob = {\n urls: readonly string[];\n filters: readonly Filter[];\n id?: string;\n};\n\nexport type FanInOptions = {\n onevent?: (event: Event) => void;\n oneose?: () => void;\n onclose?: (reason: string) => void;\n signal?: AbortSignal;\n /** Armed at fan-in only. Never forwarded to relay.subscribe. */\n eoseTimeoutMs?: number;\n alreadyHaveEvent?: (id: string) => boolean;\n receivedEvent?: (id: string) => void;\n closeOnEose?: boolean;\n /** Optional. ensureRelay uses Pool.#opts.connectTimeoutMs when omitted. */\n connectTimeoutMs?: number;\n};\n\n/**\n * Aggregate EOSE/close across routed jobs. One `seen` set.\n * `pending` counts URL list entries (duplicates included).\n * `pendingEose` counts unique `jobIndex:url` keys.\n */\nexport function fanIn(\n pool: Pool,\n jobs: readonly RoutedJob[],\n opts: FanInOptions = {},\n): { close: (reason?: string) => void } {\n const seen = new Set<string>();\n const closers: Array<{ close: (reason?: string) => void }> = [];\n let closed = false;\n let eoseFired = false;\n let eoseTimer: ReturnType<typeof setTimeout> | undefined;\n const eoseDone = new Set<string>();\n const eoseAttempted = new Set<string>();\n let pendingEose = 0;\n let pending = 0;\n\n const fireEose = () => {\n if (closed || eoseFired) return;\n eoseFired = true;\n if (eoseTimer !== undefined) {\n clearTimeout(eoseTimer);\n eoseTimer = undefined;\n }\n opts.oneose?.();\n };\n\n const markEose = (jobIndex: number, url: string) => {\n const key = `${jobIndex}:${url}`;\n if (eoseDone.has(key)) return;\n eoseDone.add(key);\n pendingEose -= 1;\n if (pendingEose === 0) fireEose();\n };\n\n const settleClose = () => {\n closed = true;\n if (eoseTimer !== undefined) {\n clearTimeout(eoseTimer);\n eoseTimer = undefined;\n }\n };\n\n const closeAll = (reason?: string) => {\n if (closed) return;\n settleClose();\n for (const c of closers) c.close(reason);\n opts.onclose?.(reason ?? \"closed by client\");\n };\n\n if (opts.signal?.aborted) {\n closed = true;\n opts.onclose?.(\"aborted\");\n return { close: closeAll };\n }\n\n opts.signal?.addEventListener(\"abort\", () => closeAll(\"aborted\"), { once: true });\n\n const attach = (relay: Relay, job: RoutedJob, jobIndex: number): void => {\n if (closed) return;\n const sub = relay.subscribe([...job.filters], {\n id: jobs.length === 1 ? job.id : undefined,\n closeOnEose: opts.closeOnEose,\n alreadyHaveEvent: (id) => Boolean(opts.alreadyHaveEvent?.(id) || seen.has(id)),\n receivedEvent: opts.receivedEvent,\n onevent: (event) => {\n seen.add(event.id);\n opts.onevent?.(event);\n },\n oneose: () => markEose(jobIndex, relay.url),\n onclose: (reason) => {\n markEose(jobIndex, relay.url);\n pending -= 1;\n if (pending <= 0 && !closed) {\n settleClose();\n opts.onclose?.(reason);\n }\n },\n });\n closers.push(sub);\n };\n\n for (let jobIndex = 0; jobIndex < jobs.length; jobIndex++) {\n const job = jobs[jobIndex]!;\n for (const url of job.urls) {\n let key: string;\n try {\n key = normalizeURL(url);\n } catch {\n key = url;\n }\n pending += 1;\n const eoseKey = `${jobIndex}:${key}`;\n if (!eoseAttempted.has(eoseKey)) {\n eoseAttempted.add(eoseKey);\n pendingEose += 1;\n }\n\n const failUrl = (): void => {\n markEose(jobIndex, key);\n pending -= 1;\n if (pending <= 0 && closers.length === 0 && !closed) {\n settleClose();\n opts.onclose?.(\"all relays failed\");\n }\n };\n\n const tryAttach = (relay: Relay): void => {\n try {\n attach(relay, job, jobIndex);\n } catch (err) {\n if (err instanceof RelayClosedError) {\n failUrl();\n return;\n }\n throw err;\n }\n };\n\n void pool.ensureRelay(url, { signal: opts.signal, timeoutMs: opts.connectTimeoutMs }).then(\n (relay) => tryAttach(relay),\n () => {\n // ensureRelay rejected only — not tryAttach throws (those must not look like connect failure).\n const relay = pool.getRelay(key);\n if (relay) {\n tryAttach(relay);\n return;\n }\n failUrl();\n },\n );\n }\n }\n\n if (opts.eoseTimeoutMs !== undefined && pending > 0) {\n eoseTimer = setTimeout(() => {\n eoseTimer = undefined;\n fireEose();\n }, opts.eoseTimeoutMs);\n }\n\n if (pending === 0) {\n queueMicrotask(() => {\n if (closed) return;\n settleClose();\n opts.onclose?.(\"no relays\");\n });\n }\n\n return { close: closeAll };\n}\n\nexport async function fetchRouted(\n pool: Pool,\n jobs: readonly RoutedJob[],\n opts: { timeoutMs?: number; signal?: AbortSignal; connectTimeoutMs?: number } = {},\n): Promise<Event[]> {\n const byId = new Map<string, Event>();\n await Promise.all(\n jobs.flatMap((job) =>\n job.urls.map(async (url) => {\n try {\n const relay = await pool.ensureRelay(url, {\n signal: opts.signal,\n timeoutMs: opts.connectTimeoutMs,\n });\n const batch = await relay.fetch([...job.filters], {\n timeoutMs: opts.timeoutMs,\n signal: opts.signal,\n });\n for (const event of batch) if (!byId.has(event.id)) byId.set(event.id, event);\n } catch {\n // skip failed relays\n }\n }),\n ),\n );\n return [...byId.values()];\n}\n","import { MessageError } from \"../core/error.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { canonicalizeFilters, type Filter } from \"../core/filter.ts\";\nimport { assertSubscriptionId, type CountResult } from \"../core/message.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { RelayConnectionError, RelayPublishError } from \"./error.ts\";\nimport { fanIn, fetchRouted } from \"./fan-in.ts\";\nimport { Relay, type PublishResult, type RelayOptions, type SubscribeOptions } from \"./relay.ts\";\nimport { isInsecureRelayUrl } from \"./url.ts\";\nimport type { WebSocketConstructor } from \"./websocket.ts\";\n\nexport type PoolOptions = {\n websocketImplementation?: WebSocketConstructor;\n verifyEvent?: RelayOptions[\"verifyEvent\"];\n publishTimeoutMs?: number;\n connectTimeoutMs?: number;\n enableReconnect?: boolean;\n reconnectBackoffMs?: number[];\n enablePing?: boolean;\n pingIntervalMs?: number;\n pingTimeoutMs?: number;\n /**\n * When set, automatically answer NIP-42 AUTH challenges for relays that\n * send them. Return null to skip a given relay URL.\n */\n automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<Event>);\n /** When false, ensureRelay rejects isInsecureRelayUrl unless trusted. Default true (allow). */\n allowInsecure?: boolean;\n trustedInsecureUrls?: readonly string[];\n /** Close unused relays. Unset/0 = disabled. */\n idleTimeoutMs?: number;\n idleCleanupIntervalMs?: number;\n onIdleRelaysClosed?: (urls: string[]) => void;\n};\n\nexport type PoolPublishResult = {\n url: string;\n result?: PublishResult;\n error?: string;\n};\n\nexport type PoolCountResult = {\n url: string;\n count?: number;\n approximate?: boolean;\n hll?: string;\n error?: string;\n};\n\n/**\n * Multi-relay coordinator: connection reuse, cross-relay event dedup, fan-out publish.\n */\nexport class Pool {\n readonly #relays = new Map<string, Relay>();\n readonly #opts: PoolOptions;\n readonly #lastActivity = new Map<string, number>();\n readonly #idleTimeoutMs: number;\n #idleTimer: ReturnType<typeof setInterval> | undefined;\n #allowInsecure: boolean;\n #trustedInsecure: Set<string>;\n\n constructor(opts: PoolOptions = {}) {\n this.#opts = opts;\n this.#allowInsecure = opts.allowInsecure ?? true;\n this.#trustedInsecure = new Set((opts.trustedInsecureUrls ?? []).map(normalizeURL));\n this.#idleTimeoutMs = opts.idleTimeoutMs ?? 0;\n if (this.#idleTimeoutMs > 0) {\n this.#idleTimer = setInterval(\n () => this.cleanIdleRelays(),\n opts.idleCleanupIntervalMs ?? 30_000,\n );\n }\n }\n\n setAllowInsecure(allow: boolean): void {\n this.#allowInsecure = allow;\n }\n\n setTrustedInsecureUrls(urls: readonly string[]): void {\n this.#trustedInsecure = new Set(urls.map(normalizeURL));\n }\n\n cleanIdleRelays(): void {\n if (this.#idleTimeoutMs <= 0) return;\n const now = Date.now();\n const idle: string[] = [];\n for (const [url, relay] of this.#relays) {\n if (relay.subscriptionCount > 0) continue;\n const last = this.#lastActivity.get(url) ?? 0;\n if (!relay.connected || now - last >= this.#idleTimeoutMs) {\n idle.push(url);\n }\n }\n for (const url of this.#lastActivity.keys()) {\n if (!this.#relays.has(url)) this.#lastActivity.delete(url);\n }\n if (idle.length === 0) return;\n this.close(idle);\n this.#opts.onIdleRelaysClosed?.(idle);\n }\n\n #touch(url: string): void {\n this.#lastActivity.set(url, Date.now());\n }\n\n #rejectInsecure(url: string, norm: string): void {\n if (this.#allowInsecure) return;\n if (!isInsecureRelayUrl(url)) return;\n if (this.#trustedInsecure.has(norm)) return;\n throw new RelayConnectionError(\"insecure relay connection blocked\", norm);\n }\n\n #stopIdleCleanup(): void {\n if (this.#idleTimer === undefined) return;\n clearInterval(this.#idleTimer);\n this.#idleTimer = undefined;\n }\n\n async ensureRelay(\n url: string,\n opts?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<Relay> {\n const norm = normalizeURL(url);\n this.#rejectInsecure(url, norm);\n let relay = this.#relays.get(norm);\n if (!relay) {\n const signFn = this.#opts.automaticallyAuth?.(norm) ?? undefined;\n const created = new Relay(norm, {\n websocketImplementation: this.#opts.websocketImplementation,\n verifyEvent: this.#opts.verifyEvent,\n publishTimeoutMs: this.#opts.publishTimeoutMs,\n connectTimeoutMs: this.#opts.connectTimeoutMs,\n enableReconnect: this.#opts.enableReconnect,\n reconnectBackoffMs: this.#opts.reconnectBackoffMs,\n enablePing: this.#opts.enablePing,\n pingIntervalMs: this.#opts.pingIntervalMs,\n pingTimeoutMs: this.#opts.pingTimeoutMs,\n authSigner: signFn,\n });\n // Only drop from the pool on terminal close (reconnect keeps the entry).\n created.onclose = () => {\n this.#relays.delete(norm);\n this.#lastActivity.delete(norm);\n };\n if (signFn) {\n created.onauth = () => {\n void created.auth(signFn).catch(() => {\n // auth failure surfaces on subsequent CLOSED/OK; avoid unhandled rejection\n });\n };\n }\n this.#relays.set(norm, created);\n relay = created;\n }\n this.#touch(norm);\n if (!relay.connected) {\n try {\n await relay.connect({\n signal: opts?.signal,\n timeoutMs: opts?.timeoutMs ?? this.#opts.connectTimeoutMs,\n });\n } catch (err) {\n // Keep the entry when reconnect is enabled so open subscriptions can recover.\n if (!this.#opts.enableReconnect) {\n this.#relays.delete(norm);\n this.#lastActivity.delete(norm);\n }\n throw err;\n }\n }\n return relay;\n }\n\n close(urls?: string[]): void {\n if (!urls) {\n this.#stopIdleCleanup();\n for (const relay of this.#relays.values()) relay.close();\n this.#relays.clear();\n this.#lastActivity.clear();\n return;\n }\n for (const url of urls) {\n const norm = normalizeURL(url);\n this.#relays.get(norm)?.close();\n this.#relays.delete(norm);\n this.#lastActivity.delete(norm);\n }\n }\n\n /**\n * Subscribe across relays. Deduplicates by event id.\n * Returns a closer; callbacks receive every new event once.\n */\n subscribe(\n relays: string[],\n filters: Filter[],\n opts: SubscribeOptions = {},\n ): { close: (reason?: string) => void } {\n if (filters.length === 0) throw new MessageError(\"REQ requires at least one filter\");\n if (opts.id !== undefined) assertSubscriptionId(opts.id);\n filters = canonicalizeFilters(filters);\n return fanIn(this, [{ urls: relays, filters, id: opts.id }], {\n onevent: opts.onevent,\n oneose: opts.oneose,\n onclose: opts.onclose,\n signal: opts.signal,\n eoseTimeoutMs: opts.eoseTimeoutMs,\n alreadyHaveEvent: opts.alreadyHaveEvent,\n receivedEvent: opts.receivedEvent,\n closeOnEose: opts.closeOnEose,\n connectTimeoutMs: this.#opts.connectTimeoutMs,\n });\n }\n\n /** Fetch events until each connected relay EOSE or timeout; dedupe by id. */\n async fetch(\n relays: string[],\n filters: Filter[],\n opts?: { timeoutMs?: number; signal?: AbortSignal },\n ): Promise<Event[]> {\n if (filters.length === 0) throw new MessageError(\"REQ requires at least one filter\");\n return fetchRouted(this, [{ urls: relays, filters: canonicalizeFilters(filters) }], {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n connectTimeoutMs: this.#opts.connectTimeoutMs,\n });\n }\n\n /** Publish to all listed relays; returns per-relay outcomes. */\n async publish(\n relays: string[],\n event: Event,\n opts?: { timeoutMs?: number },\n ): Promise<PoolPublishResult[]> {\n const results = await Promise.all(\n relays.map(async (url): Promise<PoolPublishResult> => {\n try {\n const relay = await this.ensureRelay(url, {\n timeoutMs: this.#opts.connectTimeoutMs,\n });\n this.#touch(relay.url);\n const result = await relay.publish(event, { timeoutMs: opts?.timeoutMs });\n return { url: relay.url, result };\n } catch (err) {\n return { url, error: err instanceof Error ? err.message : String(err) };\n }\n }),\n );\n return results;\n }\n\n /** First successful publish (Promise.any semantics). */\n async publishAny(\n relays: string[],\n event: Event,\n opts?: { timeoutMs?: number },\n ): Promise<PoolPublishResult> {\n return Promise.any(\n relays.map(async (url) => {\n const relay = await this.ensureRelay(url, {\n timeoutMs: this.#opts.connectTimeoutMs,\n });\n this.#touch(relay.url);\n const result = await relay.publish(event, { timeoutMs: opts?.timeoutMs });\n if (!result.ok) throw new RelayPublishError(result.message || \"rejected\", relay.url);\n return { url: relay.url, result };\n }),\n );\n }\n\n /**\n * NIP-45 COUNT across relays. Per-relay outcomes; failures do not throw.\n * Counts are not summed — each relay reports independently (may overlap).\n */\n async count(\n relays: string[],\n filters: Filter[],\n opts?: { timeoutMs?: number; signal?: AbortSignal },\n ): Promise<PoolCountResult[]> {\n filters = canonicalizeFilters(filters);\n const results = await Promise.all(\n relays.map(async (url): Promise<PoolCountResult> => {\n try {\n const relay = await this.ensureRelay(url, {\n signal: opts?.signal,\n timeoutMs: this.#opts.connectTimeoutMs,\n });\n this.#touch(relay.url);\n const payload: CountResult = await relay.count(filters, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n });\n return {\n url: relay.url,\n count: payload.count,\n approximate: payload.approximate,\n hll: payload.hll,\n };\n } catch (err) {\n return { url, error: err instanceof Error ? err.message : String(err) };\n }\n }),\n );\n return results;\n }\n\n listRelays(): string[] {\n return [...this.#relays.keys()];\n }\n\n /** Lookup a pooled relay, including disconnected reconnecting entries. */\n getRelay(url: string): Relay | undefined {\n try {\n return this.#relays.get(normalizeURL(url));\n } catch {\n return this.#relays.get(url);\n }\n }\n\n /** Currently connected URLs. Unlike listRelays(), excludes reconnecting/disconnected entries. */\n connectedUrls(): string[] {\n const urls: string[] = [];\n for (const [url, relay] of this.#relays) {\n if (relay.connected) urls.push(url);\n }\n return urls;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { n as EventTemplate, t as Event } from "./event-B5D2jxt_.mjs";
|
|
2
|
+
import { t as Filter } from "./filter-DUEKP3q1.mjs";
|
|
3
|
+
import { n as CountResult } from "./message-DJC-Jz6T.mjs";
|
|
4
|
+
import { a as NegentropyStorageVector } from "./nip77-ByxlgoQO.mjs";
|
|
5
|
+
//#region src/relay/subscription.d.ts
|
|
6
|
+
type SubscriptionHandlers = {
|
|
7
|
+
onevent?: (event: Event) => void;
|
|
8
|
+
oneose?: () => void;
|
|
9
|
+
onclose?: (reason: string) => void;
|
|
10
|
+
/** Skip verify + onevent when true. Evaluated after parse, before verify. */
|
|
11
|
+
alreadyHaveEvent?: (id: string) => boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Fired for every EVENT id this sub sees, including duplicates and
|
|
14
|
+
* alreadyHaveEvent hits, after parse and before the verify skip.
|
|
15
|
+
*/
|
|
16
|
+
receivedEvent?: (id: string) => void;
|
|
17
|
+
};
|
|
18
|
+
type SubscribeOptions = SubscriptionHandlers & {
|
|
19
|
+
id?: string;
|
|
20
|
+
/**
|
|
21
|
+
* If set, fire `oneose` once after this many ms if EOSE has not arrived.
|
|
22
|
+
* Does not close the REQ. `Relay.fetch` is the one-shot closer.
|
|
23
|
+
*/
|
|
24
|
+
eoseTimeoutMs?: number;
|
|
25
|
+
/**
|
|
26
|
+
* One-shot REQ: do not join a live coalescing group, and close on EOSE.
|
|
27
|
+
* Default false (live). `Relay.fetch` passes true.
|
|
28
|
+
*/
|
|
29
|
+
closeOnEose?: boolean;
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
};
|
|
32
|
+
declare class Subscription {
|
|
33
|
+
#private;
|
|
34
|
+
private readonly sendClose;
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly filters: Filter[];
|
|
37
|
+
readonly handlers: SubscriptionHandlers;
|
|
38
|
+
readonly closeOnEose: boolean;
|
|
39
|
+
eosed: boolean;
|
|
40
|
+
closed: boolean;
|
|
41
|
+
/** True after one CLOSED `auth-required:` retry. */
|
|
42
|
+
authRetried: boolean;
|
|
43
|
+
/** Inclusive NIP-01 `since` watermark from verified EVENTs. */
|
|
44
|
+
lastCreatedAt: number | undefined;
|
|
45
|
+
/** Event ids at `lastCreatedAt` (same-second reconnect dedup). Not all seen ids. */
|
|
46
|
+
readonly idsAtWatermark: Set<string>;
|
|
47
|
+
constructor(filters: Filter[], opts: SubscribeOptions, sendClose: (id: string) => void);
|
|
48
|
+
close(reason?: string): void;
|
|
49
|
+
/** Advance the reconnect watermark after a verified EVENT. */
|
|
50
|
+
noteVerified(event: Event): void;
|
|
51
|
+
/**
|
|
52
|
+
* Filters for re-REQ. Original `filters` stay unchanged.
|
|
53
|
+
* NIP-01 `since` is inclusive — never `lastCreatedAt + 1`.
|
|
54
|
+
*/
|
|
55
|
+
replayFilters(): Filter[];
|
|
56
|
+
}
|
|
57
|
+
/** AsyncIterable wrapper over a subscription's events until EOSE or close. */
|
|
58
|
+
declare function subscriptionToAsyncIterable(start: (handlers: SubscriptionHandlers) => {
|
|
59
|
+
close: (reason?: string) => void;
|
|
60
|
+
}, opts?: {
|
|
61
|
+
signal?: AbortSignal;
|
|
62
|
+
includeEose?: boolean;
|
|
63
|
+
}): AsyncIterable<Event> & {
|
|
64
|
+
close: (reason?: string) => void;
|
|
65
|
+
};
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/relay/websocket.d.ts
|
|
68
|
+
/** Minimal WebSocket surface used by Relay (browser or `ws`). */
|
|
69
|
+
type WebSocketLike = {
|
|
70
|
+
readyState: number;
|
|
71
|
+
send(data: string): void;
|
|
72
|
+
close(code?: number, reason?: string): void;
|
|
73
|
+
addEventListener(type: string, listener: (ev: unknown) => void): void;
|
|
74
|
+
removeEventListener(type: string, listener: (ev: unknown) => void): void;
|
|
75
|
+
ping?(): void;
|
|
76
|
+
/** Node `ws` emits `pong` on the EventEmitter; `addEventListener("pong")` is a no-op. */
|
|
77
|
+
once?(event: string, listener: (...args: unknown[]) => void): void;
|
|
78
|
+
on?(event: string, listener: (...args: unknown[]) => void): void;
|
|
79
|
+
off?(event: string, listener: (...args: unknown[]) => void): void;
|
|
80
|
+
};
|
|
81
|
+
type WebSocketConstructor = {
|
|
82
|
+
new (url: string): WebSocketLike;
|
|
83
|
+
readonly OPEN: number;
|
|
84
|
+
readonly CONNECTING: number;
|
|
85
|
+
readonly CLOSING: number;
|
|
86
|
+
readonly CLOSED: number;
|
|
87
|
+
};
|
|
88
|
+
/** Inject a WebSocket implementation (required on Node unless undici/global is present). */
|
|
89
|
+
declare function useWebSocketImplementation(websocketImplementation: WebSocketConstructor): void;
|
|
90
|
+
declare function getWebSocketImplementation(): WebSocketConstructor;
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/relay/relay.d.ts
|
|
93
|
+
declare const RelayStatus: {
|
|
94
|
+
readonly Initialized: "initialized";
|
|
95
|
+
readonly Connecting: "connecting";
|
|
96
|
+
readonly Connected: "connected";
|
|
97
|
+
readonly Disconnected: "disconnected";
|
|
98
|
+
readonly Closed: "closed";
|
|
99
|
+
};
|
|
100
|
+
type RelayStatusName = (typeof RelayStatus)[keyof typeof RelayStatus];
|
|
101
|
+
type RelayOptions = {
|
|
102
|
+
websocketImplementation?: WebSocketConstructor;
|
|
103
|
+
verifyEvent?: (event: Event) => boolean;
|
|
104
|
+
publishTimeoutMs?: number;
|
|
105
|
+
connectTimeoutMs?: number;
|
|
106
|
+
/**
|
|
107
|
+
* When true, unexpected disconnects schedule reconnect with backoff and
|
|
108
|
+
* re-fire open subscriptions. Default false.
|
|
109
|
+
*/
|
|
110
|
+
enableReconnect?: boolean;
|
|
111
|
+
/** Backoff delays in ms between reconnect attempts. */
|
|
112
|
+
reconnectBackoffMs?: number[];
|
|
113
|
+
enablePing?: boolean;
|
|
114
|
+
pingIntervalMs?: number;
|
|
115
|
+
pingTimeoutMs?: number;
|
|
116
|
+
/** When set, CLOSED/OK `auth-required:` triggers AUTH then retries the REQ/EVENT/COUNT. */
|
|
117
|
+
authSigner?: (template: EventTemplate) => Promise<Event>;
|
|
118
|
+
};
|
|
119
|
+
type PublishResult = {
|
|
120
|
+
ok: boolean;
|
|
121
|
+
message: string;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Single-relay NIP-01 client.
|
|
125
|
+
* Connection lifecycle, REQ/CLOSE, EVENT publish ACK, AUTH, COUNT, NIP-77, optional reconnect.
|
|
126
|
+
*/
|
|
127
|
+
declare class Relay {
|
|
128
|
+
#private;
|
|
129
|
+
readonly url: string;
|
|
130
|
+
onnotice: ((msg: string) => void) | null;
|
|
131
|
+
onclose: (() => void) | null;
|
|
132
|
+
/** Fired when the relay sends a NIP-42 AUTH challenge. */
|
|
133
|
+
onauth: ((challenge: string) => void) | null;
|
|
134
|
+
/** Fired after a successful reconnect (not the initial connect). */
|
|
135
|
+
onreconnect: (() => void) | null;
|
|
136
|
+
constructor(url: string, opts?: RelayOptions);
|
|
137
|
+
/** Latest NIP-42 challenge, if any. */
|
|
138
|
+
get challenge(): string | undefined;
|
|
139
|
+
get connected(): boolean;
|
|
140
|
+
get status(): RelayStatusName;
|
|
141
|
+
get generation(): number;
|
|
142
|
+
get reconnectEnabled(): boolean;
|
|
143
|
+
/** User subscriptions only; dummy ping REQs are not counted. */
|
|
144
|
+
get subscriptionCount(): number;
|
|
145
|
+
static connect(url: string, opts?: RelayOptions & {
|
|
146
|
+
signal?: AbortSignal;
|
|
147
|
+
}): Promise<Relay>;
|
|
148
|
+
connect(opts?: {
|
|
149
|
+
signal?: AbortSignal;
|
|
150
|
+
timeoutMs?: number;
|
|
151
|
+
}): Promise<void>;
|
|
152
|
+
/** Graceful shutdown: disables reconnect and closes all subscriptions. */
|
|
153
|
+
close(): void;
|
|
154
|
+
/** Low-level REQ with callbacks. Survives reconnect when enableReconnect is on. */
|
|
155
|
+
subscribe(filters: Filter[], opts?: SubscribeOptions): Subscription;
|
|
156
|
+
/** AsyncIterable of events for filters until the subscription is closed. */
|
|
157
|
+
stream(filters: Filter[], opts?: {
|
|
158
|
+
signal?: AbortSignal;
|
|
159
|
+
id?: string;
|
|
160
|
+
}): AsyncIterable<Event> & {
|
|
161
|
+
close: (reason?: string) => void;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* One-shot query: collect events until EOSE or timeout, then close.
|
|
165
|
+
*/
|
|
166
|
+
fetch(filters: Filter[], opts?: {
|
|
167
|
+
timeoutMs?: number;
|
|
168
|
+
signal?: AbortSignal;
|
|
169
|
+
id?: string;
|
|
170
|
+
}): Promise<Event[]>;
|
|
171
|
+
/** Publish an event and wait for OK. */
|
|
172
|
+
publish(event: Event, opts?: {
|
|
173
|
+
timeoutMs?: number;
|
|
174
|
+
}): Promise<PublishResult>;
|
|
175
|
+
/**
|
|
176
|
+
* NIP-45 COUNT: ask the relay how many events match `filters`.
|
|
177
|
+
* Resolves with the COUNT payload (count / optional approximate / optional hll).
|
|
178
|
+
*/
|
|
179
|
+
count(filters: Filter[], opts?: {
|
|
180
|
+
id?: string;
|
|
181
|
+
timeoutMs?: number;
|
|
182
|
+
signal?: AbortSignal;
|
|
183
|
+
}): Promise<CountResult>;
|
|
184
|
+
/**
|
|
185
|
+
* NIP-42 AUTH: sign the current challenge and wait for OK.
|
|
186
|
+
*/
|
|
187
|
+
auth(sign: (template: EventTemplate) => Promise<Event>, opts?: {
|
|
188
|
+
timeoutMs?: number;
|
|
189
|
+
}): Promise<PublishResult>;
|
|
190
|
+
/**
|
|
191
|
+
* NIP-77: run Negentropy reconciliation against this relay.
|
|
192
|
+
* Returns the local-only (`have`) and remote-only (`need`) event ids.
|
|
193
|
+
* Does not upload or download events.
|
|
194
|
+
*
|
|
195
|
+
* `timeoutMs` is a single wall-clock deadline for the whole session
|
|
196
|
+
* (`NEG-OPEN` through the last `NEG-MSG`), not a per-message budget.
|
|
197
|
+
* Default: {@link RelayOptions.publishTimeoutMs}.
|
|
198
|
+
*/
|
|
199
|
+
negReconcile(filter: Filter, storage: NegentropyStorageVector, opts?: {
|
|
200
|
+
id?: string;
|
|
201
|
+
timeoutMs?: number;
|
|
202
|
+
signal?: AbortSignal;
|
|
203
|
+
}): Promise<{
|
|
204
|
+
have: string[];
|
|
205
|
+
need: string[];
|
|
206
|
+
}>;
|
|
207
|
+
/** Generate a unique subscription id for this relay instance. */
|
|
208
|
+
nextSubId(prefix?: string): string;
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/relay/pool.d.ts
|
|
212
|
+
type PoolOptions = {
|
|
213
|
+
websocketImplementation?: WebSocketConstructor;
|
|
214
|
+
verifyEvent?: RelayOptions["verifyEvent"];
|
|
215
|
+
publishTimeoutMs?: number;
|
|
216
|
+
connectTimeoutMs?: number;
|
|
217
|
+
enableReconnect?: boolean;
|
|
218
|
+
reconnectBackoffMs?: number[];
|
|
219
|
+
enablePing?: boolean;
|
|
220
|
+
pingIntervalMs?: number;
|
|
221
|
+
pingTimeoutMs?: number;
|
|
222
|
+
/**
|
|
223
|
+
* When set, automatically answer NIP-42 AUTH challenges for relays that
|
|
224
|
+
* send them. Return null to skip a given relay URL.
|
|
225
|
+
*/
|
|
226
|
+
automaticallyAuth?: (relayURL: string) => null | ((event: EventTemplate) => Promise<Event>);
|
|
227
|
+
/** When false, ensureRelay rejects isInsecureRelayUrl unless trusted. Default true (allow). */
|
|
228
|
+
allowInsecure?: boolean;
|
|
229
|
+
trustedInsecureUrls?: readonly string[];
|
|
230
|
+
/** Close unused relays. Unset/0 = disabled. */
|
|
231
|
+
idleTimeoutMs?: number;
|
|
232
|
+
idleCleanupIntervalMs?: number;
|
|
233
|
+
onIdleRelaysClosed?: (urls: string[]) => void;
|
|
234
|
+
};
|
|
235
|
+
type PoolPublishResult = {
|
|
236
|
+
url: string;
|
|
237
|
+
result?: PublishResult;
|
|
238
|
+
error?: string;
|
|
239
|
+
};
|
|
240
|
+
type PoolCountResult = {
|
|
241
|
+
url: string;
|
|
242
|
+
count?: number;
|
|
243
|
+
approximate?: boolean;
|
|
244
|
+
hll?: string;
|
|
245
|
+
error?: string;
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Multi-relay coordinator: connection reuse, cross-relay event dedup, fan-out publish.
|
|
249
|
+
*/
|
|
250
|
+
declare class Pool {
|
|
251
|
+
#private;
|
|
252
|
+
constructor(opts?: PoolOptions);
|
|
253
|
+
setAllowInsecure(allow: boolean): void;
|
|
254
|
+
setTrustedInsecureUrls(urls: readonly string[]): void;
|
|
255
|
+
cleanIdleRelays(): void;
|
|
256
|
+
ensureRelay(url: string, opts?: {
|
|
257
|
+
signal?: AbortSignal;
|
|
258
|
+
timeoutMs?: number;
|
|
259
|
+
}): Promise<Relay>;
|
|
260
|
+
close(urls?: string[]): void;
|
|
261
|
+
/**
|
|
262
|
+
* Subscribe across relays. Deduplicates by event id.
|
|
263
|
+
* Returns a closer; callbacks receive every new event once.
|
|
264
|
+
*/
|
|
265
|
+
subscribe(relays: string[], filters: Filter[], opts?: SubscribeOptions): {
|
|
266
|
+
close: (reason?: string) => void;
|
|
267
|
+
};
|
|
268
|
+
/** Fetch events until each connected relay EOSE or timeout; dedupe by id. */
|
|
269
|
+
fetch(relays: string[], filters: Filter[], opts?: {
|
|
270
|
+
timeoutMs?: number;
|
|
271
|
+
signal?: AbortSignal;
|
|
272
|
+
}): Promise<Event[]>;
|
|
273
|
+
/** Publish to all listed relays; returns per-relay outcomes. */
|
|
274
|
+
publish(relays: string[], event: Event, opts?: {
|
|
275
|
+
timeoutMs?: number;
|
|
276
|
+
}): Promise<PoolPublishResult[]>;
|
|
277
|
+
/** First successful publish (Promise.any semantics). */
|
|
278
|
+
publishAny(relays: string[], event: Event, opts?: {
|
|
279
|
+
timeoutMs?: number;
|
|
280
|
+
}): Promise<PoolPublishResult>;
|
|
281
|
+
/**
|
|
282
|
+
* NIP-45 COUNT across relays. Per-relay outcomes; failures do not throw.
|
|
283
|
+
* Counts are not summed — each relay reports independently (may overlap).
|
|
284
|
+
*/
|
|
285
|
+
count(relays: string[], filters: Filter[], opts?: {
|
|
286
|
+
timeoutMs?: number;
|
|
287
|
+
signal?: AbortSignal;
|
|
288
|
+
}): Promise<PoolCountResult[]>;
|
|
289
|
+
listRelays(): string[];
|
|
290
|
+
/** Lookup a pooled relay, including disconnected reconnecting entries. */
|
|
291
|
+
getRelay(url: string): Relay | undefined;
|
|
292
|
+
/** Currently connected URLs. Unlike listRelays(), excludes reconnecting/disconnected entries. */
|
|
293
|
+
connectedUrls(): string[];
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
296
|
+
export { subscriptionToAsyncIterable as _, PublishResult as a, RelayStatus as c, WebSocketLike as d, getWebSocketImplementation as f, SubscriptionHandlers as g, Subscription as h, PoolPublishResult as i, RelayStatusName as l, SubscribeOptions as m, PoolCountResult as n, Relay as o, useWebSocketImplementation as p, PoolOptions as r, RelayOptions as s, Pool as t, WebSocketConstructor as u };
|
|
297
|
+
//# sourceMappingURL=pool-DhRi7l-b.d.mts.map
|
package/dist/relay.d.mts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { n as CountResult } from "./message-DJC-Jz6T.mjs";
|
|
2
|
+
import { _ as subscriptionToAsyncIterable, a as PublishResult, c as RelayStatus, d as WebSocketLike, f as getWebSocketImplementation, g as SubscriptionHandlers, h as Subscription, i as PoolPublishResult, l as RelayStatusName, m as SubscribeOptions, n as PoolCountResult, o as Relay, p as useWebSocketImplementation, r as PoolOptions, s as RelayOptions, t as Pool, u as WebSocketConstructor } from "./pool-DhRi7l-b.mjs";
|
|
3
|
+
import { a as isInsecureRelayUrl, c as RelayError, i as fetchRouted, l as RelayPublishError, n as RoutedJob, o as RelayClosedError, r as fanIn, s as RelayConnectionError, t as FanInOptions } from "./index-C0C4e-Gn.mjs";
|
|
4
|
+
export { type CountResult, type FanInOptions, Pool, type PoolCountResult, type PoolOptions, type PoolPublishResult, type PublishResult, Relay, RelayClosedError, RelayConnectionError, RelayError, type RelayOptions, RelayPublishError, RelayStatus, type RelayStatusName, type RoutedJob, type SubscribeOptions, Subscription, type SubscriptionHandlers, type WebSocketConstructor, type WebSocketLike, fanIn, fetchRouted, getWebSocketImplementation, isInsecureRelayUrl, subscriptionToAsyncIterable, useWebSocketImplementation };
|
package/dist/relay.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as RelayStatus, c as isInsecureRelayUrl, d as RelayClosedError, f as RelayConnectionError, i as Relay, l as getWebSocketImplementation, m as RelayPublishError, n as fanIn, o as Subscription, p as RelayError, r as fetchRouted, s as subscriptionToAsyncIterable, t as Pool, u as useWebSocketImplementation } from "./pool-C_j1OmBB.mjs";
|
|
2
|
+
export { Pool, Relay, RelayClosedError, RelayConnectionError, RelayError, RelayPublishError, RelayStatus, Subscription, fanIn, fetchRouted, getWebSocketImplementation, isInsecureRelayUrl, subscriptionToAsyncIterable, useWebSocketImplementation };
|