@qntx/nostr 0.0.1 → 0.2.0
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/dist/abort-Ba8tDrOI.mjs +38 -0
- package/dist/abort-Ba8tDrOI.mjs.map +1 -0
- package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
- package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
- package/dist/builder-CyrGKRVP.mjs.map +1 -0
- package/dist/{client-Bd8B1mYm.mjs → client-BWsX9Cv1.mjs} +354 -195
- package/dist/client-BWsX9Cv1.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/core.d.mts +8 -8
- package/dist/core.mjs +8 -9
- package/dist/error-BW7re1UN.d.mts +9 -0
- package/dist/error-B__OMBRY.mjs +14 -0
- package/dist/error-B__OMBRY.mjs.map +1 -0
- package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
- package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
- package/dist/error-DrEIM7FO.mjs.map +1 -0
- package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
- package/dist/event-D8Nfoh4K.mjs.map +1 -0
- package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
- package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
- package/dist/filter-BQl3GlHt.mjs.map +1 -0
- package/dist/filter-C0IMByE3.d.mts +37 -0
- package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
- package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
- package/dist/gossip-oloDMWEj.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
- package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
- package/dist/http-CuazNRIP.mjs.map +1 -0
- package/dist/index-B425CAcQ.d.mts +328 -0
- package/dist/index-BVyfUBuG.d.mts +181 -0
- package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
- package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
- package/dist/index-CUIvFQdE.d.mts +73 -0
- package/dist/index-JdaS_uce.d.mts +207 -0
- package/dist/index.d.mts +22 -20
- package/dist/index.mjs +19 -18
- package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
- package/dist/key-Bbtys6o7.mjs.map +1 -0
- package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
- package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
- package/dist/kind-Cxwhhkx8.mjs.map +1 -0
- package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
- package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -3
- package/dist/loaders.mjs +2 -2
- package/dist/memory-C2EVx0_O.mjs +46 -0
- package/dist/memory-C2EVx0_O.mjs.map +1 -0
- package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
- package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
- package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
- package/dist/message-DcY6Fi9o.mjs.map +1 -0
- package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
- package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
- package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
- package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
- package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
- package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
- package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
- package/dist/nips/blossom.d.mts +37 -37
- package/dist/nips/blossom.mjs +67 -52
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +2 -3
- package/dist/nips/nip04.mjs +6 -10
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +37 -43
- package/dist/nips/nip05.mjs +57 -57
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +18 -19
- package/dist/nips/nip10.mjs +76 -61
- package/dist/nips/nip10.mjs.map +1 -1
- package/dist/nips/nip11.d.mts +9 -8
- package/dist/nips/nip11.mjs +14 -11
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +9 -9
- package/dist/nips/nip13.mjs +15 -17
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +17 -12
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +52 -29
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +5 -6
- package/dist/nips/nip21.mjs +8 -7
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +7 -8
- package/dist/nips/nip27.mjs +26 -25
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.d.mts +5 -6
- package/dist/nips/nip42.mjs +3 -3
- package/dist/nips/nip42.mjs.map +1 -1
- package/dist/nips/nip44.d.mts +24 -7
- package/dist/nips/nip44.mjs +25 -15
- package/dist/nips/nip44.mjs.map +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +52 -49
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +8 -7
- package/dist/nips/nip49.mjs +16 -12
- package/dist/nips/nip49.mjs.map +1 -1
- package/dist/nips/nip51.d.mts +25 -27
- package/dist/nips/nip51.mjs +47 -36
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.d.mts +12 -13
- package/dist/nips/nip57.mjs +35 -38
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +14 -18
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +11 -8
- package/dist/nips/nip65.mjs.map +1 -1
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +56 -34
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip96.d.mts +21 -12
- package/dist/nips/nip96.mjs +59 -36
- package/dist/nips/nip96.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +12 -12
- package/dist/nips/nip98.mjs +18 -19
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/{nostr_crypto_wasm_bg-hi5aXi5P.mjs → nostr_crypto_wasm_bg-Czrdty7Q.mjs} +1 -1
- package/dist/nostr_crypto_wasm_bg.wasm +0 -0
- package/dist/{pool-C_j1OmBB.mjs → pool-XmAGaufS.mjs} +611 -487
- package/dist/pool-XmAGaufS.mjs.map +1 -0
- package/dist/pool-tEr7c5bG.d.mts +302 -0
- package/dist/reactive-DDlThDSZ.mjs +324 -0
- package/dist/reactive-DDlThDSZ.mjs.map +1 -0
- package/dist/reactive-DFpqtIf_.d.mts +66 -0
- package/dist/relay.d.mts +5 -4
- package/dist/relay.mjs +2 -2
- package/dist/report-CHld4uWj.mjs +32 -0
- package/dist/report-CHld4uWj.mjs.map +1 -0
- package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
- package/dist/signer-CKR9EHce.mjs.map +1 -0
- package/dist/signer.d.mts +3 -3
- package/dist/signer.mjs +3 -2
- package/dist/storage-xXxLGCVb.mjs +1376 -0
- package/dist/storage-xXxLGCVb.mjs.map +1 -0
- package/dist/storage.d.mts +4 -4
- package/dist/storage.mjs +5 -4
- package/dist/store.d.mts +2 -0
- package/dist/store.mjs +2 -0
- package/dist/testing.d.mts +125 -0
- package/dist/testing.mjs +957 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-2pCUprRU.d.mts +47 -0
- package/dist/types-CRQEk0Oi.d.mts +19 -0
- package/dist/util-LfQirgDk.mjs +113 -0
- package/dist/util-LfQirgDk.mjs.map +1 -0
- package/dist/wasm.d.mts +4 -4
- package/dist/wasm.mjs +55 -56
- package/dist/wasm.mjs.map +1 -1
- package/dist/websocket-CmeqvAkp.d.mts +27 -0
- package/package.json +31 -9
- package/dist/builder-q0VsvnYE.mjs.map +0 -1
- package/dist/client-Bd8B1mYm.mjs.map +0 -1
- package/dist/error-B49rbDzT.d.mts +0 -7
- package/dist/error-CwBxle6N.mjs.map +0 -1
- package/dist/error-DZEs5OuZ.mjs +0 -24
- package/dist/error-DZEs5OuZ.mjs.map +0 -1
- package/dist/event-CzoQEFMF.mjs.map +0 -1
- package/dist/filter-BxwjEkZp.mjs.map +0 -1
- package/dist/filter-DUEKP3q1.d.mts +0 -40
- package/dist/gossip-3xkRRCm_.mjs.map +0 -1
- package/dist/http-D2rhokZo.mjs.map +0 -1
- package/dist/index-C0C4e-Gn.d.mts +0 -55
- package/dist/index-DgOugrpF.d.mts +0 -275
- package/dist/index-EwL6fHLo.d.mts +0 -267
- package/dist/index-k2YhCgsh.d.mts +0 -57
- package/dist/key-Dn-aX4x4.mjs.map +0 -1
- package/dist/kind-BLl4cLOc.mjs.map +0 -1
- package/dist/loaders-BNQszDAn.mjs.map +0 -1
- package/dist/memory-BCmhZPek.mjs.map +0 -1
- package/dist/message-C_Uj-Y-c.mjs.map +0 -1
- package/dist/pool-C_j1OmBB.mjs.map +0 -1
- package/dist/pool-DhRi7l-b.d.mts +0 -297
- package/dist/signer-CZ6K6FUc.mjs.map +0 -1
- package/dist/storage-DOy0iLRE.mjs +0 -870
- package/dist/storage-DOy0iLRE.mjs.map +0 -1
- package/dist/types-BzyADqnw.d.mts +0 -19
- package/dist/types-Dn24TPFs.d.mts +0 -43
- package/dist/util-6tfLQ9JF.mjs +0 -73
- package/dist/util-6tfLQ9JF.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loaders-BNQszDAn.mjs","names":["#events","#relays","#batchLoadFn","#cacheKeyFn","#maxBatchSize","#useCache","#cache","#inflight","#queue","#dispatch","#scheduled","#pool","#gossip","#storage","#discovery","#kinds","#maxRelays","#timeoutMs","#onEvent","#observe","#applySync","#hydrate","#bounds","#authors","#assertOpen","#hydrateBoundsFromStore","#syncFilters","#updateBounds","#persistBounds","#liveCloser","#noteEvent","#closed"],"sources":["../src/loaders/cache.ts","../src/loaders/context.ts","../src/loaders/event.ts","../src/loaders/dataloader.ts","../src/loaders/replaceable.ts","../src/loaders/lists.ts","../src/loaders/profile.ts","../src/loaders/outbox.ts","../src/loaders/index.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner } from \"../core/event.ts\";\n\nexport type ReplaceableKey = {\n kind: number;\n pubkey: string;\n dTag?: string;\n};\n\nfunction keyOf(spec: ReplaceableKey): string {\n return `${spec.kind}:${spec.pubkey.toLowerCase()}:${spec.dTag ?? \"\"}`;\n}\n\n/** In-memory replaceable-event cache used by loaders (no DOM / localStorage). */\nexport class ReplaceableCache {\n #events = new Map<string, { event: Event | null; fetchedAt: number }>();\n\n get(spec: ReplaceableKey): { event: Event | null; fetchedAt: number } | undefined {\n return this.#events.get(keyOf(spec));\n }\n\n set(spec: ReplaceableKey, event: Event | null, fetchedAt = Math.floor(Date.now() / 1000)): void {\n this.#events.set(keyOf(spec), { event, fetchedAt });\n }\n\n /** Keep newer event by created_at. */\n putIfNewer(event: Event, dTag?: string): void {\n const spec: ReplaceableKey = { kind: event.kind, pubkey: event.pubkey, dTag };\n const prev = this.get(spec);\n if (!prev?.event || isReplaceableWinner(event, prev.event)) {\n this.set(spec, event);\n }\n }\n\n clear(spec?: ReplaceableKey): void {\n if (spec) this.#events.delete(keyOf(spec));\n else this.#events.clear();\n }\n\n get size(): number {\n return this.#events.size;\n }\n}\n","import type { Pool } from \"../relay/pool.ts\";\nimport { ReplaceableCache } from \"./cache.ts\";\n\nexport type LoaderContextOptions = {\n pool: Pool;\n /** Fallback / discovery relays when no per-user routing is known. */\n relays: readonly string[];\n cache?: ReplaceableCache;\n /** Max age (seconds) before a cached replaceable is considered stale. Default 2 days. */\n staleAfterSec?: number;\n fetchTimeoutMs?: number;\n};\n\n/**\n * Explicit dependency bag for loaders — never a module-level singleton.\n */\nexport class LoaderContext {\n readonly pool: Pool;\n readonly cache: ReplaceableCache;\n readonly staleAfterSec: number;\n readonly fetchTimeoutMs: number;\n #relays: string[];\n\n constructor(opts: LoaderContextOptions) {\n this.pool = opts.pool;\n this.#relays = [...opts.relays];\n this.cache = opts.cache ?? new ReplaceableCache();\n this.staleAfterSec = opts.staleAfterSec ?? 60 * 60 * 24 * 2;\n this.fetchTimeoutMs = opts.fetchTimeoutMs ?? 4400;\n }\n\n /** Snapshot of discovery/fallback relays. */\n get relays(): readonly string[] {\n return this.#relays;\n }\n\n addRelay(url: string): void {\n if (!this.#relays.includes(url)) this.#relays.push(url);\n }\n\n removeRelay(url: string): void {\n this.#relays = this.#relays.filter((r) => r !== url);\n }\n\n setRelays(urls: readonly string[]): void {\n this.#relays = [...urls];\n }\n\n isFresh(fetchedAt: number, now = Math.floor(Date.now() / 1000)): boolean {\n return now - fetchedAt < this.staleAfterSec;\n }\n}\n","import { isReplaceableWinner, type Event } from \"../core/event.ts\";\nimport { isAddressableKind } from \"../core/kind.ts\";\nimport { isHex32 } from \"../core/util.ts\";\nimport { decode, Nip19Error, type AddressPointer, type EventPointer } from \"../nips/nip19.ts\";\nimport type { LoaderContext } from \"./context.ts\";\n\nexport type EventRef = string | EventPointer | AddressPointer;\n\nfunction dFilter(kind: number, identifier: string): { \"#d\"?: string[] } {\n if (isAddressableKind(kind)) return { \"#d\": [identifier] };\n return {};\n}\n\nfunction parseRef(ref: EventRef): {\n filter: { ids?: string[]; authors?: string[]; kinds?: number[]; \"#d\"?: string[] };\n hints: string[];\n cacheKey: string;\n} {\n if (typeof ref === \"string\") {\n if (isHex32(ref)) {\n return {\n filter: { ids: [ref.toLowerCase()] },\n hints: [],\n cacheKey: `id:${ref.toLowerCase()}`,\n };\n }\n const decoded = decode(ref);\n switch (decoded.type) {\n case \"note\":\n return { filter: { ids: [decoded.data] }, hints: [], cacheKey: `id:${decoded.data}` };\n case \"nevent\":\n return {\n filter: { ids: [decoded.data.id] },\n hints: decoded.data.relays ?? [],\n cacheKey: `id:${decoded.data.id}`,\n };\n case \"naddr\":\n return {\n filter: {\n authors: [decoded.data.pubkey.toLowerCase()],\n kinds: [decoded.data.kind],\n ...dFilter(decoded.data.kind, decoded.data.identifier),\n },\n hints: decoded.data.relays ?? [],\n cacheKey: `addr:${decoded.data.kind}:${decoded.data.pubkey}:${decoded.data.identifier}`,\n };\n default:\n throw new Nip19Error(`cannot load event from ${decoded.type}`);\n }\n }\n if (\"id\" in ref) {\n return {\n filter: { ids: [ref.id.toLowerCase()] },\n hints: ref.relays ?? [],\n cacheKey: `id:${ref.id.toLowerCase()}`,\n };\n }\n return {\n filter: {\n authors: [ref.pubkey.toLowerCase()],\n kinds: [ref.kind],\n ...dFilter(ref.kind, ref.identifier),\n },\n hints: ref.relays ?? [],\n cacheKey: `addr:${ref.kind}:${ref.pubkey}:${ref.identifier}`,\n };\n}\n\nexport function createEventLoader(ctx: LoaderContext) {\n const inflight = new Map<string, Promise<Event | undefined>>();\n const cache = new Map<string, Event | undefined>();\n return {\n load(ref: EventRef): Promise<Event | undefined> {\n const parsed = parseRef(ref);\n if (cache.has(parsed.cacheKey)) return Promise.resolve(cache.get(parsed.cacheKey));\n const hit = inflight.get(parsed.cacheKey);\n if (hit) return hit;\n const p = (async () => {\n const relays = [...new Set([...parsed.hints, ...ctx.relays])];\n if (relays.length === 0) return undefined;\n const events = await ctx.pool.fetch(relays, [parsed.filter], {\n timeoutMs: ctx.fetchTimeoutMs,\n });\n let best: Event | undefined;\n for (const e of events) {\n if (!best || isReplaceableWinner(e, best)) best = e;\n }\n cache.set(parsed.cacheKey, best);\n return best;\n })().finally(() => inflight.delete(parsed.cacheKey));\n inflight.set(parsed.cacheKey, p);\n return p;\n },\n clearAll() {\n inflight.clear();\n cache.clear();\n },\n };\n}\n\nexport type EventLoader = ReturnType<typeof createEventLoader>;\n","import { NostrError } from \"../core/error.ts\";\n\nexport class LoaderError extends NostrError {}\n\n/** Minimal request coalescer. Batches keys within a microtask; no global state. */\nexport type BatchLoadFn<K, V> = (keys: readonly K[]) => Promise<readonly (V | Error)[]>;\n\nexport type DataLoaderOptions<K, C = K> = {\n cacheKeyFn?: (key: K) => C;\n maxBatchSize?: number;\n /** When false, only coalesces in-flight requests (no durable memoization). Default true. */\n cache?: boolean;\n};\n\nexport class DataLoader<K, V, C = K> {\n readonly #batchLoadFn: BatchLoadFn<K, V>;\n readonly #cacheKeyFn: (key: K) => C;\n readonly #maxBatchSize: number;\n readonly #useCache: boolean;\n readonly #cache = new Map<C, Promise<V>>();\n readonly #inflight = new Map<C, Promise<V>>();\n #queue: Array<{\n key: K;\n resolve: (v: V) => void;\n reject: (e: unknown) => void;\n }> = [];\n #scheduled = false;\n\n constructor(batchLoadFn: BatchLoadFn<K, V>, options: DataLoaderOptions<K, C> = {}) {\n this.#batchLoadFn = batchLoadFn;\n this.#cacheKeyFn = options.cacheKeyFn ?? ((k: K) => k as unknown as C);\n this.#maxBatchSize = options.maxBatchSize ?? Infinity;\n this.#useCache = options.cache !== false;\n }\n\n load(key: K): Promise<V> {\n const cacheKey = this.#cacheKeyFn(key);\n if (this.#useCache) {\n const cached = this.#cache.get(cacheKey);\n if (cached) return cached;\n }\n const inflight = this.#inflight.get(cacheKey);\n if (inflight) return inflight;\n\n const promise = new Promise<V>((resolve, reject) => {\n this.#queue.push({ key, resolve, reject });\n if (this.#queue.length >= this.#maxBatchSize) {\n this.#dispatch();\n } else if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => this.#dispatch());\n }\n }).finally(() => {\n this.#inflight.delete(cacheKey);\n });\n\n this.#inflight.set(cacheKey, promise);\n if (this.#useCache) this.#cache.set(cacheKey, promise);\n return promise;\n }\n\n clear(key: K): void {\n this.#cache.delete(this.#cacheKeyFn(key));\n this.#inflight.delete(this.#cacheKeyFn(key));\n }\n\n clearAll(): void {\n this.#cache.clear();\n this.#inflight.clear();\n }\n\n #dispatch(): void {\n this.#scheduled = false;\n const batch = this.#queue.splice(0, this.#maxBatchSize);\n if (batch.length === 0) return;\n\n const keys = batch.map((b) => b.key);\n void this.#batchLoadFn(keys)\n .then((values) => {\n if (values.length !== keys.length) {\n const err = new LoaderError(\n `DataLoader batch function must return array of length ${keys.length}, got ${values.length}`,\n );\n for (const item of batch) item.reject(err);\n return;\n }\n for (let i = 0; i < batch.length; i++) {\n const value = values[i]!;\n if (value instanceof Error) batch[i]!.reject(value);\n else batch[i]!.resolve(value);\n }\n })\n .catch((err) => {\n for (const item of batch) item.reject(err);\n });\n\n if (this.#queue.length > 0) {\n this.#scheduled = true;\n queueMicrotask(() => this.#dispatch());\n }\n }\n}\n","import type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { getDTag } from \"../core/tag.ts\";\nimport { DataLoader } from \"./dataloader.ts\";\nimport type { LoaderContext } from \"./context.ts\";\n\nexport type ReplaceableLoadResult = {\n event: Event | null;\n fresh: boolean;\n};\n\nexport type LoadStyle = \"default\" | \"force\" | \"cache-only\";\n\n/**\n * Batch-fetch replaceable events (kind + authors) via the pool, with cache.\n * Durable cache is ReplaceableCache; DataLoader only coalesces in-flight requests.\n */\nexport function createReplaceableLoader(ctx: LoaderContext, kind: number) {\n type Key = { pubkey: string; hints?: string[] };\n\n const loader = new DataLoader<Key, Event | null, string>(\n async (keys) => {\n const authors = [...new Set(keys.map((k) => k.pubkey))];\n const hintRelays = [...new Set(keys.flatMap((k) => k.hints ?? []).concat(ctx.relays))];\n const filter: Filter = { kinds: [kind], authors };\n const events =\n hintRelays.length > 0\n ? await ctx.pool.fetch(hintRelays, [filter], { timeoutMs: ctx.fetchTimeoutMs })\n : [];\n\n const best = new Map<string, Event>();\n for (const event of events) {\n if (event.kind !== kind) continue;\n const prev = best.get(event.pubkey);\n if (!prev || isReplaceableWinner(event, prev)) best.set(event.pubkey, event);\n }\n\n const now = Math.floor(Date.now() / 1000);\n return keys.map((k) => {\n const event = best.get(k.pubkey) ?? null;\n ctx.cache.set(\n { kind, pubkey: k.pubkey, dTag: event ? (getDTag(event.tags) ?? undefined) : undefined },\n event,\n now,\n );\n return event;\n });\n },\n {\n cache: false,\n cacheKeyFn: (k) => `${kind}:${k.pubkey}:${(k.hints ?? []).join(\",\")}`,\n maxBatchSize: 50,\n },\n );\n\n return {\n async load(\n pubkey: string,\n opts?: { hints?: string[]; style?: LoadStyle },\n ): Promise<ReplaceableLoadResult> {\n const pk = pubkey.toLowerCase();\n const style = opts?.style ?? \"default\";\n const cached = ctx.cache.get({ kind, pubkey: pk });\n\n if (style === \"cache-only\") {\n return { event: cached?.event ?? null, fresh: false };\n }\n if (style === \"default\" && cached && ctx.isFresh(cached.fetchedAt)) {\n return { event: cached.event, fresh: false };\n }\n\n const event = await loader.load({ pubkey: pk, hints: opts?.hints });\n return { event, fresh: true };\n },\n clear(pubkey?: string) {\n if (pubkey) {\n const pk = pubkey.toLowerCase();\n loader.clear({ pubkey: pk });\n ctx.cache.clear({ kind, pubkey: pk });\n } else {\n loader.clearAll();\n }\n },\n };\n}\n","import type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { isHex32, normalizeURL } from \"../core/util.ts\";\nimport { parseDmRelayList } from \"../nips/nip17.ts\";\nimport type { RelayListItem } from \"../nips/nip65.ts\";\nimport { parseRelayList } from \"../nips/nip65.ts\";\nimport type { LoaderContext } from \"./context.ts\";\nimport { createReplaceableLoader, type LoadStyle } from \"./replaceable.ts\";\n\nexport type ListResult<T> = {\n event: Event | null;\n items: T[];\n fresh: boolean;\n};\n\nexport type MutedEntity =\n | { label: \"pubkey\"; value: string }\n | { label: \"thread\"; value: string }\n | { label: \"hashtag\"; value: string }\n | { label: \"word\"; value: string };\n\nfunction fromTags<T>(event: Event | null, map: (tag: readonly string[]) => T | undefined): T[] {\n if (!event) return [];\n const out: T[] = [];\n for (const tag of event.tags) {\n const item = map(tag);\n if (item !== undefined) out.push(item);\n }\n return out;\n}\n\nexport function createListLoaders(ctx: LoaderContext) {\n const followsLoader = createReplaceableLoader(ctx, Kind.Contacts);\n const muteLoader = createReplaceableLoader(ctx, Kind.MuteList);\n const relayListLoader = createReplaceableLoader(ctx, Kind.RelayList);\n const dmRelayListLoader = createReplaceableLoader(ctx, Kind.DirectMessageRelaysList);\n\n return {\n async follows(\n pubkey: string,\n opts?: { hints?: string[]; style?: LoadStyle },\n ): Promise<ListResult<string>> {\n const { event, fresh } = await followsLoader.load(pubkey, opts);\n return {\n event,\n fresh,\n items: fromTags(event, (tag) =>\n tag[0] === \"p\" && tag[1] && isHex32(tag[1]) ? tag[1].toLowerCase() : undefined,\n ),\n };\n },\n\n async muteList(\n pubkey: string,\n opts?: { hints?: string[]; style?: LoadStyle },\n ): Promise<ListResult<MutedEntity>> {\n const { event, fresh } = await muteLoader.load(pubkey, opts);\n return {\n event,\n fresh,\n items: fromTags(event, (tag) => {\n if (!tag[1]) return undefined;\n switch (tag[0]) {\n case \"p\":\n return isHex32(tag[1]) ? { label: \"pubkey\", value: tag[1].toLowerCase() } : undefined;\n case \"e\":\n return isHex32(tag[1]) ? { label: \"thread\", value: tag[1].toLowerCase() } : undefined;\n case \"t\":\n return { label: \"hashtag\", value: tag[1] };\n case \"word\":\n return { label: \"word\", value: tag[1] };\n default:\n return undefined;\n }\n }),\n };\n },\n\n async relayList(\n pubkey: string,\n opts?: { hints?: string[]; style?: LoadStyle },\n ): Promise<ListResult<RelayListItem>> {\n const { event, fresh } = await relayListLoader.load(pubkey, opts);\n let items: RelayListItem[] = [];\n if (event) {\n try {\n items = parseRelayList(event);\n } catch {\n items = [];\n }\n }\n // ensure normalized urls even if parse skipped\n items = items.map((i) => {\n try {\n return { ...i, url: normalizeURL(i.url) };\n } catch {\n return i;\n }\n });\n return { event, fresh, items };\n },\n\n async dmRelayList(\n pubkey: string,\n opts?: { hints?: string[]; style?: LoadStyle },\n ): Promise<ListResult<string>> {\n const { event, fresh } = await dmRelayListLoader.load(pubkey, opts);\n let items: string[] = [];\n if (event) {\n try {\n items = parseDmRelayList(event);\n } catch {\n items = [];\n }\n }\n return { event, fresh, items };\n },\n };\n}\n\nexport type ListLoaders = ReturnType<typeof createListLoaders>;\n","import type { Event } from \"../core/event.ts\";\nimport type { ProfileMetadata } from \"../core/builder.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { npubEncode } from \"../nips/nip19.ts\";\nimport type { LoaderContext } from \"./context.ts\";\nimport { createReplaceableLoader, type LoadStyle } from \"./replaceable.ts\";\n\nexport type NostrUser = {\n pubkey: string;\n npub: string;\n shortName: string;\n image?: string;\n metadata: ProfileMetadata;\n lastUpdated: number;\n event: Event | null;\n fresh: boolean;\n};\n\nexport function bareNostrUser(pubkey: string): NostrUser {\n const pk = pubkey.toLowerCase();\n let npub: string;\n try {\n npub = npubEncode(pk);\n } catch {\n npub = pk;\n }\n return {\n pubkey: pk,\n npub,\n shortName: npub.startsWith(\"npub1\") ? `${npub.slice(0, 8)}…${npub.slice(-4)}` : pk.slice(0, 8),\n metadata: {},\n lastUpdated: 0,\n event: null,\n fresh: false,\n };\n}\n\nfunction parseMetadata(content: string): ProfileMetadata {\n try {\n const obj = JSON.parse(content) as ProfileMetadata;\n return typeof obj === \"object\" && obj !== null ? obj : {};\n } catch {\n return {};\n }\n}\n\nexport function createProfileLoader(ctx: LoaderContext) {\n const loader = createReplaceableLoader(ctx, Kind.Metadata);\n\n return {\n async load(pubkey: string, opts?: { hints?: string[]; style?: LoadStyle }): Promise<NostrUser> {\n const base = bareNostrUser(pubkey);\n const { event, fresh } = await loader.load(pubkey, opts);\n if (!event) return { ...base, fresh };\n\n const metadata = parseMetadata(event.content);\n const display = metadata.display_name || metadata.name;\n return {\n ...base,\n shortName: display || base.shortName,\n image: metadata.picture,\n metadata,\n lastUpdated: event.created_at,\n event,\n fresh,\n };\n },\n };\n}\n\nexport type ProfileLoader = ReturnType<typeof createProfileLoader>;\n","import type { Event } from \"../core/event.ts\";\nimport { NostrError } from \"../core/error.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { sortedEvents } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport { toStorageError } from \"../storage/error.ts\";\nimport type { EventStore, OutboxBound } from \"../storage/types.ts\";\n\nexport type { OutboxBound } from \"../storage/types.ts\";\n\nexport class OutboxError extends NostrError {}\n\nexport type OutboxFeedOptions = {\n pool: Pool;\n gossip: Gossip;\n storage: EventStore;\n /** Fallback relays when an author has no known outbox routes. */\n discoveryRelays: readonly string[];\n authors: readonly string[];\n /** Event kinds to fetch. Default `[Kind.TextNote]`. */\n kinds?: readonly number[];\n /** Max write-relays to use per author. Default 3. */\n maxRelaysPerAuthor?: number;\n fetchTimeoutMs?: number;\n /** Called for live events after observe, and for sync events returned by applySync. */\n onEvent?: (event: Event) => void;\n /** Live ingest (e.g. `client.observe`). StartLive never putManys. */\n observe?: (event: Event) => void;\n /**\n * Sync path only. Awaited. Return events that should advance bounds.\n * Throw → sync throws, bounds unchanged.\n */\n applySync?: (events: readonly Event[]) => Promise<readonly Event[]>;\n /**\n * Load kind:10002 for authors before sync when routes are missing.\n * Requires a relay-list loader.\n */\n hydrate?: (pubkeys: readonly string[]) => Promise<void>;\n};\n\nfunction boundKey(pubkey: string, kind: number): string {\n return `${pubkey.toLowerCase()}:${kind}`;\n}\n\n/** Same skip/dedup as Gossip.setRoutes so prefer can match Client.relays. */\nfunction canonicalRelayUrls(urls: readonly string[]): string[] {\n const out: string[] = [];\n for (const raw of urls) {\n try {\n const url = normalizeURL(raw);\n if (!out.includes(url)) out.push(url);\n } catch {\n // not a relay URL\n }\n }\n return out;\n}\n\n/**\n * Group authors by outbox relay (discovery fallback).\n * `prefer` reorders existing candidates only; it never appends a URL\n * that is not already in the author's outbox or discovery list.\n * Returns Map<relayUrl, authors[]>.\n */\nexport function groupAuthorsByOutboxRelay(\n authors: readonly string[],\n gossip: Gossip,\n discoveryRelays: readonly string[],\n maxRelaysPerAuthor = 3,\n prefer: readonly string[] = [],\n): Map<string, string[]> {\n const leftover = new Set(\n (gossip.route({ authors: authors.map((a) => a.toLowerCase()) }).remainder?.authors ?? []).map(\n (a) => a.toLowerCase(),\n ),\n );\n const preferSet = new Set(canonicalRelayUrls(prefer));\n const discovery = canonicalRelayUrls(discoveryRelays);\n const map = new Map<string, string[]>();\n\n for (const author of authors) {\n const pk = author.toLowerCase();\n const urls = leftover.has(pk) ? discovery : gossip.outboxRelays(pk);\n const preferred: string[] = [];\n const rest: string[] = [];\n for (const url of urls) {\n if (preferSet.has(url)) preferred.push(url);\n else rest.push(url);\n }\n for (const url of preferred.concat(rest).slice(0, maxRelaysPerAuthor)) {\n const list = map.get(url) ?? [];\n if (!list.includes(pk)) list.push(pk);\n map.set(url, list);\n }\n }\n return map;\n}\n\n/**\n * Outbox-model feed: history sync + live subscription for a set of authors,\n * routed via NIP-65 write relays (with discovery fallback).\n */\nexport class OutboxFeed {\n readonly #pool: Pool;\n readonly #gossip: Gossip;\n readonly #storage: EventStore;\n readonly #discovery: string[];\n readonly #kinds: number[];\n readonly #maxRelays: number;\n readonly #timeoutMs: number;\n readonly #onEvent: ((event: Event) => void) | undefined;\n readonly #observe: ((event: Event) => void) | undefined;\n readonly #applySync: ((events: readonly Event[]) => Promise<readonly Event[]>) | undefined;\n readonly #hydrate: ((pubkeys: readonly string[]) => Promise<void>) | undefined;\n readonly #bounds = new Map<string, OutboxBound>();\n #authors: string[];\n #liveCloser: { close: (reason?: string) => void } | undefined;\n #closed = false;\n\n constructor(opts: OutboxFeedOptions) {\n this.#pool = opts.pool;\n this.#gossip = opts.gossip;\n this.#storage = opts.storage;\n this.#discovery = [...opts.discoveryRelays];\n this.#authors = [...new Set(opts.authors.map((a) => a.toLowerCase()))];\n this.#kinds = [...(opts.kinds ?? [Kind.TextNote])];\n this.#maxRelays = opts.maxRelaysPerAuthor ?? 3;\n this.#timeoutMs = opts.fetchTimeoutMs ?? 4400;\n this.#onEvent = opts.onEvent;\n this.#observe = opts.observe;\n this.#applySync = opts.applySync;\n this.#hydrate = opts.hydrate;\n }\n\n get authors(): readonly string[] {\n return this.#authors;\n }\n\n get kinds(): readonly number[] {\n return this.#kinds;\n }\n\n /** Replace the author set (does not auto-restart live). */\n setAuthors(authors: readonly string[]): void {\n this.#authors = [...new Set(authors.map((a) => a.toLowerCase()))];\n }\n\n getBound(pubkey: string, kind: number): OutboxBound | undefined {\n return this.#bounds.get(boundKey(pubkey, kind));\n }\n\n /**\n * Load NIP-65 relay lists for authors that have no outbox routes yet.\n */\n async hydrate(): Promise<void> {\n this.#assertOpen();\n if (!this.#hydrate) return;\n const missing = this.#authors.filter((pk) => this.#gossip.outboxRelays(pk).length === 0);\n if (missing.length === 0) return;\n await this.#hydrate(missing);\n }\n\n /**\n * One-shot history pull. Persistence is `applySync`; bounds move only for events it returns.\n */\n async sync(opts?: {\n limit?: number;\n since?: number;\n until?: number;\n signal?: AbortSignal;\n /** Skip hydrate() even if provided. */\n skipHydrate?: boolean;\n }): Promise<Event[]> {\n this.#assertOpen();\n if (!opts?.skipHydrate) await this.hydrate();\n await this.#hydrateBoundsFromStore();\n\n const byRelay = groupAuthorsByOutboxRelay(\n this.#authors,\n this.#gossip,\n this.#discovery,\n this.#maxRelays,\n this.#pool.connectedUrls(),\n );\n\n if (byRelay.size === 0) return [];\n\n const byId = new Map<string, Event>();\n await Promise.all(\n [...byRelay.entries()].map(async ([url, authors]) => {\n if (opts?.signal?.aborted) return;\n\n const filters = this.#syncFilters(authors, opts);\n try {\n const batch = await this.#pool.fetch([url], filters, {\n timeoutMs: this.#timeoutMs,\n signal: opts?.signal,\n });\n for (const event of batch) byId.set(event.id, event);\n } catch {\n // skip failed relay\n }\n }),\n );\n\n const unique = [...byId.values()];\n if (unique.length === 0) return [];\n\n let applied: readonly Event[] = [];\n if (this.#applySync) {\n try {\n applied = await this.#applySync(unique);\n } catch (err) {\n throw toStorageError(err);\n }\n }\n const dirty = new Set<string>();\n for (const event of applied) {\n this.#updateBounds(event);\n dirty.add(boundKey(event.pubkey, event.kind));\n this.#onEvent?.(event);\n }\n await this.#persistBounds(dirty);\n return sortedEvents(unique);\n }\n\n /**\n * Start a live subscription across outbox relays for the current authors.\n * Closes any previous live subscription.\n */\n startLive(opts?: { signal?: AbortSignal; since?: number }): { close: (reason?: string) => void } {\n this.#assertOpen();\n this.#liveCloser?.close(\"restart\");\n\n const byRelay = groupAuthorsByOutboxRelay(\n this.#authors,\n this.#gossip,\n this.#discovery,\n this.#maxRelays,\n this.#pool.connectedUrls(),\n );\n\n const seen = new Set<string>();\n const closers: Array<{ close: (reason?: string) => void }> = [];\n const since = opts?.since ?? Math.floor(Date.now() / 1000) - 60;\n\n for (const [url, authors] of byRelay) {\n const filter: Filter = {\n authors,\n kinds: this.#kinds,\n since,\n };\n closers.push(\n this.#pool.subscribe([url], [filter], {\n signal: opts?.signal,\n onevent: (event) => {\n if (seen.has(event.id)) return;\n seen.add(event.id);\n this.#noteEvent(event);\n },\n }),\n );\n }\n\n const closer = {\n close: (reason?: string) => {\n for (const c of closers) c.close(reason);\n if (this.#liveCloser === closer) this.#liveCloser = undefined;\n },\n };\n this.#liveCloser = closer;\n return closer;\n }\n\n /** Convenience: hydrate + sync + startLive. */\n async start(opts?: {\n limit?: number;\n signal?: AbortSignal;\n }): Promise<{ close: (reason?: string) => void; events: Event[] }> {\n const events = await this.sync({ limit: opts?.limit, signal: opts?.signal });\n const live = this.startLive({ signal: opts?.signal });\n return { close: (r) => live.close(r), events };\n }\n\n close(): void {\n this.#closed = true;\n this.#liveCloser?.close(\"feed closed\");\n this.#liveCloser = undefined;\n }\n\n #assertOpen(): void {\n if (this.#closed) throw new OutboxError(\"OutboxFeed is closed\");\n }\n\n async #hydrateBoundsFromStore(): Promise<void> {\n await Promise.all(\n this.#authors.flatMap((pk) =>\n this.#kinds.map(async (kind) => {\n const key = boundKey(pk, kind);\n if (this.#bounds.has(key)) return;\n const bound = await this.#storage.getOutboxBound(pk, kind);\n if (!bound) return;\n await this.#storage.setOutboxBound(pk, kind, bound);\n this.#bounds.set(key, { oldest: bound.oldest, newest: bound.newest });\n }),\n ),\n );\n }\n\n #syncFilters(\n authors: string[],\n opts?: { limit?: number; since?: number; until?: number },\n ): Filter[] {\n const base: Filter = {\n kinds: this.#kinds,\n limit: opts?.limit ?? 50,\n ...(opts?.until !== undefined ? { until: opts.until } : {}),\n };\n\n if (opts?.since !== undefined) {\n return [{ ...base, authors, since: opts.since }];\n }\n\n const bounded: string[] = [];\n const unbounded: string[] = [];\n let minBoundedNewest: number | undefined;\n\n for (const pk of authors) {\n let newest: number | undefined;\n let complete = true;\n for (const kind of this.#kinds) {\n const b = this.#bounds.get(boundKey(pk, kind));\n if (!b) {\n complete = false;\n break;\n }\n if (newest === undefined || b.newest < newest) newest = b.newest;\n }\n if (complete && newest !== undefined) {\n bounded.push(pk);\n if (minBoundedNewest === undefined || newest < minBoundedNewest) {\n minBoundedNewest = newest;\n }\n } else {\n unbounded.push(pk);\n }\n }\n\n if (bounded.length === 0 || minBoundedNewest === undefined) {\n return [{ ...base, authors }];\n }\n if (unbounded.length === 0) {\n return [{ ...base, authors, since: Math.max(0, minBoundedNewest - 1) }];\n }\n return [\n { ...base, authors: bounded, since: Math.max(0, minBoundedNewest - 1) },\n { ...base, authors: unbounded },\n ];\n }\n\n #noteEvent(event: Event): void {\n this.#updateBounds(event);\n const bound = this.#bounds.get(boundKey(event.pubkey, event.kind));\n if (bound) {\n void this.#storage\n .setOutboxBound(event.pubkey, event.kind, {\n oldest: bound.oldest,\n newest: bound.newest,\n })\n .catch(() => {});\n }\n this.#observe?.(event);\n this.#onEvent?.(event);\n }\n\n async #persistBounds(keys: Iterable<string>): Promise<void> {\n const writes: Promise<void>[] = [];\n for (const key of keys) {\n const bound = this.#bounds.get(key);\n if (!bound) continue;\n const sep = key.lastIndexOf(\":\");\n writes.push(\n this.#storage.setOutboxBound(key.slice(0, sep), Number(key.slice(sep + 1)), {\n oldest: bound.oldest,\n newest: bound.newest,\n }),\n );\n }\n if (writes.length === 0) return;\n try {\n await Promise.all(writes);\n } catch (err) {\n throw toStorageError(err);\n }\n }\n\n #updateBounds(event: Event): void {\n const key = boundKey(event.pubkey, event.kind);\n const prev = this.#bounds.get(key);\n if (!prev) {\n this.#bounds.set(key, { oldest: event.created_at, newest: event.created_at });\n return;\n }\n if (event.created_at < prev.oldest) prev.oldest = event.created_at;\n if (event.created_at > prev.newest) prev.newest = event.created_at;\n }\n}\n\n/** Factory matching other loaders helpers. */\nexport function createOutboxFeed(opts: OutboxFeedOptions): OutboxFeed {\n return new OutboxFeed(opts);\n}\n","import type { Pool } from \"../relay/pool.ts\";\nimport { LoaderContext } from \"./context.ts\";\nimport { createEventLoader } from \"./event.ts\";\nimport { createListLoaders } from \"./lists.ts\";\nimport { createProfileLoader } from \"./profile.ts\";\nimport type { ReplaceableCache } from \"./cache.ts\";\n\nexport { DataLoader, LoaderError } from \"./dataloader.ts\";\nexport { ReplaceableCache } from \"./cache.ts\";\nexport { LoaderContext, type LoaderContextOptions } from \"./context.ts\";\nexport {\n createReplaceableLoader,\n type LoadStyle,\n type ReplaceableLoadResult,\n} from \"./replaceable.ts\";\nexport { createListLoaders, type ListResult, type ListLoaders, type MutedEntity } from \"./lists.ts\";\nexport {\n createProfileLoader,\n bareNostrUser,\n type NostrUser,\n type ProfileLoader,\n} from \"./profile.ts\";\nexport { createEventLoader, type EventLoader, type EventRef } from \"./event.ts\";\nexport {\n OutboxError,\n OutboxFeed,\n createOutboxFeed,\n groupAuthorsByOutboxRelay,\n type OutboxBound,\n type OutboxFeedOptions,\n} from \"./outbox.ts\";\n\nimport type { ListLoaders } from \"./lists.ts\";\nimport type { ProfileLoader } from \"./profile.ts\";\nimport type { EventLoader } from \"./event.ts\";\n\nexport type Loaders = {\n context: LoaderContext;\n follows: ListLoaders[\"follows\"];\n muteList: ListLoaders[\"muteList\"];\n relayList: ListLoaders[\"relayList\"];\n dmRelayList: ListLoaders[\"dmRelayList\"];\n profile: ProfileLoader[\"load\"];\n event: EventLoader[\"load\"];\n};\n\nexport type CreateLoadersOptions = {\n pool: Pool;\n relays: readonly string[];\n cache?: ReplaceableCache;\n staleAfterSec?: number;\n fetchTimeoutMs?: number;\n};\n\n/** Build an instance-scoped loader suite (no module globals). */\nexport function createLoaders(opts: CreateLoadersOptions): Loaders {\n const context = new LoaderContext(opts);\n const lists = createListLoaders(context);\n const profile = createProfileLoader(context);\n const event = createEventLoader(context);\n return {\n context,\n follows: (pubkey, o) => lists.follows(pubkey, o),\n muteList: (pubkey, o) => lists.muteList(pubkey, o),\n relayList: (pubkey, o) => lists.relayList(pubkey, o),\n dmRelayList: (pubkey, o) => lists.dmRelayList(pubkey, o),\n profile: (pubkey, o) => profile.load(pubkey, o),\n event: (ref) => event.load(ref),\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"memory-BCmhZPek.mjs","names":["#deletion","#byId","#replaceable","#indexInsert","#indexRemove","#matchedEvents","#eachCandidate","#outboxBounds","#deriveOutboxBound","#byPubkey","#byKind","#byKindPubkey","#byEpTag"],"sources":["../src/storage/deletion.ts","../src/storage/put.ts","../src/storage/memory.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { eventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\n\nexport type DeletionPlan = {\n /** Stored events validated as same-pubkey (not kind 5) and to be removed now. */\n removeIds: string[];\n /** Referenced ids not in the store; remember deletion pubkey until the event arrives. */\n pendingIds: Array<{ id: string; pubkey: string }>;\n /** Replaceable/addressable coordinates tombstoned up to `until` (deletion created_at). */\n coordinates: Array<{ key: string; until: number }>;\n};\n\n/**\n * NIP-09 application plan for a kind 5 event.\n *\n * - `e` tags: delete only when the target exists, is not itself kind 5, and shares pubkey.\n * - Unknown `e` ids are pending until the event arrives (client MUST validate pubkey).\n * - `a` tags: tombstone that coordinate up to this deletion's `created_at` when pubkey matches.\n * - Deleting a deletion request is a no-op.\n */\nexport function planDeletion(\n deletion: Pick<Event, \"pubkey\" | \"created_at\" | \"tags\">,\n getById: (id: string) => Pick<Event, \"id\" | \"pubkey\" | \"kind\"> | undefined,\n): DeletionPlan {\n const pubkey = deletion.pubkey.toLowerCase();\n const removeIds: string[] = [];\n const pendingIds: Array<{ id: string; pubkey: string }> = [];\n const coordinates: Array<{ key: string; until: number }> = [];\n const seenIds = new Set<string>();\n const seenCoords = new Set<string>();\n\n for (const tag of deletion.tags) {\n if (tag[0] === \"e\" && tag[1] && isHex32(tag[1])) {\n const id = tag[1].toLowerCase();\n if (seenIds.has(id)) continue;\n seenIds.add(id);\n const existing = getById(id);\n if (!existing) {\n pendingIds.push({ id, pubkey });\n continue;\n }\n if (existing.kind === Kind.EventDeletion) continue;\n if (existing.pubkey.toLowerCase() !== pubkey) continue;\n removeIds.push(id);\n continue;\n }\n\n if (tag[0] === \"a\" && tag[1]) {\n const coord = parseEventAddress(tag[1]);\n if (!coord || coord.pubkey !== pubkey) continue;\n const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;\n if (seenCoords.has(key)) continue;\n seenCoords.add(key);\n coordinates.push({ key, until: deletion.created_at });\n }\n }\n\n return { removeIds, pendingIds, coordinates };\n}\n\n/** In-memory NIP-09 tombstones shared by MemoryEventStore and IndexedDbEventStore. */\nexport class DeletionState {\n readonly ids = new Set<string>();\n readonly pending = new Map<string, string>();\n readonly coordinates = new Map<string, number>();\n\n clear(): void {\n this.ids.clear();\n this.pending.clear();\n this.coordinates.clear();\n }\n\n covers(event: Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\">): boolean {\n if (this.ids.has(event.id)) return true;\n if (event.kind === Kind.EventDeletion) return false;\n const pendingPk = this.pending.get(event.id);\n if (pendingPk && event.pubkey.toLowerCase() === pendingPk) return true;\n const addr = eventAddress(event);\n if (!addr) return false;\n const until = this.coordinates.get(addr);\n return until !== undefined && event.created_at <= until;\n }\n\n absorb(plan: DeletionPlan): void {\n for (const id of plan.removeIds) {\n this.ids.add(id);\n this.pending.delete(id);\n }\n for (const p of plan.pendingIds) {\n if (!this.ids.has(p.id)) this.pending.set(p.id, p.pubkey);\n }\n for (const c of plan.coordinates) {\n const prev = this.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;\n this.coordinates.set(c.key, Math.max(prev, c.until));\n }\n }\n}\n\nexport function coordinateRemovals(\n coordinates: readonly { key: string; until: number }[],\n getCurrent: (key: string) => Pick<Event, \"id\" | \"created_at\"> | undefined,\n): string[] {\n const ids: string[] = [];\n for (const c of coordinates) {\n const current = getCurrent(c.key);\n if (current && current.created_at <= c.until) ids.push(current.id);\n }\n return ids;\n}\n","import type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner } from \"../core/event.ts\";\nimport { isEphemeralKind, Kind } from \"../core/kind.ts\";\nimport { eventAddress } from \"../core/tag.ts\";\nimport {\n coordinateRemovals,\n planDeletion,\n type DeletionPlan,\n type DeletionState,\n} from \"./deletion.ts\";\nimport type { PutResult } from \"./types.ts\";\n\nexport function normalizeEvent(event: Event): Event {\n const id = event.id.toLowerCase();\n const pubkey = event.pubkey.toLowerCase();\n if (id === event.id && pubkey === event.pubkey) return event;\n return { ...event, id, pubkey };\n}\n\nexport function outboxBoundKey(pubkey: string, kind: number): string {\n return `${pubkey.toLowerCase()}:${kind}`;\n}\n\nexport type PutDecision =\n | { action: \"skip\"; result: \"duplicate\" | \"ephemeral\" | \"rejected\"; event: Event }\n | { action: \"tombstone\"; result: \"duplicate\"; event: Event }\n | { action: \"delete\"; result: \"deleted\"; event: Event; plan: DeletionPlan; coordIds: string[] }\n | {\n action: \"insert\";\n result: \"accepted\" | \"replaced\";\n event: Event;\n address?: string;\n replaceId?: string;\n };\n\nexport type PutLookup = {\n deletion: DeletionState;\n getById: (\n id: string,\n ) => Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\"> | undefined;\n getReplaceable: (address: string) => Pick<Event, \"id\" | \"created_at\"> | undefined;\n};\n\nexport function decidePut(raw: Event, lookup: PutLookup): PutDecision {\n const event = normalizeEvent(raw);\n if (lookup.deletion.ids.has(event.id) || lookup.getById(event.id)) {\n return { action: \"skip\", result: \"duplicate\", event };\n }\n if (event.kind === Kind.EventDeletion) {\n const plan = planDeletion(event, lookup.getById);\n const coordIds = coordinateRemovals(plan.coordinates, lookup.getReplaceable);\n return { action: \"delete\", result: \"deleted\", event, plan, coordIds };\n }\n if (lookup.deletion.covers(event)) return { action: \"tombstone\", result: \"duplicate\", event };\n if (isEphemeralKind(event.kind)) return { action: \"skip\", result: \"ephemeral\", event };\n const address = eventAddress(event);\n if (address) {\n const prev = lookup.getReplaceable(address);\n if (prev && !isReplaceableWinner(event, prev)) {\n return { action: \"skip\", result: \"rejected\", event };\n }\n return {\n action: \"insert\",\n result: prev ? \"replaced\" : \"accepted\",\n event,\n address,\n replaceId: prev?.id,\n };\n }\n return { action: \"insert\", result: \"accepted\", event };\n}\n\nexport function applyPutMemory(\n s: {\n deletion: DeletionState;\n indexInsert(event: Event): void;\n indexRemove(id: string): boolean;\n },\n d: PutDecision,\n): PutResult {\n switch (d.action) {\n case \"skip\":\n return d.result;\n case \"tombstone\":\n s.deletion.ids.add(d.event.id);\n s.deletion.pending.delete(d.event.id);\n return \"duplicate\";\n case \"delete\":\n s.deletion.pending.delete(d.event.id);\n s.deletion.absorb(d.plan);\n for (const id of d.plan.removeIds) s.indexRemove(id);\n for (const id of d.coordIds) {\n s.deletion.ids.add(id);\n s.indexRemove(id);\n }\n s.indexInsert(d.event);\n return \"deleted\";\n case \"insert\":\n if (d.replaceId) s.indexRemove(d.replaceId);\n s.indexInsert(d.event);\n return d.result;\n }\n}\n","import type { Event } from \"../core/event.ts\";\nimport { itemCompare, sortEvents } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { matchFilter } from \"../core/filter.ts\";\nimport { eventAddress } from \"../core/tag.ts\";\nimport { DeletionState } from \"./deletion.ts\";\nimport { applyPutMemory, decidePut, outboxBoundKey, type PutLookup } from \"./put.ts\";\nimport type { EventStore, NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\n/**\n * In-memory event store with NIP-01 replaceable / addressable / ephemeral\n * handling and NIP-09 deletion (kind 5) application.\n */\nexport class MemoryEventStore implements EventStore {\n #byId = new Map<string, Event>();\n #byPubkey = new Map<string, Set<string>>();\n #byKind = new Map<number, Set<string>>();\n #byKindPubkey = new Map<string, Set<string>>(); // `${kind}:${pubkey}` → ids\n #byEpTag = new Map<string, Set<string>>(); // `${\"e\"|\"p\"}:${value.toLowerCase()}` → ids\n #replaceable = new Map<string, string>(); // address -> event id\n #deletion = new DeletionState();\n #outboxBounds = new Map<string, OutboxBound>();\n\n async put(raw: Event): Promise<PutResult> {\n const lookup: PutLookup = {\n deletion: this.#deletion,\n getById: (id) => this.#byId.get(id),\n getReplaceable: (addr) => {\n const id = this.#replaceable.get(addr);\n const ev = id ? this.#byId.get(id) : undefined;\n return ev ? { id: ev.id, created_at: ev.created_at } : undefined;\n },\n };\n return applyPutMemory(\n {\n deletion: this.#deletion,\n indexInsert: (e) => this.#indexInsert(e),\n indexRemove: (id) => this.#indexRemove(id),\n },\n decidePut(raw, lookup),\n );\n }\n\n /** Sequential `put` in input order. No transaction: a throw leaves earlier events applied. */\n async putMany(events: readonly Event[]): Promise<PutResult[]> {\n const results: PutResult[] = [];\n for (const event of events) results.push(await this.put(event));\n return results;\n }\n\n async get(id: string): Promise<Event | undefined> {\n const key = id.toLowerCase();\n if (this.#deletion.ids.has(key)) return undefined;\n return this.#byId.get(key);\n }\n\n async query(filters: Filter[]): Promise<Event[]> {\n const seen = new Set<string>();\n const events: Event[] = [];\n for (const filter of filters) {\n for (const event of this.#matchedEvents(filter)) {\n if (seen.has(event.id)) continue;\n seen.add(event.id);\n events.push(event);\n }\n }\n sortEvents(events);\n return events;\n }\n\n async count(filters: Filter[]): Promise<number> {\n const seen = new Set<string>();\n for (const filter of filters) {\n for (const event of this.#matchedEvents(filter)) seen.add(event.id);\n }\n return seen.size;\n }\n\n async negentropyItems(filter: Filter): Promise<NegentropyItem[]> {\n if (filter.limit === 0) return [];\n const items: NegentropyItem[] = [];\n this.#eachCandidate(filter, (event) => {\n if (this.#deletion.covers(event)) return;\n if (!matchFilter(filter, event)) return;\n items.push({ id: event.id, created_at: event.created_at });\n });\n if (filter.limit !== undefined) {\n items.sort(queryItemOrder);\n if (items.length > filter.limit) items.length = filter.limit;\n }\n items.sort(itemCompare);\n return items;\n }\n\n async remove(ids: string[]): Promise<number> {\n let n = 0;\n for (const raw of ids) {\n const id = raw.toLowerCase();\n if (this.#indexRemove(id)) n += 1;\n this.#deletion.ids.add(id);\n this.#deletion.pending.delete(id);\n }\n return n;\n }\n\n async getOutboxBound(pubkey: string, kind: number): Promise<OutboxBound | undefined> {\n const persisted = this.#outboxBounds.get(outboxBoundKey(pubkey, kind));\n if (persisted) return { oldest: persisted.oldest, newest: persisted.newest };\n return this.#deriveOutboxBound(pubkey, kind);\n }\n\n async setOutboxBound(pubkey: string, kind: number, bound: OutboxBound): Promise<void> {\n this.#outboxBounds.set(outboxBoundKey(pubkey, kind), {\n oldest: bound.oldest,\n newest: bound.newest,\n });\n }\n\n async clear(): Promise<void> {\n this.#byId.clear();\n this.#byPubkey.clear();\n this.#byKind.clear();\n this.#byKindPubkey.clear();\n this.#byEpTag.clear();\n this.#replaceable.clear();\n this.#deletion.clear();\n this.#outboxBounds.clear();\n }\n\n get size(): number {\n return this.#byId.size;\n }\n\n #indexInsert(event: Event): void {\n this.#byId.set(event.id, event);\n const pubkey = event.pubkey.toLowerCase();\n addToSet(this.#byPubkey, pubkey, event.id);\n addToSet(this.#byKind, event.kind, event.id);\n addToSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, event.id);\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) continue;\n addToSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, event.id);\n }\n const addr = eventAddress(event);\n if (addr) this.#replaceable.set(addr, event.id);\n }\n\n #indexRemove(id: string): boolean {\n const key = id.toLowerCase();\n const event = this.#byId.get(key);\n if (!event) return false;\n this.#byId.delete(key);\n const pubkey = event.pubkey.toLowerCase();\n removeFromSet(this.#byPubkey, pubkey, key);\n removeFromSet(this.#byKind, event.kind, key);\n removeFromSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, key);\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) continue;\n removeFromSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, key);\n }\n const addr = eventAddress(event);\n if (addr && this.#replaceable.get(addr) === key) this.#replaceable.delete(addr);\n return true;\n }\n\n #matchedEvents(filter: Filter): Event[] {\n if (filter.limit === 0) return [];\n const matched: Event[] = [];\n this.#eachCandidate(filter, (event) => {\n if (this.#deletion.covers(event)) return;\n if (!matchFilter(filter, event)) return;\n matched.push(event);\n });\n sortEvents(matched);\n return filter.limit !== undefined ? matched.slice(0, filter.limit) : matched;\n }\n\n #deriveOutboxBound(pubkey: string, kind: number): OutboxBound | undefined {\n const byPk = this.#byPubkey.get(pubkey.toLowerCase());\n const byKind = this.#byKind.get(kind);\n if (!byPk || !byKind) return undefined;\n let oldest: number | undefined;\n let newest: number | undefined;\n for (const id of byPk) {\n if (!byKind.has(id) || this.#deletion.ids.has(id)) continue;\n const event = this.#byId.get(id);\n if (!event || this.#deletion.covers(event)) continue;\n if (oldest === undefined || event.created_at < oldest) oldest = event.created_at;\n if (newest === undefined || event.created_at > newest) newest = event.created_at;\n }\n if (oldest === undefined || newest === undefined) return undefined;\n return { oldest, newest };\n }\n\n #eachCandidate(filter: Filter, visit: (event: Event) => void): void {\n if (filter.ids) {\n const seen = new Set<string>();\n for (const raw of filter.ids) {\n const event = this.#byId.get(raw.toLowerCase());\n if (!event || seen.has(event.id)) continue;\n seen.add(event.id);\n visit(event);\n }\n return;\n }\n\n if (filter.authors && filter.kinds) {\n const seen = new Set<string>();\n for (const pk of filter.authors) {\n const pubkey = pk.toLowerCase();\n for (const kind of filter.kinds) {\n const ids = this.#byKindPubkey.get(`${kind}:${pubkey}`);\n if (!ids) continue;\n for (const id of ids) {\n if (seen.has(id)) continue;\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) visit(event);\n }\n }\n }\n return;\n }\n\n if (filter.authors) {\n const seen = new Set<string>();\n for (const pk of filter.authors) {\n const byPk = this.#byPubkey.get(pk.toLowerCase());\n if (!byPk) continue;\n for (const id of byPk) {\n if (seen.has(id)) continue;\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) visit(event);\n }\n }\n return;\n }\n\n if (filter.kinds) {\n const seen = new Set<string>();\n for (const kind of filter.kinds) {\n const byKind = this.#byKind.get(kind);\n if (!byKind) continue;\n for (const id of byKind) {\n if (seen.has(id)) continue;\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) visit(event);\n }\n }\n return;\n }\n\n const eTags = filter[\"#e\"];\n const pTags = filter[\"#p\"];\n if (eTags !== undefined || pTags !== undefined) {\n const seen = new Set<string>();\n visitEpTagIds(this.#byEpTag, this.#byId, \"e\", eTags, seen, visit);\n visitEpTagIds(this.#byEpTag, this.#byId, \"p\", pTags, seen, visit);\n return;\n }\n\n // #t/#d and other non-e/p tags are not indexed (e/p only). A generic tag\n // store is extra put/remove amp; hashtag-only queries scan #byId.\n for (const event of this.#byId.values()) visit(event);\n }\n}\n\nfunction visitEpTagIds(\n byEpTag: Map<string, Set<string>>,\n byId: Map<string, Event>,\n name: \"e\" | \"p\",\n values: readonly string[] | undefined,\n seen: Set<string>,\n visit: (event: Event) => void,\n): void {\n if (values === undefined) return;\n for (const value of values) {\n const ids = byEpTag.get(`${name}:${value.toLowerCase()}`);\n if (!ids) continue;\n for (const id of ids) {\n if (seen.has(id)) continue;\n seen.add(id);\n const event = byId.get(id);\n if (event) visit(event);\n }\n }\n}\n\nfunction addToSet<K>(map: Map<K, Set<string>>, key: K, id: string): void {\n let set = map.get(key);\n if (!set) {\n set = new Set();\n map.set(key, set);\n }\n set.add(id);\n}\n\nfunction removeFromSet<K>(map: Map<K, Set<string>>, key: K, id: string): void {\n const set = map.get(key);\n if (!set) return;\n set.delete(id);\n if (set.size === 0) map.delete(key);\n}\n\nfunction queryItemOrder(a: NegentropyItem, b: NegentropyItem): number {\n if (a.created_at !== b.created_at) return b.created_at - a.created_at;\n return a.id.localeCompare(b.id);\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message-C_Uj-Y-c.mjs","names":[],"sources":["../src/core/message.ts"],"sourcesContent":["import { MessageError } from \"./error.ts\";\nimport type { Event } from \"./event.ts\";\nimport { validateSignedEvent } from \"./event.ts\";\nimport type { Filter } from \"./filter.ts\";\nimport { SUBSCRIPTION_ID_MAX_CHARS } from \"./limits.ts\";\nimport { bytesToHex, hexToBytes } from \"./util.ts\";\n\nexport type SubscriptionId = string;\n\nexport function assertSubscriptionId(id: string): SubscriptionId {\n if (id.length === 0 || id.length > SUBSCRIPTION_ID_MAX_CHARS) {\n throw new MessageError(`subscription id length must be 1..${SUBSCRIPTION_ID_MAX_CHARS}`);\n }\n return id;\n}\n\nexport function createSubscriptionId(id?: string): SubscriptionId {\n if (id !== undefined) return assertSubscriptionId(id);\n const bytes = new Uint8Array(8);\n crypto.getRandomValues(bytes);\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** Client → relay messages (NIP-01 + NIP-42 AUTH + NIP-45 COUNT + NIP-77). */\nexport type ClientMessage =\n | [\"EVENT\", Event]\n | [\"REQ\", SubscriptionId, ...Filter[]]\n | [\"CLOSE\", SubscriptionId]\n | [\"AUTH\", Event]\n | [\"COUNT\", SubscriptionId, ...Filter[]]\n | [\"NEG-OPEN\", SubscriptionId, Filter, string]\n | [\"NEG-MSG\", SubscriptionId, string]\n | [\"NEG-CLOSE\", SubscriptionId];\n\n/** Relay → client messages. */\nexport type RelayMessage =\n | [\"EVENT\", SubscriptionId, Event]\n | [\"OK\", string, boolean, string]\n | [\"EOSE\", SubscriptionId]\n | [\"CLOSED\", SubscriptionId, string]\n | [\"NOTICE\", string]\n | [\"AUTH\", string]\n | [\"COUNT\", SubscriptionId, CountResult]\n | [\"NEG-MSG\", SubscriptionId, string]\n | [\"NEG-ERR\", SubscriptionId, string];\n\n/** NIP-45 COUNT reply payload. */\nexport type CountResult = {\n count: number;\n approximate?: boolean;\n /**\n * Optional 512-char hex HyperLogLog sketch (256 registers).\n * Merge sketches with `mergeCountHll`. Estimation is unspecified by NIP-45 and not provided.\n */\n hll?: string;\n};\n\nconst HLL_BYTES = 256; // 512 hex chars (NIP-45; hex only, not base64)\nconst HLL_HEX_LEN = HLL_BYTES * 2;\nconst HEX_RE = /^[0-9a-fA-F]+$/;\n\n/**\n * Register-wise max of NIP-45 HyperLogLog sketches.\n * Output is always lowercase 512 hex. Empty input is the zero sketch (identity).\n */\nexport function mergeCountHll(hexes: readonly string[]): string {\n const merged = new Uint8Array(HLL_BYTES);\n for (const hex of hexes) {\n if (hex.length !== HLL_HEX_LEN || !HEX_RE.test(hex)) {\n throw new MessageError(\"invalid NIP-45 HLL sketch: expected 512-char hex\");\n }\n const bytes = hexToBytes(hex);\n for (let i = 0; i < HLL_BYTES; i++) {\n const b = bytes[i]!;\n if (b > merged[i]!) merged[i] = b;\n }\n }\n return bytesToHex(merged);\n}\n\nexport function encodeClientMessage(message: ClientMessage): string {\n return JSON.stringify(message);\n}\n\nexport function encodeRelayMessage(message: RelayMessage): string {\n return JSON.stringify(message);\n}\n\nexport function parseClientMessage(raw: string): ClientMessage {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new MessageError(\"client message is not valid JSON\");\n }\n if (!Array.isArray(data) || data.length < 1 || typeof data[0] !== \"string\") {\n throw new MessageError(\"client message must be a non-empty JSON array\");\n }\n const type = data[0];\n switch (type) {\n case \"EVENT\": {\n if (data.length !== 2 || !validateSignedEvent(data[1])) {\n throw new MessageError(\"invalid EVENT client message\");\n }\n return [\"EVENT\", data[1]];\n }\n case \"REQ\": {\n if (data.length < 3 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid REQ client message\");\n }\n return [\"REQ\", data[1], ...(data.slice(2) as Filter[])];\n }\n case \"CLOSE\": {\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid CLOSE client message\");\n }\n return [\"CLOSE\", data[1]];\n }\n case \"AUTH\": {\n if (data.length !== 2 || !validateSignedEvent(data[1])) {\n throw new MessageError(\"invalid AUTH client message\");\n }\n return [\"AUTH\", data[1]];\n }\n case \"COUNT\": {\n if (data.length < 3 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid COUNT client message\");\n }\n return [\"COUNT\", data[1], ...(data.slice(2) as Filter[])];\n }\n case \"NEG-OPEN\": {\n if (data.length === 5) {\n throw new MessageError(\"obsolete 5-element NEG-OPEN; expected [NEG-OPEN, id, filter, hex]\");\n }\n if (\n data.length !== 4 ||\n typeof data[1] !== \"string\" ||\n !isFilterObject(data[2]) ||\n !isNegHex(data[3])\n ) {\n throw new MessageError(\"invalid NEG-OPEN client message\");\n }\n return [\"NEG-OPEN\", data[1], data[2], data[3].toLowerCase()];\n }\n case \"NEG-MSG\": {\n if (data.length !== 3 || typeof data[1] !== \"string\" || !isNegHex(data[2])) {\n throw new MessageError(\"invalid NEG-MSG client message\");\n }\n return [\"NEG-MSG\", data[1], data[2].toLowerCase()];\n }\n case \"NEG-CLOSE\": {\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid NEG-CLOSE client message\");\n }\n return [\"NEG-CLOSE\", data[1]];\n }\n default:\n throw new MessageError(`unknown client message type: ${type}`);\n }\n}\n\nexport function parseRelayMessage(raw: string): RelayMessage {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new MessageError(\"relay message is not valid JSON\");\n }\n if (!Array.isArray(data) || data.length < 1 || typeof data[0] !== \"string\") {\n throw new MessageError(\"relay message must be a non-empty JSON array\");\n }\n const type = data[0];\n switch (type) {\n case \"EVENT\": {\n if (data.length !== 3 || typeof data[1] !== \"string\" || !validateSignedEvent(data[2])) {\n throw new MessageError(\"invalid EVENT relay message\");\n }\n return [\"EVENT\", data[1], data[2]];\n }\n case \"OK\": {\n if (\n data.length !== 4 ||\n typeof data[1] !== \"string\" ||\n typeof data[2] !== \"boolean\" ||\n typeof data[3] !== \"string\"\n ) {\n throw new MessageError(\"invalid OK relay message\");\n }\n return [\"OK\", data[1], data[2], data[3]];\n }\n case \"EOSE\": {\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid EOSE relay message\");\n }\n return [\"EOSE\", data[1]];\n }\n case \"CLOSED\": {\n if (data.length !== 3 || typeof data[1] !== \"string\" || typeof data[2] !== \"string\") {\n throw new MessageError(\"invalid CLOSED relay message\");\n }\n return [\"CLOSED\", data[1], data[2]];\n }\n case \"NOTICE\": {\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid NOTICE relay message\");\n }\n return [\"NOTICE\", data[1]];\n }\n case \"AUTH\": {\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid AUTH relay message\");\n }\n return [\"AUTH\", data[1]];\n }\n case \"COUNT\": {\n if (\n data.length !== 3 ||\n typeof data[1] !== \"string\" ||\n typeof data[2] !== \"object\" ||\n data[2] === null ||\n typeof (data[2] as { count?: unknown }).count !== \"number\"\n ) {\n throw new MessageError(\"invalid COUNT relay message\");\n }\n const payload = data[2] as { count: number; approximate?: unknown; hll?: unknown };\n const result: CountResult = { count: payload.count };\n if (typeof payload.approximate === \"boolean\") result.approximate = payload.approximate;\n const hll = parseCountHll(payload.hll);\n if (hll !== undefined) result.hll = hll;\n return [\"COUNT\", data[1], result];\n }\n case \"NEG-MSG\": {\n if (data.length !== 3 || typeof data[1] !== \"string\" || !isNegHex(data[2])) {\n throw new MessageError(\"invalid NEG-MSG relay message\");\n }\n return [\"NEG-MSG\", data[1], data[2].toLowerCase()];\n }\n case \"NEG-ERR\": {\n if (\n (data.length !== 3 && data.length !== 4) ||\n typeof data[1] !== \"string\" ||\n typeof data[2] !== \"string\"\n ) {\n throw new MessageError(\"invalid NEG-ERR relay message\");\n }\n return [\"NEG-ERR\", data[1], data[2]];\n }\n default:\n throw new MessageError(`unknown relay message type: ${type}`);\n }\n}\n\nfunction parseCountHll(value: unknown): string | undefined {\n if (typeof value !== \"string\" || value.length !== HLL_HEX_LEN || !HEX_RE.test(value)) {\n return undefined;\n }\n return value.toLowerCase();\n}\n\nfunction isFilterObject(value: unknown): value is Filter {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isNegHex(value: unknown): value is string {\n return (\n typeof value === \"string\" && value.length > 0 && value.length % 2 === 0 && HEX_RE.test(value)\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
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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"}
|