@qntx/nostr 0.6.0 → 0.8.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.
Files changed (183) hide show
  1. package/dist/{abort-Ba8tDrOI.mjs → abort-DBq2STPj.mjs} +13 -6
  2. package/dist/abort-DBq2STPj.mjs.map +1 -0
  3. package/dist/{builder-sSflu6nB.d.mts → builder-CTwkB2zM.d.mts} +14 -15
  4. package/dist/{builder-9tjI6WQ3.mjs → builder-aNU2gGFP.mjs} +11 -17
  5. package/dist/builder-aNU2gGFP.mjs.map +1 -0
  6. package/dist/{client-CadO5fAX.mjs → client-Cj2lUaVh.mjs} +141 -353
  7. package/dist/client-Cj2lUaVh.mjs.map +1 -0
  8. package/dist/client.d.mts +2 -2
  9. package/dist/client.mjs +2 -2
  10. package/dist/collections-DtwMc1NU.mjs +33 -0
  11. package/dist/collections-DtwMc1NU.mjs.map +1 -0
  12. package/dist/core.d.mts +9 -8
  13. package/dist/core.mjs +7 -7
  14. package/dist/emitter-DkCY6NkU.mjs +32 -0
  15. package/dist/emitter-DkCY6NkU.mjs.map +1 -0
  16. package/dist/{error-BW7re1UN.d.mts → error-BD_0QFKT.d.mts} +2 -2
  17. package/dist/{error-DrEIM7FO.mjs → error-BILMe3Gr.mjs} +2 -2
  18. package/dist/{error-DrEIM7FO.mjs.map → error-BILMe3Gr.mjs.map} +1 -1
  19. package/dist/{error-CR8Urc84.d.mts → error-BquXDmIN.d.mts} +4 -4
  20. package/dist/{error-B__OMBRY.mjs → error-DlhGW4mJ.mjs} +2 -2
  21. package/dist/{error-B__OMBRY.mjs.map → error-DlhGW4mJ.mjs.map} +1 -1
  22. package/dist/{event-D8Nfoh4K.mjs → event-CHf47dPM.mjs} +16 -10
  23. package/dist/event-CHf47dPM.mjs.map +1 -0
  24. package/dist/{event-fU3Y01PR.d.mts → event-DJeb_DIL.d.mts} +4 -2
  25. package/dist/{filter-C0IMByE3.d.mts → filter-jI_9htF7.d.mts} +2 -2
  26. package/dist/{gossip-oloDMWEj.mjs → gossip-DFxIBlsr.mjs} +23 -33
  27. package/dist/gossip-DFxIBlsr.mjs.map +1 -0
  28. package/dist/{gossip-DpIvkBuj.d.mts → gossip-OOeeFzG5.d.mts} +37 -28
  29. package/dist/gossip.d.mts +1 -1
  30. package/dist/gossip.mjs +1 -1
  31. package/dist/{index-D1bqFHKB.d.mts → index-BxCBX-8R.d.mts} +18 -18
  32. package/dist/{index-lvXeER5t.d.mts → index-CPVEyzNX.d.mts} +32 -50
  33. package/dist/{index-MxC28S4P.d.mts → index-C_w2WfVz.d.mts} +27 -29
  34. package/dist/{index-CNl2q67H.d.mts → index-Cl_1XdYP.d.mts} +111 -180
  35. package/dist/{index-CvkvqB16.d.mts → index-DU9tRFTZ.d.mts} +7 -20
  36. package/dist/{index-q4MDce_r.d.mts → index-DyKwQnbt.d.mts} +6 -8
  37. package/dist/index.d.mts +23 -21
  38. package/dist/index.mjs +21 -19
  39. package/dist/{instance-DQE8BoWt.mjs → instance-C62w3cEt.mjs} +26 -27
  40. package/dist/instance-C62w3cEt.mjs.map +1 -0
  41. package/dist/{instance-CIca-ynn.d.mts → instance-DjsL8h84.d.mts} +5 -5
  42. package/dist/{key-Cv4_pN3x.d.mts → key-D_5OgGT7.d.mts} +11 -7
  43. package/dist/{key-Bps-LqX7.mjs → key-DnVpchl6.mjs} +13 -9
  44. package/dist/key-DnVpchl6.mjs.map +1 -0
  45. package/dist/{loaders-o6YXB_Ri.mjs → loaders-BmPkQfzI.mjs} +73 -82
  46. package/dist/loaders-BmPkQfzI.mjs.map +1 -0
  47. package/dist/loaders.d.mts +3 -3
  48. package/dist/loaders.mjs +2 -2
  49. package/dist/{memory-C2EVx0_O.mjs → memory-CdJ07kub.mjs} +2 -2
  50. package/dist/memory-CdJ07kub.mjs.map +1 -0
  51. package/dist/{memory-index-Do1mz0WF.mjs → memory-index-Cbc83vOZ.mjs} +30 -247
  52. package/dist/memory-index-Cbc83vOZ.mjs.map +1 -0
  53. package/dist/message-oNr7t0SJ.mjs +194 -0
  54. package/dist/message-oNr7t0SJ.mjs.map +1 -0
  55. package/dist/{message-iGPIqnn0.d.mts → message-tclYCVNc.d.mts} +6 -6
  56. package/dist/{nip17-CKIufV98.d.mts → nip17-B5WgW4C4.d.mts} +6 -7
  57. package/dist/{nip19-BevCJZsp.d.mts → nip19-DpPN17bY.d.mts} +25 -31
  58. package/dist/{nip46-DDV4dC56.d.mts → nip46-BHaxC9TZ.d.mts} +19 -21
  59. package/dist/nip51-CR0Q8eym.d.mts +87 -0
  60. package/dist/{nip59-zQgdLcEr.d.mts → nip59-C-RWFQIv.d.mts} +11 -5
  61. package/dist/nip65-D1fbmhOg.d.mts +23 -0
  62. package/dist/{nip77-DH4RhS5r.d.mts → nip77-DzUwUxHq.d.mts} +8 -9
  63. package/dist/nips/blossom.d.mts +17 -13
  64. package/dist/nips/blossom.mjs +30 -9
  65. package/dist/nips/blossom.mjs.map +1 -1
  66. package/dist/nips/nip04.mjs +17 -7
  67. package/dist/nips/nip04.mjs.map +1 -1
  68. package/dist/nips/nip05.d.mts +9 -10
  69. package/dist/nips/nip05.mjs +1 -4
  70. package/dist/nips/nip05.mjs.map +1 -1
  71. package/dist/nips/nip10.d.mts +8 -8
  72. package/dist/nips/nip10.mjs +34 -32
  73. package/dist/nips/nip10.mjs.map +1 -1
  74. package/dist/nips/nip11.d.mts +28 -28
  75. package/dist/nips/nip11.mjs +4 -4
  76. package/dist/nips/nip11.mjs.map +1 -1
  77. package/dist/nips/nip13.d.mts +7 -7
  78. package/dist/nips/nip13.mjs +32 -10
  79. package/dist/nips/nip13.mjs.map +1 -1
  80. package/dist/nips/nip17.d.mts +1 -1
  81. package/dist/nips/nip17.mjs +16 -34
  82. package/dist/nips/nip17.mjs.map +1 -1
  83. package/dist/nips/nip19.d.mts +2 -2
  84. package/dist/nips/nip19.mjs +16 -28
  85. package/dist/nips/nip19.mjs.map +1 -1
  86. package/dist/nips/nip21.d.mts +9 -4
  87. package/dist/nips/nip21.mjs +15 -5
  88. package/dist/nips/nip21.mjs.map +1 -1
  89. package/dist/nips/nip27.d.mts +22 -22
  90. package/dist/nips/nip27.mjs +2 -2
  91. package/dist/nips/nip27.mjs.map +1 -1
  92. package/dist/nips/nip42.d.mts +1 -1
  93. package/dist/nips/nip42.mjs +2 -1
  94. package/dist/nips/nip42.mjs.map +1 -1
  95. package/dist/nips/nip44.mjs +2 -2
  96. package/dist/nips/nip44.mjs.map +1 -1
  97. package/dist/nips/nip46.d.mts +2 -2
  98. package/dist/nips/nip46.mjs +15 -19
  99. package/dist/nips/nip46.mjs.map +1 -1
  100. package/dist/nips/nip49.d.mts +9 -9
  101. package/dist/nips/nip49.mjs +4 -4
  102. package/dist/nips/nip49.mjs.map +1 -1
  103. package/dist/nips/nip51.d.mts +2 -86
  104. package/dist/nips/nip51.mjs +7 -23
  105. package/dist/nips/nip51.mjs.map +1 -1
  106. package/dist/nips/nip57.d.mts +25 -23
  107. package/dist/nips/nip57.mjs +12 -17
  108. package/dist/nips/nip57.mjs.map +1 -1
  109. package/dist/nips/nip59.d.mts +1 -1
  110. package/dist/nips/nip59.mjs +27 -19
  111. package/dist/nips/nip59.mjs.map +1 -1
  112. package/dist/nips/nip65.d.mts +2 -2
  113. package/dist/nips/nip65.mjs +14 -37
  114. package/dist/nips/nip65.mjs.map +1 -1
  115. package/dist/nips/nip77.d.mts +1 -1
  116. package/dist/nips/nip77.mjs +33 -32
  117. package/dist/nips/nip77.mjs.map +1 -1
  118. package/dist/nips/nip98.d.mts +5 -5
  119. package/dist/nips/nip98.mjs +8 -8
  120. package/dist/nips/nip98.mjs.map +1 -1
  121. package/dist/{pool-DBIYMtwy.d.mts → pool-CqQvUnsq.d.mts} +132 -108
  122. package/dist/pool-vlw74C4t.mjs +606 -0
  123. package/dist/pool-vlw74C4t.mjs.map +1 -0
  124. package/dist/put-w13ci3sC.mjs +187 -0
  125. package/dist/put-w13ci3sC.mjs.map +1 -0
  126. package/dist/{reactive-BfhPp9a3.mjs → reactive-Tz0otDIP.mjs} +125 -88
  127. package/dist/reactive-Tz0otDIP.mjs.map +1 -0
  128. package/dist/{reactive-7aqeip4A.d.mts → reactive-gGqUrzW8.d.mts} +20 -26
  129. package/dist/relay-quCs7MHD.mjs +1811 -0
  130. package/dist/relay-quCs7MHD.mjs.map +1 -0
  131. package/dist/relay.d.mts +4 -4
  132. package/dist/relay.mjs +3 -2
  133. package/dist/serial-Bzpw-xz3.mjs +21 -0
  134. package/dist/serial-Bzpw-xz3.mjs.map +1 -0
  135. package/dist/{signer-CYQutDzD.mjs → signer-poTGe6Er.mjs} +12 -14
  136. package/dist/signer-poTGe6Er.mjs.map +1 -0
  137. package/dist/signer.d.mts +2 -2
  138. package/dist/signer.mjs +2 -2
  139. package/dist/{storage-Dky3wuoH.mjs → storage-B5IXTVuS.mjs} +28 -51
  140. package/dist/storage-B5IXTVuS.mjs.map +1 -0
  141. package/dist/storage.d.mts +3 -3
  142. package/dist/storage.mjs +4 -4
  143. package/dist/store.d.mts +2 -2
  144. package/dist/store.mjs +1 -1
  145. package/dist/testing.d.mts +19 -19
  146. package/dist/testing.mjs +9 -9
  147. package/dist/testing.mjs.map +1 -1
  148. package/dist/{types-2pCUprRU.d.mts → types-BacwnDtU.d.mts} +10 -10
  149. package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
  150. package/dist/{util-LfQirgDk.mjs → util-DMF9AIO0.mjs} +33 -7
  151. package/dist/util-DMF9AIO0.mjs.map +1 -0
  152. package/dist/{verifier-t5WoWbtY.mjs → verifier-B51Wkt5H.mjs} +3 -3
  153. package/dist/verifier-B51Wkt5H.mjs.map +1 -0
  154. package/dist/verifier-CgTFpgsC.d.mts +22 -0
  155. package/dist/wasm.browser.d.mts +3 -3
  156. package/dist/wasm.browser.mjs +3 -3
  157. package/dist/wasm.d.mts +3 -3
  158. package/dist/wasm.mjs +3 -3
  159. package/dist/wasm.mjs.map +1 -1
  160. package/package.json +5 -9
  161. package/dist/abort-Ba8tDrOI.mjs.map +0 -1
  162. package/dist/builder-9tjI6WQ3.mjs.map +0 -1
  163. package/dist/client-CadO5fAX.mjs.map +0 -1
  164. package/dist/event-D8Nfoh4K.mjs.map +0 -1
  165. package/dist/gossip-oloDMWEj.mjs.map +0 -1
  166. package/dist/instance-DQE8BoWt.mjs.map +0 -1
  167. package/dist/key-Bps-LqX7.mjs.map +0 -1
  168. package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
  169. package/dist/memory-C2EVx0_O.mjs.map +0 -1
  170. package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
  171. package/dist/message-DcY6Fi9o.mjs +0 -193
  172. package/dist/message-DcY6Fi9o.mjs.map +0 -1
  173. package/dist/nip65-CjgmdpPB.d.mts +0 -26
  174. package/dist/nips/nip96.d.mts +0 -40
  175. package/dist/nips/nip96.mjs +0 -118
  176. package/dist/nips/nip96.mjs.map +0 -1
  177. package/dist/pool-As20OGxB.mjs +0 -2166
  178. package/dist/pool-As20OGxB.mjs.map +0 -1
  179. package/dist/reactive-BfhPp9a3.mjs.map +0 -1
  180. package/dist/signer-CYQutDzD.mjs.map +0 -1
  181. package/dist/storage-Dky3wuoH.mjs.map +0 -1
  182. package/dist/util-LfQirgDk.mjs.map +0 -1
  183. package/dist/verifier-t5WoWbtY.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"nip98.mjs","names":[],"sources":["../../src/nips/nip98.ts"],"sourcesContent":["/**\n * NIP-98: HTTP Auth (kind 27235). Token is standard base64 of the signed event JSON, not base64url.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/98.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { NostrError } from \"../core/error.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { validateSignedEvent } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { bytesToHex, utf8Decoder, utf8Encoder } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\n\nconst AUTHORIZATION_SCHEME = \"Nostr \";\nconst DEFAULT_MAX_SKEW_SEC = 60;\n\nexport class Nip98Error extends NostrError {\n override name = \"Nip98Error\";\n}\n\n/** SHA-256 of the request body: raw bytes for string/Uint8Array, else JSON.stringify. */\nfunction hashPayload(payload: unknown): string {\n const bytes =\n typeof payload === \"string\"\n ? utf8Encoder.encode(payload)\n : payload instanceof Uint8Array\n ? payload\n : utf8Encoder.encode(JSON.stringify(payload));\n return bytesToHex(sha256(bytes));\n}\n\n/** RFC 4648 standard base64; accept missing `=` (NIP-98 spec example is unpadded). */\nfunction decodeStandardBase64(encoded: string): Uint8Array {\n const rem = encoded.length % 4;\n const padded = rem === 0 ? encoded : encoded + \"=\".repeat(4 - rem);\n return base64.decode(padded);\n}\n\nfunction stripAuthorizationScheme(token: string): string {\n const prefix = /^nostr\\s+/i.exec(token);\n return prefix ? token.slice(prefix[0].length) : token;\n}\n\n/**\n * Sign a kind 27235 auth event and encode it as a NIP-98 token. Uses standard base64\n * (`@scure/base`), not base64url.\n */\nexport async function getToken(\n url: string,\n method: string,\n sign: (template: EventTemplate) => Promise<Event> | Event,\n opts?: {\n includeAuthorizationScheme?: boolean;\n content?: string;\n payload?: unknown;\n now?: number;\n },\n): Promise<string> {\n const tags: Tag[] = [\n [\"u\", url],\n [\"method\", method],\n ];\n if (opts?.payload !== undefined) {\n tags.push([\"payload\", hashPayload(opts.payload)]);\n }\n\n const signed = await sign({\n kind: Kind.HttpAuth,\n created_at: opts?.now ?? Math.floor(Date.now() / 1000),\n tags,\n content: opts?.content ?? \"\",\n });\n\n const encoded = base64.encode(utf8Encoder.encode(JSON.stringify(signed)));\n return opts?.includeAuthorizationScheme === true ? AUTHORIZATION_SCHEME + encoded : encoded;\n}\n\n/** Decode a NIP-98 token (with or without the `Nostr ` scheme) into an event. */\nexport function unpackEventFromToken(token: string): Event {\n if (token === \"\") {\n throw new Nip98Error(\"missing token\");\n }\n\n const encoded = stripAuthorizationScheme(token);\n\n let json: string;\n try {\n json = utf8Decoder.decode(decodeStandardBase64(encoded));\n } catch (error) {\n throw new Nip98Error(\"invalid token encoding\", { cause: error });\n }\n if (!json.startsWith(\"{\")) {\n throw new Nip98Error(\"invalid token\");\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (error) {\n throw new Nip98Error(\"invalid token JSON\", { cause: error });\n }\n if (!validateSignedEvent(parsed)) {\n throw new Nip98Error(\"token is not a signed event\");\n }\n return parsed;\n}\n\n/**\n * True when `event` is a valid NIP-98 auth event for `url` + `method`. Verifies signature, kind\n * 27235, timestamp window, `u`, `method`, and optional `payload` tag.\n */\nexport function validateAuthEvent(\n event: Event,\n url: string,\n method: string,\n opts?: { payload?: unknown; maxSkewSec?: number },\n): boolean {\n if (!verifyEvent(event)) {\n return false;\n }\n if (event.kind !== Kind.HttpAuth) {\n return false;\n }\n\n const maxSkewSec = opts?.maxSkewSec ?? DEFAULT_MAX_SKEW_SEC;\n const now = Math.floor(Date.now() / 1000);\n if (Math.abs(now - event.created_at) > maxSkewSec) {\n return false;\n }\n\n const u = event.tags.find((t) => t[0] === \"u\")?.[1];\n if (u !== url) {\n return false;\n }\n\n const m = event.tags.find((t) => t[0] === \"method\")?.[1];\n if (m === undefined || m.toLowerCase() !== method.toLowerCase()) {\n return false;\n }\n\n if (opts?.payload !== undefined) {\n const payloadTag = event.tags.find((t) => t[0] === \"payload\")?.[1];\n if (payloadTag !== hashPayload(opts.payload)) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAE7B,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;AAGA,SAAS,YAAY,SAA0B;CAC7C,MAAM,QACJ,OAAO,YAAY,WACf,YAAY,OAAO,OAAO,IAC1B,mBAAmB,aACjB,UACA,YAAY,OAAO,KAAK,UAAU,OAAO,CAAC;CAClD,OAAO,WAAW,OAAO,KAAK,CAAC;AACjC;;AAGA,SAAS,qBAAqB,SAA6B;CACzD,MAAM,MAAM,QAAQ,SAAS;CAC7B,MAAM,SAAS,QAAQ,IAAI,UAAU,UAAU,IAAI,OAAO,IAAI,GAAG;CACjE,OAAO,OAAO,OAAO,MAAM;AAC7B;AAEA,SAAS,yBAAyB,OAAuB;CACvD,MAAM,SAAS,aAAa,KAAK,KAAK;CACtC,OAAO,SAAS,MAAM,MAAM,OAAO,EAAE,CAAC,MAAM,IAAI;AAClD;;;;;AAMA,eAAsB,SACpB,KACA,QACA,MACA,MAMiB;CACjB,MAAM,OAAc,CAClB,CAAC,KAAK,GAAG,GACT,CAAC,UAAU,MAAM,CACnB;CACA,IAAI,MAAM,YAAY,KAAA,GACpB,KAAK,KAAK,CAAC,WAAW,YAAY,KAAK,OAAO,CAAC,CAAC;CAGlD,MAAM,SAAS,MAAM,KAAK;EACxB,MAAM,KAAK;EACX,YAAY,MAAM,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACrD;EACA,SAAS,MAAM,WAAW;CAC5B,CAAC;CAED,MAAM,UAAU,OAAO,OAAO,YAAY,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC;CACxE,OAAO,MAAM,+BAA+B,OAAO,uBAAuB,UAAU;AACtF;;AAGA,SAAgB,qBAAqB,OAAsB;CACzD,IAAI,UAAU,IACZ,MAAM,IAAI,WAAW,eAAe;CAGtC,MAAM,UAAU,yBAAyB,KAAK;CAE9C,IAAI;CACJ,IAAI;EACF,OAAO,YAAY,OAAO,qBAAqB,OAAO,CAAC;CACzD,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,0BAA0B,EAAE,OAAO,MAAM,CAAC;CACjE;CACA,IAAI,CAAC,KAAK,WAAW,GAAG,GACtB,MAAM,IAAI,WAAW,eAAe;CAGtC,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,sBAAsB,EAAE,OAAO,MAAM,CAAC;CAC7D;CACA,IAAI,CAAC,oBAAoB,MAAM,GAC7B,MAAM,IAAI,WAAW,6BAA6B;CAEpD,OAAO;AACT;;;;;AAMA,SAAgB,kBACd,OACA,KACA,QACA,MACS;CACT,IAAI,CAAC,YAAY,KAAK,GACpB,OAAO;CAET,IAAI,MAAM,SAAS,KAAK,UACtB,OAAO;CAGT,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,IAAI,KAAK,IAAI,MAAM,MAAM,UAAU,IAAI,YACrC,OAAO;CAIT,IADU,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,GAAG,CAAC,GAAG,OACvC,KACR,OAAO;CAGT,MAAM,IAAI,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,QAAQ,CAAC,GAAG;CACtD,IAAI,MAAM,KAAA,KAAa,EAAE,YAAY,MAAM,OAAO,YAAY,GAC5D,OAAO;CAGT,IAAI,MAAM,YAAY,KAAA,GACD;MAAA,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,SAAS,CAAC,GAAG,OAC7C,YAAY,KAAK,OAAO,GACzC,OAAO;CAAA;CAIX,OAAO;AACT"}
1
+ {"version":3,"file":"nip98.mjs","names":[],"sources":["../../src/nips/nip98.ts"],"sourcesContent":["/**\n * NIP-98: HTTP Auth (kind 27235). Token is standard base64 of the signed event JSON, not base64url.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/98.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { NostrError } from \"../core/error.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { validateSignedEvent } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { firstTagValue } from \"../core/tag.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { bytesToHex, nowSeconds, utf8Decoder, utf8Encoder } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\n\nconst AUTHORIZATION_SCHEME = \"Nostr \";\nconst DEFAULT_MAX_SKEW_SEC = 60;\n\nexport class Nip98Error extends NostrError {\n override name = \"Nip98Error\";\n}\n\n/** SHA-256 of the request body: raw bytes for string/Uint8Array, else JSON.stringify. */\nfunction hashPayload(payload: unknown): string {\n const bytes =\n typeof payload === \"string\"\n ? utf8Encoder.encode(payload)\n : payload instanceof Uint8Array\n ? payload\n : utf8Encoder.encode(JSON.stringify(payload));\n return bytesToHex(sha256(bytes));\n}\n\n/** RFC 4648 standard base64; accept missing `=` (NIP-98 spec example is unpadded). */\nfunction decodeStandardBase64(encoded: string): Uint8Array {\n const rem = encoded.length % 4;\n const padded = rem === 0 ? encoded : encoded + \"=\".repeat(4 - rem);\n return base64.decode(padded);\n}\n\nfunction stripAuthorizationScheme(token: string): string {\n const prefix = /^nostr\\s+/i.exec(token);\n return prefix ? token.slice(prefix[0].length) : token;\n}\n\n/**\n * Sign a kind 27235 auth event and encode it as a NIP-98 token. Uses standard base64\n * (`@scure/base`), not base64url.\n */\nexport async function getToken(\n url: string,\n method: string,\n sign: (template: EventTemplate) => Promise<Event> | Event,\n opts?: {\n includeAuthorizationScheme?: boolean | undefined;\n content?: string | undefined;\n payload?: unknown;\n now?: number | undefined;\n },\n): Promise<string> {\n const tags: Tag[] = [\n [\"u\", url],\n [\"method\", method],\n ];\n if (opts?.payload !== undefined) {\n tags.push([\"payload\", hashPayload(opts.payload)]);\n }\n\n const signed = await sign({\n kind: Kind.HttpAuth,\n created_at: opts?.now ?? nowSeconds(),\n tags,\n content: opts?.content ?? \"\",\n });\n\n const encoded = base64.encode(utf8Encoder.encode(JSON.stringify(signed)));\n return opts?.includeAuthorizationScheme === true ? AUTHORIZATION_SCHEME + encoded : encoded;\n}\n\n/** Decode a NIP-98 token (with or without the `Nostr ` scheme) into an event. */\nexport function unpackEventFromToken(token: string): Event {\n if (token === \"\") {\n throw new Nip98Error(\"missing token\");\n }\n\n const encoded = stripAuthorizationScheme(token);\n\n let json: string;\n try {\n json = utf8Decoder.decode(decodeStandardBase64(encoded));\n } catch (error) {\n throw new Nip98Error(\"invalid token encoding\", { cause: error });\n }\n if (!json.startsWith(\"{\")) {\n throw new Nip98Error(\"invalid token\");\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (error) {\n throw new Nip98Error(\"invalid token JSON\", { cause: error });\n }\n if (!validateSignedEvent(parsed)) {\n throw new Nip98Error(\"token is not a signed event\");\n }\n return parsed;\n}\n\n/**\n * True when `event` is a valid NIP-98 auth event for `url` + `method`. Verifies signature, kind\n * 27235, timestamp window, `u`, `method`, and optional `payload` tag.\n */\nexport function validateAuthEvent(\n event: Event,\n url: string,\n method: string,\n opts?: { payload?: unknown; maxSkewSec?: number },\n): boolean {\n if (!verifyEvent(event)) {\n return false;\n }\n if (event.kind !== Kind.HttpAuth) {\n return false;\n }\n\n const maxSkewSec = opts?.maxSkewSec ?? DEFAULT_MAX_SKEW_SEC;\n const now = nowSeconds();\n if (Math.abs(now - event.created_at) > maxSkewSec) {\n return false;\n }\n\n const u = firstTagValue(event.tags, \"u\");\n if (u !== url) {\n return false;\n }\n\n const m = firstTagValue(event.tags, \"method\");\n if (m === undefined || m.toLowerCase() !== method.toLowerCase()) {\n return false;\n }\n\n if (opts?.payload !== undefined) {\n const payloadTag = firstTagValue(event.tags, \"payload\");\n if (payloadTag !== hashPayload(opts.payload)) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAE7B,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;AAGA,SAAS,YAAY,SAA0B;CAC7C,MAAM,QACJ,OAAO,YAAY,WACf,YAAY,OAAO,OAAO,IAC1B,mBAAmB,aACjB,UACA,YAAY,OAAO,KAAK,UAAU,OAAO,CAAC;CAClD,OAAO,WAAW,OAAO,KAAK,CAAC;AACjC;;AAGA,SAAS,qBAAqB,SAA6B;CACzD,MAAM,MAAM,QAAQ,SAAS;CAC7B,MAAM,SAAS,QAAQ,IAAI,UAAU,UAAU,IAAI,OAAO,IAAI,GAAG;CACjE,OAAO,OAAO,OAAO,MAAM;AAC7B;AAEA,SAAS,yBAAyB,OAAuB;CACvD,MAAM,SAAS,aAAa,KAAK,KAAK;CACtC,OAAO,SAAS,MAAM,MAAM,OAAO,EAAE,CAAC,MAAM,IAAI;AAClD;;;;;AAMA,eAAsB,SACpB,KACA,QACA,MACA,MAMiB;CACjB,MAAM,OAAc,CAClB,CAAC,KAAK,GAAG,GACT,CAAC,UAAU,MAAM,CACnB;CACA,IAAI,MAAM,YAAY,KAAA,GACpB,KAAK,KAAK,CAAC,WAAW,YAAY,KAAK,OAAO,CAAC,CAAC;CAGlD,MAAM,SAAS,MAAM,KAAK;EACxB,MAAM,KAAK;EACX,YAAY,MAAM,OAAO,WAAW;EACpC;EACA,SAAS,MAAM,WAAW;CAC5B,CAAC;CAED,MAAM,UAAU,OAAO,OAAO,YAAY,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC;CACxE,OAAO,MAAM,+BAA+B,OAAO,uBAAuB,UAAU;AACtF;;AAGA,SAAgB,qBAAqB,OAAsB;CACzD,IAAI,UAAU,IACZ,MAAM,IAAI,WAAW,eAAe;CAGtC,MAAM,UAAU,yBAAyB,KAAK;CAE9C,IAAI;CACJ,IAAI;EACF,OAAO,YAAY,OAAO,qBAAqB,OAAO,CAAC;CACzD,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,0BAA0B,EAAE,OAAO,MAAM,CAAC;CACjE;CACA,IAAI,CAAC,KAAK,WAAW,GAAG,GACtB,MAAM,IAAI,WAAW,eAAe;CAGtC,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,sBAAsB,EAAE,OAAO,MAAM,CAAC;CAC7D;CACA,IAAI,CAAC,oBAAoB,MAAM,GAC7B,MAAM,IAAI,WAAW,6BAA6B;CAEpD,OAAO;AACT;;;;;AAMA,SAAgB,kBACd,OACA,KACA,QACA,MACS;CACT,IAAI,CAAC,YAAY,KAAK,GACpB,OAAO;CAET,IAAI,MAAM,SAAS,KAAK,UACtB,OAAO;CAGT,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,MAAM,WAAW;CACvB,IAAI,KAAK,IAAI,MAAM,MAAM,UAAU,IAAI,YACrC,OAAO;CAIT,IADU,cAAc,MAAM,MAAM,GAChC,MAAM,KACR,OAAO;CAGT,MAAM,IAAI,cAAc,MAAM,MAAM,QAAQ;CAC5C,IAAI,MAAM,KAAA,KAAa,EAAE,YAAY,MAAM,OAAO,YAAY,GAC5D,OAAO;CAGT,IAAI,MAAM,YAAY,KAAA,GACD;MAAA,cAAc,MAAM,MAAM,SAChC,MAAM,YAAY,KAAK,OAAO,GACzC,OAAO;CAAA;CAIX,OAAO;AACT"}
@@ -1,7 +1,8 @@
1
- import { n as EventTemplate, t as Event } from "./event-fU3Y01PR.mjs";
2
- import { t as Filter } from "./filter-C0IMByE3.mjs";
3
- import { n as CountResult } from "./message-iGPIqnn0.mjs";
4
- import { a as NegentropyStorageVector } from "./nip77-DH4RhS5r.mjs";
1
+ import { n as EventTemplate, t as Event } from "./event-DJeb_DIL.mjs";
2
+ import { t as Filter } from "./filter-jI_9htF7.mjs";
3
+ import { n as CountResult } from "./message-tclYCVNc.mjs";
4
+ import { t as EventVerifier } from "./verifier-CgTFpgsC.mjs";
5
+ import { a as NegentropyStorageVector } from "./nip77-DzUwUxHq.mjs";
5
6
  import { t as WebSocketConstructor } from "./websocket-CmeqvAkp.mjs";
6
7
  //#region src/relay/subscription.d.ts
7
8
  type SubscriptionHandlers = {
@@ -20,8 +21,9 @@ type SubscriptionHandlers = {
20
21
  type SubscribeOptions = SubscriptionHandlers & {
21
22
  id?: string | undefined;
22
23
  /**
23
- * If set, fire `oneose` once after this many ms if EOSE has not arrived. Does not close the REQ.
24
- * `Relay.fetch` is the one-shot closer.
24
+ * If set, fire `oneose` once after this many ms if EOSE has not arrived. The synthesized EOSE
25
+ * also closes a `closeOnEose` subscription — without it a one-shot REQ would hang until a real
26
+ * EOSE that is then ignored.
25
27
  */
26
28
  eoseTimeoutMs?: number | undefined;
27
29
  /**
@@ -31,41 +33,23 @@ type SubscribeOptions = SubscriptionHandlers & {
31
33
  closeOnEose?: boolean | undefined;
32
34
  signal?: AbortSignal | undefined;
33
35
  };
34
- declare class Subscription {
35
- #private;
36
+ /** Uniform close handle returned by subscriptions, fan-ins, and feeds. */
37
+ type Closer = {
38
+ close: (reason?: string) => void;
39
+ };
40
+ /** Public subscription handle returned by {@link Relay.subscribe}. */
41
+ type RelaySubscription = {
36
42
  readonly id: string;
37
- readonly filters: Filter[];
38
- readonly handlers: SubscriptionHandlers;
39
- readonly closeOnEose: boolean;
40
- eosed: boolean;
41
- closed: boolean;
42
- /** True after one CLOSED `auth-required:` retry. */
43
- authRetried: boolean;
44
- /** Inclusive NIP-01 `since` watermark from verified EVENTs. */
45
- lastCreatedAt: number | undefined;
46
- /** Event ids at `lastCreatedAt` (same-second reconnect dedup). Not all seen ids. */
47
- readonly idsAtWatermark: Set<string>;
48
- constructor(filters: Filter[], opts: SubscribeOptions, sendClose: (id: string) => void);
49
- close(reason?: string): void;
50
- /** Advance the reconnect watermark after a verified EVENT. */
51
- noteVerified(event: Event): void;
52
- /**
53
- * Filters for re-REQ. Original `filters` stay unchanged. NIP-01 `since` is inclusive — never
54
- * `lastCreatedAt + 1`.
55
- */
56
- replayFilters(): Filter[];
57
- }
58
- /** AsyncIterable wrapper over a subscription's events until EOSE or close. */
59
- declare function subscriptionToAsyncIterable(start: (handlers: SubscriptionHandlers) => {
43
+ readonly closed: boolean;
60
44
  close: (reason?: string) => void;
61
- }, opts?: {
45
+ };
46
+ /** AsyncIterable wrapper over a subscription's events until EOSE or close. */
47
+ declare function subscriptionToAsyncIterable(start: (handlers: SubscriptionHandlers) => Closer, opts?: {
62
48
  signal?: AbortSignal | undefined;
63
49
  includeEose?: boolean | undefined;
64
- }): AsyncIterable<Event> & {
65
- close: (reason?: string) => void;
66
- };
50
+ }): AsyncIterable<Event> & Closer;
67
51
  //#endregion
68
- //#region src/relay/subscribe.d.ts
52
+ //#region src/relay/fetch.d.ts
69
53
  /** How a one-shot {@link Relay.fetch} REQ ended. */
70
54
  type RelayFetchEnd = {
71
55
  readonly type: "eose";
@@ -77,12 +61,29 @@ type RelayFetchEnd = {
77
61
  };
78
62
  /** `Relay.fetch` result: collected events plus the REQ's end reason. */
79
63
  type RelayFetchResult = {
80
- readonly events: Event[];
64
+ readonly events: ReadonlyArray<Event>;
81
65
  readonly end: RelayFetchEnd;
82
66
  };
83
67
  //#endregion
68
+ //#region src/relay/ok-tracker.d.ts
69
+ /** A relay's NIP-01 OK reply to a published event. */
70
+ type PublishResult = {
71
+ readonly ok: boolean;
72
+ readonly message: string;
73
+ };
74
+ //#endregion
84
75
  //#region src/relay/relay.d.ts
85
- /** Relay lifecycle states. */
76
+ /**
77
+ * Relay lifecycle states:
78
+ *
79
+ * - `initialized` — constructed; `connect()` not yet attempted.
80
+ * - `connecting` — a `connect()` attempt is in flight (socket opening / resubscribing).
81
+ * - `connected` — socket open; REQ/EVENT/COUNT flow.
82
+ * - `disconnected` — socket down: a reconnect is scheduled when `enableReconnect` has live
83
+ * subscriptions to restore, or `disconnect()` severed manually (REQ state kept).
84
+ * - `closed` — terminal until the next `connect()`: `close()` or a socket death without a scheduled
85
+ * reconnect.
86
+ */
86
87
  declare const RelayStatus: {
87
88
  readonly Initialized: "initialized";
88
89
  readonly Connecting: "connecting";
@@ -95,7 +96,7 @@ type RelayStatusName = (typeof RelayStatus)[keyof typeof RelayStatus];
95
96
  /** Per-relay options: socket override, verification, timeouts, reconnect, ping, NIP-42 auth. */
96
97
  type RelayOptions = {
97
98
  websocketImplementation?: WebSocketConstructor | undefined;
98
- verifyEvent?: ((event: Event) => boolean) | undefined;
99
+ verifyEvent?: EventVerifier | undefined;
99
100
  publishTimeoutMs?: number | undefined;
100
101
  connectTimeoutMs?: number | undefined;
101
102
  /**
@@ -104,17 +105,28 @@ type RelayOptions = {
104
105
  */
105
106
  enableReconnect?: boolean | undefined;
106
107
  /** Backoff delays in ms between reconnect attempts. */
107
- reconnectBackoffMs?: number[] | undefined;
108
+ reconnectBackoffMs?: ReadonlyArray<number> | undefined;
108
109
  enablePing?: boolean | undefined;
109
110
  pingIntervalMs?: number | undefined;
110
111
  pingTimeoutMs?: number | undefined;
111
112
  /** When set, CLOSED/OK `auth-required:` triggers AUTH then retries the REQ/EVENT/COUNT. */
112
113
  authSigner?: ((template: EventTemplate) => Promise<Event>) | undefined;
113
114
  };
114
- /** A relay's NIP-01 OK reply to a published event. */
115
- type PublishResult = {
116
- ok: boolean;
117
- message: string;
115
+ /** Typed event payloads for {@link Relay.on}: lifecycle and protocol notifications. */
116
+ type RelayEventMap = {
117
+ /** A NIP-01 NOTICE message from the relay. */
118
+ notice: string;
119
+ /** Terminal close of the relay (a transient disconnect with reconnect does not fire it). */
120
+ close: undefined;
121
+ /** Fired when the relay sends a NIP-42 AUTH challenge. */
122
+ auth: string;
123
+ /** Fired after a successful reconnect (not the initial connect). */
124
+ reconnect: undefined;
125
+ /**
126
+ * Fired when a delivered EVENT fails id/signature verification, just before the event is dropped.
127
+ * A poisoned verifier dropping events does not fire it.
128
+ */
129
+ invalidevent: undefined;
118
130
  };
119
131
  /**
120
132
  * Single-relay NIP-01 client. Connection lifecycle, REQ/CLOSE, EVENT publish ACK, AUTH, COUNT,
@@ -123,17 +135,6 @@ type PublishResult = {
123
135
  declare class Relay {
124
136
  #private;
125
137
  readonly url: string;
126
- onnotice: ((msg: string) => void) | undefined;
127
- onclose: (() => void) | undefined;
128
- /** Fired when the relay sends a NIP-42 AUTH challenge. */
129
- onauth: ((challenge: string) => void) | undefined;
130
- /** Fired after a successful reconnect (not the initial connect). */
131
- onreconnect: (() => void) | undefined;
132
- /**
133
- * Fired when a delivered EVENT fails id/signature verification, just before the event is dropped.
134
- * A poisoned verifier dropping events does not fire it.
135
- */
136
- oninvalidevent: (() => void) | undefined;
137
138
  constructor(url: string, opts?: RelayOptions);
138
139
  /** Latest NIP-42 challenge, if any. */
139
140
  get challenge(): string | undefined;
@@ -145,6 +146,12 @@ declare class Relay {
145
146
  get subscriptionCount(): number;
146
147
  /** One-shot requests still awaiting a relay reply (EVENT ACK, COUNT, NEG). */
147
148
  get inFlightCount(): number;
149
+ /**
150
+ * Listen for a relay event. Multiple listeners per type are allowed and fire in registration
151
+ * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe
152
+ * function.
153
+ */
154
+ on<K extends keyof RelayEventMap>(type: K, listener: (payload: RelayEventMap[K]) => void): () => void;
148
155
  static connect(url: string, opts?: RelayOptions & {
149
156
  signal?: AbortSignal;
150
157
  }): Promise<Relay>;
@@ -161,25 +168,26 @@ declare class Relay {
161
168
  */
162
169
  disconnect(): void;
163
170
  /** Low-level REQ with callbacks. Survives reconnect when enableReconnect is on. */
164
- subscribe(filters: Filter[], opts?: SubscribeOptions): Subscription;
171
+ subscribe(filters: ReadonlyArray<Filter>, opts?: SubscribeOptions): RelaySubscription;
165
172
  /** AsyncIterable of events for filters until the subscription is closed. */
166
- stream(filters: Filter[], opts?: {
167
- signal?: AbortSignal;
168
- id?: string;
169
- }): AsyncIterable<Event> & {
170
- close: (reason?: string) => void;
171
- };
173
+ stream(filters: ReadonlyArray<Filter>, opts?: {
174
+ signal?: AbortSignal | undefined;
175
+ id?: string | undefined;
176
+ }): AsyncIterable<Event> & Closer;
172
177
  /**
173
178
  * One-shot query: collect events until the REQ ends, then close. `end` reports how it ended —
174
179
  * relay `EOSE`, relay `CLOSED` (with its reason), or the local timeout; events received before a
175
180
  * CLOSED or the deadline are still returned. Abort rejects; a failed (re)connect rejects.
176
181
  */
177
- fetch(filters: Filter[], opts?: {
182
+ fetch(filters: ReadonlyArray<Filter>, opts?: {
178
183
  timeoutMs?: number | undefined;
179
184
  signal?: AbortSignal | undefined;
180
185
  id?: string | undefined;
181
186
  }): Promise<RelayFetchResult>;
182
- /** Publish an event and wait for OK. */
187
+ /**
188
+ * Publish an event and wait for OK. A concurrent publish of the same event id joins the in-flight
189
+ * publish instead of racing it — the joiner's `timeoutMs` does not apply.
190
+ */
183
191
  publish(event: Event, opts?: {
184
192
  timeoutMs?: number | undefined;
185
193
  }): Promise<PublishResult>;
@@ -187,7 +195,7 @@ declare class Relay {
187
195
  * NIP-45 COUNT: ask the relay how many events match `filters`. Resolves with the COUNT payload
188
196
  * (count / optional approximate / optional hll).
189
197
  */
190
- count(filters: Filter[], opts?: {
198
+ count(filters: ReadonlyArray<Filter>, opts?: {
191
199
  id?: string | undefined;
192
200
  timeoutMs?: number | undefined;
193
201
  signal?: AbortSignal | undefined;
@@ -198,8 +206,8 @@ declare class Relay {
198
206
  }): Promise<PublishResult>;
199
207
  /**
200
208
  * Clear a cached AUTH rejection for the current challenge so a later `auth()` signs again. A
201
- * successful auth and in-flight answers are kept. When a challenge is pending and unanswered,
202
- * `onauth` is re-fired so the pool's automatic auth can run once more.
209
+ * successful auth and in-flight answers are kept. When a challenge is pending and unanswered, an
210
+ * `auth` event is re-fired so the pool's automatic auth can run once more.
203
211
  */
204
212
  resetAuth(): void;
205
213
  /**
@@ -231,16 +239,7 @@ type InvalidEventPolicy = {
231
239
  cooldownMs: number;
232
240
  };
233
241
  /** Pool-wide options applied to every managed relay. */
234
- type PoolOptions = {
235
- websocketImplementation?: WebSocketConstructor | undefined;
236
- verifyEvent?: RelayOptions["verifyEvent"];
237
- publishTimeoutMs?: number | undefined;
238
- connectTimeoutMs?: number | undefined;
239
- enableReconnect?: boolean | undefined;
240
- reconnectBackoffMs?: number[] | undefined;
241
- enablePing?: boolean | undefined;
242
- pingIntervalMs?: number | undefined;
243
- pingTimeoutMs?: number | undefined;
242
+ type PoolOptions = Omit<RelayOptions, "authSigner"> & {
244
243
  /**
245
244
  * When set, automatically answer NIP-42 AUTH challenges for relays that send them. Return
246
245
  * undefined to skip a given relay URL.
@@ -252,7 +251,6 @@ type PoolOptions = {
252
251
  /** Close unused relays. Unset/0 = disabled. */
253
252
  idleTimeoutMs?: number | undefined;
254
253
  idleCleanupIntervalMs?: number | undefined;
255
- onIdleRelaysClosed?: ((urls: string[]) => void) | undefined;
256
254
  /**
257
255
  * Soft cap on connected non-pinned relays. When `ensureRelay` would create a new non-pinned relay
258
256
  * at the cap, the least-recently-used idle one is closed first (idle = no subscriptions and no
@@ -269,17 +267,33 @@ type PoolOptions = {
269
267
  * through the normal reconnect path. Unset = events are dropped without counting, as before.
270
268
  */
271
269
  invalidEventPolicy?: InvalidEventPolicy | undefined;
270
+ };
271
+ /** Typed event payloads for {@link Pool.on}. */
272
+ type PoolEventMap = {
273
+ /** Normalized URLs closed by an idle-cleanup pass. */
274
+ idle: ReadonlyArray<string>;
272
275
  /**
273
- * Fired once when a relay is suspended for exceeding `invalidEventPolicy`: `url` is the
274
- * normalized relay URL and `until` the epoch-ms time the suspension lifts.
276
+ * A relay suspended for exceeding `invalidEventPolicy`: `url` is the normalized relay URL and
277
+ * `until` the epoch-ms time the suspension lifts.
275
278
  */
276
- onRelaySuspended?: ((url: string, until: number) => void) | undefined;
279
+ suspend: {
280
+ readonly url: string;
281
+ readonly until: number;
282
+ };
277
283
  };
278
- /** Per-relay publish outcome: the relay's OK reply or an error string. */
284
+ /** Per-relay publish outcome: the relay's OK verdict or a transport-level failure. */
279
285
  type PoolPublishResult = {
280
- url: string;
281
- result?: PublishResult;
282
- error?: string;
286
+ readonly url: string;
287
+ readonly status: "ok";
288
+ readonly message: string;
289
+ } | {
290
+ readonly url: string;
291
+ readonly status: "rejected";
292
+ readonly message: string;
293
+ } | {
294
+ readonly url: string;
295
+ readonly status: "failed";
296
+ readonly error: Error;
283
297
  };
284
298
  /** Multi-relay subscribe options: callbacks also receive the normalized relay URL. */
285
299
  type PoolSubscribeOptions = Omit<SubscribeOptions, "onevent" | "receivedEvent"> & {
@@ -288,25 +302,32 @@ type PoolSubscribeOptions = Omit<SubscribeOptions, "onevent" | "receivedEvent">
288
302
  };
289
303
  /** Per-relay one-shot fetch outcome: collected events plus how its REQ ended. */
290
304
  type PoolFetchResult = {
291
- url: string;
292
- events: Event[];
293
- end: RelayFetchEnd | {
305
+ readonly url: string;
306
+ readonly events: ReadonlyArray<Event>;
307
+ readonly end: RelayFetchEnd | {
294
308
  readonly type: "failed";
295
- readonly reason: string;
309
+ readonly error: Error;
296
310
  };
297
311
  };
298
- /** Per-relay NIP-45 COUNT result (or its error). */
299
- type PoolCountResult = {
300
- url: string;
301
- count?: number;
302
- approximate?: boolean;
303
- hll?: string;
304
- error?: string;
312
+ /** Per-relay NIP-45 COUNT result (or a transport-level failure). */
313
+ type PoolCountResult = ({
314
+ readonly url: string;
315
+ readonly status: "ok";
316
+ } & CountResult) | {
317
+ readonly url: string;
318
+ readonly status: "failed";
319
+ readonly error: Error;
305
320
  };
306
321
  /** Multi-relay coordinator: connection reuse, cross-relay event dedup, fan-out publish. */
307
322
  declare class Pool {
308
323
  #private;
309
324
  constructor(opts?: PoolOptions);
325
+ /**
326
+ * Listen for a pool event. Multiple listeners per type are allowed and fire in registration
327
+ * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe
328
+ * function.
329
+ */
330
+ on<K extends keyof PoolEventMap>(type: K, listener: (payload: PoolEventMap[K]) => void): () => void;
310
331
  setAllowInsecure(allow: boolean): void;
311
332
  setTrustedInsecureUrls(urls: ReadonlyArray<string>): void;
312
333
  setPinnedUrls(urls: ReadonlyArray<string>): void;
@@ -320,43 +341,46 @@ declare class Pool {
320
341
  signal?: AbortSignal | undefined;
321
342
  timeoutMs?: number | undefined;
322
343
  }): Promise<Relay>;
323
- close(urls?: string[]): void;
344
+ /** Close every pooled relay and clear all pool state. */
345
+ close(): void;
346
+ /** Close and forget the listed relays; other pooled relays are untouched. */
347
+ closeRelays(urls: ReadonlyArray<string>): void;
324
348
  /**
325
349
  * Subscribe across relays. Deduplicates by event id. Returns a closer; callbacks receive every
326
350
  * new event once.
327
351
  */
328
- subscribe(relays: string[], filters: Filter[], opts?: PoolSubscribeOptions): {
329
- close: (reason?: string) => void;
330
- };
352
+ subscribe(relays: ReadonlyArray<string>, filters: ReadonlyArray<Filter>, opts?: PoolSubscribeOptions): Closer;
331
353
  /**
332
354
  * One-shot fetch per relay: each entry reports its own events and end reason (`eose`, `closed`,
333
355
  * `timeout`), or `failed` when the connect or REQ itself errored. An abort rejects the whole
334
356
  * call. Events are not deduped across relays — use {@link fetch} for that.
335
357
  */
336
- fetchEach(relays: string[], filters: Filter[], opts?: {
358
+ fetchEach(relays: ReadonlyArray<string>, filters: ReadonlyArray<Filter>, opts?: {
337
359
  timeoutMs?: number | undefined;
338
360
  signal?: AbortSignal | undefined;
339
361
  }): Promise<PoolFetchResult[]>;
340
362
  /** Fetch events until each connected relay EOSE or timeout; dedupe by id. */
341
- fetch(relays: string[], filters: Filter[], opts?: {
363
+ fetch(relays: ReadonlyArray<string>, filters: ReadonlyArray<Filter>, opts?: {
342
364
  timeoutMs?: number | undefined;
343
365
  signal?: AbortSignal | undefined;
344
366
  /** Every event of every relay batch, including cross-relay duplicates. */
345
367
  onevent?: ((event: Event, relayUrl: string) => void) | undefined;
346
368
  }): Promise<Event[]>;
347
369
  /** Publish to all listed relays; returns per-relay outcomes. */
348
- publish(relays: string[], event: Event, opts?: {
370
+ publish(relays: ReadonlyArray<string>, event: Event, opts?: {
349
371
  timeoutMs?: number | undefined;
350
372
  }): Promise<PoolPublishResult[]>;
351
373
  /** First successful publish (Promise.any semantics). */
352
- publishAny(relays: string[], event: Event, opts?: {
374
+ publishAny(relays: ReadonlyArray<string>, event: Event, opts?: {
353
375
  timeoutMs?: number | undefined;
354
- }): Promise<PoolPublishResult>;
376
+ }): Promise<Extract<PoolPublishResult, {
377
+ status: "ok";
378
+ }>>;
355
379
  /**
356
380
  * NIP-45 COUNT across relays. Per-relay outcomes; failures do not throw. Counts are not summed —
357
381
  * each relay reports independently (may overlap).
358
382
  */
359
- count(relays: string[], filters: Filter[], opts?: {
383
+ count(relays: ReadonlyArray<string>, filters: ReadonlyArray<Filter>, opts?: {
360
384
  timeoutMs?: number | undefined;
361
385
  signal?: AbortSignal | undefined;
362
386
  }): Promise<PoolCountResult[]>;
@@ -367,5 +391,5 @@ declare class Pool {
367
391
  connectedUrls(): string[];
368
392
  }
369
393
  //#endregion
370
- export { SubscriptionHandlers as _, PoolOptions as a, PublishResult as c, RelayStatus as d, RelayStatusName as f, Subscription as g, SubscribeOptions as h, PoolFetchResult as i, Relay as l, RelayFetchResult as m, Pool as n, PoolPublishResult as o, RelayFetchEnd as p, PoolCountResult as r, PoolSubscribeOptions as s, InvalidEventPolicy as t, RelayOptions as u, subscriptionToAsyncIterable as v };
371
- //# sourceMappingURL=pool-DBIYMtwy.d.mts.map
394
+ export { Closer as _, PoolFetchResult as a, SubscriptionHandlers as b, PoolSubscribeOptions as c, RelayOptions as d, RelayStatus as f, RelayFetchResult as g, RelayFetchEnd as h, PoolEventMap as i, Relay as l, PublishResult as m, Pool as n, PoolOptions as o, RelayStatusName as p, PoolCountResult as r, PoolPublishResult as s, InvalidEventPolicy as t, RelayEventMap as u, RelaySubscription as v, subscriptionToAsyncIterable as x, SubscribeOptions as y };
395
+ //# sourceMappingURL=pool-CqQvUnsq.d.mts.map