@qntx/nostr 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +16 -0
  2. package/dist/abort-Ba8tDrOI.mjs +38 -0
  3. package/dist/abort-Ba8tDrOI.mjs.map +1 -0
  4. package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
  5. package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
  6. package/dist/builder-CyrGKRVP.mjs.map +1 -0
  7. package/dist/{client-Bd8B1mYm.mjs → client-5DhgYV7P.mjs} +372 -195
  8. package/dist/client-5DhgYV7P.mjs.map +1 -0
  9. package/dist/client.d.mts +1 -1
  10. package/dist/client.mjs +1 -1
  11. package/dist/core.d.mts +8 -8
  12. package/dist/core.mjs +8 -9
  13. package/dist/error-BW7re1UN.d.mts +9 -0
  14. package/dist/error-B__OMBRY.mjs +14 -0
  15. package/dist/error-B__OMBRY.mjs.map +1 -0
  16. package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
  17. package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
  18. package/dist/error-DrEIM7FO.mjs.map +1 -0
  19. package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
  20. package/dist/event-D8Nfoh4K.mjs.map +1 -0
  21. package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
  22. package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
  23. package/dist/filter-BQl3GlHt.mjs.map +1 -0
  24. package/dist/filter-C0IMByE3.d.mts +37 -0
  25. package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
  26. package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
  27. package/dist/gossip-oloDMWEj.mjs.map +1 -0
  28. package/dist/gossip.d.mts +1 -1
  29. package/dist/gossip.mjs +1 -1
  30. package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
  31. package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
  32. package/dist/http-CuazNRIP.mjs.map +1 -0
  33. package/dist/index-B-CGNDPS.d.mts +207 -0
  34. package/dist/index-BIhSADwK.d.mts +339 -0
  35. package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
  36. package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
  37. package/dist/index-D-TK_KJG.d.mts +84 -0
  38. package/dist/index-D1bqFHKB.d.mts +188 -0
  39. package/dist/index.d.mts +22 -20
  40. package/dist/index.mjs +19 -18
  41. package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
  42. package/dist/key-Bbtys6o7.mjs.map +1 -0
  43. package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
  44. package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
  45. package/dist/kind-Cxwhhkx8.mjs.map +1 -0
  46. package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
  47. package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
  48. package/dist/loaders.d.mts +3 -3
  49. package/dist/loaders.mjs +2 -2
  50. package/dist/memory-C2EVx0_O.mjs +46 -0
  51. package/dist/memory-C2EVx0_O.mjs.map +1 -0
  52. package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
  53. package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
  54. package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
  55. package/dist/message-DcY6Fi9o.mjs.map +1 -0
  56. package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
  57. package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
  58. package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
  59. package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
  60. package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
  61. package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
  62. package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
  63. package/dist/nips/blossom.d.mts +37 -37
  64. package/dist/nips/blossom.mjs +67 -52
  65. package/dist/nips/blossom.mjs.map +1 -1
  66. package/dist/nips/nip04.d.mts +2 -3
  67. package/dist/nips/nip04.mjs +6 -10
  68. package/dist/nips/nip04.mjs.map +1 -1
  69. package/dist/nips/nip05.d.mts +37 -43
  70. package/dist/nips/nip05.mjs +57 -57
  71. package/dist/nips/nip05.mjs.map +1 -1
  72. package/dist/nips/nip10.d.mts +18 -19
  73. package/dist/nips/nip10.mjs +76 -61
  74. package/dist/nips/nip10.mjs.map +1 -1
  75. package/dist/nips/nip11.d.mts +9 -8
  76. package/dist/nips/nip11.mjs +14 -11
  77. package/dist/nips/nip11.mjs.map +1 -1
  78. package/dist/nips/nip13.d.mts +9 -9
  79. package/dist/nips/nip13.mjs +15 -17
  80. package/dist/nips/nip13.mjs.map +1 -1
  81. package/dist/nips/nip17.d.mts +1 -1
  82. package/dist/nips/nip17.mjs +17 -12
  83. package/dist/nips/nip17.mjs.map +1 -1
  84. package/dist/nips/nip19.d.mts +1 -1
  85. package/dist/nips/nip19.mjs +52 -29
  86. package/dist/nips/nip19.mjs.map +1 -1
  87. package/dist/nips/nip21.d.mts +5 -6
  88. package/dist/nips/nip21.mjs +8 -7
  89. package/dist/nips/nip21.mjs.map +1 -1
  90. package/dist/nips/nip27.d.mts +7 -8
  91. package/dist/nips/nip27.mjs +26 -25
  92. package/dist/nips/nip27.mjs.map +1 -1
  93. package/dist/nips/nip42.d.mts +5 -6
  94. package/dist/nips/nip42.mjs +3 -3
  95. package/dist/nips/nip42.mjs.map +1 -1
  96. package/dist/nips/nip44.d.mts +24 -7
  97. package/dist/nips/nip44.mjs +25 -15
  98. package/dist/nips/nip44.mjs.map +1 -1
  99. package/dist/nips/nip46.d.mts +1 -1
  100. package/dist/nips/nip46.mjs +52 -49
  101. package/dist/nips/nip46.mjs.map +1 -1
  102. package/dist/nips/nip49.d.mts +8 -7
  103. package/dist/nips/nip49.mjs +16 -12
  104. package/dist/nips/nip49.mjs.map +1 -1
  105. package/dist/nips/nip51.d.mts +25 -27
  106. package/dist/nips/nip51.mjs +47 -36
  107. package/dist/nips/nip51.mjs.map +1 -1
  108. package/dist/nips/nip57.d.mts +12 -13
  109. package/dist/nips/nip57.mjs +35 -38
  110. package/dist/nips/nip57.mjs.map +1 -1
  111. package/dist/nips/nip59.d.mts +1 -1
  112. package/dist/nips/nip59.mjs +14 -18
  113. package/dist/nips/nip59.mjs.map +1 -1
  114. package/dist/nips/nip65.d.mts +1 -1
  115. package/dist/nips/nip65.mjs +11 -8
  116. package/dist/nips/nip65.mjs.map +1 -1
  117. package/dist/nips/nip77.d.mts +1 -1
  118. package/dist/nips/nip77.mjs +56 -34
  119. package/dist/nips/nip77.mjs.map +1 -1
  120. package/dist/nips/nip96.d.mts +21 -12
  121. package/dist/nips/nip96.mjs +59 -36
  122. package/dist/nips/nip96.mjs.map +1 -1
  123. package/dist/nips/nip98.d.mts +12 -12
  124. package/dist/nips/nip98.mjs +18 -19
  125. package/dist/nips/nip98.mjs.map +1 -1
  126. package/dist/pool-B7p_reAm.d.mts +333 -0
  127. package/dist/{pool-C_j1OmBB.mjs → pool-C2xOcsun.mjs} +792 -568
  128. package/dist/pool-C2xOcsun.mjs.map +1 -0
  129. package/dist/reactive-DDlThDSZ.mjs +324 -0
  130. package/dist/reactive-DDlThDSZ.mjs.map +1 -0
  131. package/dist/reactive-DFpqtIf_.d.mts +66 -0
  132. package/dist/relay.d.mts +5 -4
  133. package/dist/relay.mjs +2 -2
  134. package/dist/report-CHld4uWj.mjs +32 -0
  135. package/dist/report-CHld4uWj.mjs.map +1 -0
  136. package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
  137. package/dist/signer-CKR9EHce.mjs.map +1 -0
  138. package/dist/signer.d.mts +3 -3
  139. package/dist/signer.mjs +3 -2
  140. package/dist/storage-xXxLGCVb.mjs +1376 -0
  141. package/dist/storage-xXxLGCVb.mjs.map +1 -0
  142. package/dist/storage.d.mts +4 -4
  143. package/dist/storage.mjs +5 -4
  144. package/dist/store.d.mts +2 -0
  145. package/dist/store.mjs +2 -0
  146. package/dist/testing.d.mts +135 -0
  147. package/dist/testing.mjs +963 -0
  148. package/dist/testing.mjs.map +1 -0
  149. package/dist/types-2pCUprRU.d.mts +47 -0
  150. package/dist/types-CRQEk0Oi.d.mts +19 -0
  151. package/dist/util-LfQirgDk.mjs +113 -0
  152. package/dist/util-LfQirgDk.mjs.map +1 -0
  153. package/dist/wasm.d.mts +4 -4
  154. package/dist/wasm.mjs +54 -55
  155. package/dist/wasm.mjs.map +1 -1
  156. package/dist/websocket-CmeqvAkp.d.mts +27 -0
  157. package/package.json +93 -39
  158. package/dist/builder-q0VsvnYE.mjs.map +0 -1
  159. package/dist/client-Bd8B1mYm.mjs.map +0 -1
  160. package/dist/error-B49rbDzT.d.mts +0 -7
  161. package/dist/error-CwBxle6N.mjs.map +0 -1
  162. package/dist/error-DZEs5OuZ.mjs +0 -24
  163. package/dist/error-DZEs5OuZ.mjs.map +0 -1
  164. package/dist/event-CzoQEFMF.mjs.map +0 -1
  165. package/dist/filter-BxwjEkZp.mjs.map +0 -1
  166. package/dist/filter-DUEKP3q1.d.mts +0 -40
  167. package/dist/gossip-3xkRRCm_.mjs.map +0 -1
  168. package/dist/http-D2rhokZo.mjs.map +0 -1
  169. package/dist/index-C0C4e-Gn.d.mts +0 -55
  170. package/dist/index-DgOugrpF.d.mts +0 -275
  171. package/dist/index-EwL6fHLo.d.mts +0 -267
  172. package/dist/index-k2YhCgsh.d.mts +0 -57
  173. package/dist/key-Dn-aX4x4.mjs.map +0 -1
  174. package/dist/kind-BLl4cLOc.mjs.map +0 -1
  175. package/dist/loaders-BNQszDAn.mjs.map +0 -1
  176. package/dist/memory-BCmhZPek.mjs.map +0 -1
  177. package/dist/message-C_Uj-Y-c.mjs.map +0 -1
  178. package/dist/pool-C_j1OmBB.mjs.map +0 -1
  179. package/dist/pool-DhRi7l-b.d.mts +0 -297
  180. package/dist/signer-CZ6K6FUc.mjs.map +0 -1
  181. package/dist/storage-DOy0iLRE.mjs +0 -870
  182. package/dist/storage-DOy0iLRE.mjs.map +0 -1
  183. package/dist/types-BzyADqnw.d.mts +0 -19
  184. package/dist/types-Dn24TPFs.d.mts +0 -43
  185. package/dist/util-6tfLQ9JF.mjs +0 -73
  186. package/dist/util-6tfLQ9JF.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-CzoQEFMF.mjs","names":[],"sources":["../src/core/tag.ts","../src/core/event.ts"],"sourcesContent":["import { isAddressableKind, isReplaceableKind } from \"./kind.ts\";\nimport { isHex32 } from \"./util.ts\";\n\n/** A NIP-01 tag: first element is the name, rest are values. */\nexport type Tag = readonly string[];\n\n/** Mutable tag builder input. */\nexport type TagInput = string[];\n\nexport function isTag(value: unknown): value is Tag {\n if (!Array.isArray(value) || value.length === 0) return false;\n for (const item of value) {\n if (typeof item !== \"string\") return false;\n }\n return true;\n}\n\nexport function tagName(tag: Tag): string {\n return tag[0];\n}\n\nexport function tagValue(tag: Tag): string | undefined {\n return tag[1];\n}\n\n/** Construct common tags. */\nexport const Tag = {\n e(id: string, relay?: string, marker?: string, pubkey?: string): Tag {\n const t: string[] = [\"e\", id.toLowerCase()];\n if (relay !== undefined) t.push(relay);\n if (marker !== undefined) t.push(marker);\n if (pubkey !== undefined) t.push(pubkey.toLowerCase());\n return t;\n },\n p(pubkey: string, relay?: string, petname?: string): Tag {\n const t: string[] = [\"p\", pubkey.toLowerCase()];\n if (relay !== undefined) t.push(relay);\n if (petname !== undefined) t.push(petname);\n return t;\n },\n a(coordinate: string, relay?: string): Tag {\n const t: string[] = [\"a\", coordinate];\n if (relay !== undefined) t.push(relay);\n return t;\n },\n d(identifier: string): Tag {\n return [\"d\", identifier];\n },\n t(hashtag: string): Tag {\n return [\"t\", hashtag];\n },\n r(url: string, marker?: string): Tag {\n const t: string[] = [\"r\", url];\n if (marker !== undefined) t.push(marker);\n return t;\n },\n k(kind: number | string): Tag {\n return [\"k\", String(kind)];\n },\n} as const;\n\n/** First `d` tag value on an event, if any. */\nexport function getDTag(tags: readonly Tag[]): string | undefined {\n for (const tag of tags) {\n if (tag[0] === \"d\" && tag[1] !== undefined) return tag[1];\n }\n return undefined;\n}\n\n/** NIP-01 `a` tag: `kind:pubkey:identifier` (identifier may be empty and may contain extra colons). */\nexport type EventAddress = {\n kind: number;\n pubkey: string;\n identifier: string;\n};\n\n/** Parse a NIP-01 addressable/replaceable coordinate. */\nexport function parseEventAddress(value: string): EventAddress | undefined {\n const first = value.indexOf(\":\");\n if (first <= 0) return undefined;\n const second = value.indexOf(\":\", first + 1);\n if (second < 0) return undefined;\n const kind = Number(value.slice(0, first));\n if (!Number.isInteger(kind) || kind < 0 || kind > 65535) return undefined;\n const pubkey = value.slice(first + 1, second).toLowerCase();\n if (!isHex32(pubkey)) return undefined;\n return { kind, pubkey, identifier: value.slice(second + 1) };\n}\n\nexport function formatEventAddress(kind: number, pubkey: string, identifier = \"\"): string {\n return `${kind}:${pubkey.toLowerCase()}:${identifier}`;\n}\n\n/**\n * Coordinate for replaceable (`kind:pubkey:`) and addressable (`kind:pubkey:d`) events.\n * Undefined for regular/ephemeral kinds.\n */\nexport function eventAddress(event: {\n kind: number;\n pubkey: string;\n tags: readonly Tag[];\n}): string | undefined {\n if (isAddressableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, getDTag(event.tags) ?? \"\");\n }\n if (isReplaceableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, \"\");\n }\n return undefined;\n}\n","import { EventValidationError } from \"./error.ts\";\nimport { isHex32, isHex64, utf8Encoder } from \"./util.ts\";\nimport type { Tag } from \"./tag.ts\";\nimport { isTag } from \"./tag.ts\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bytesToHex } from \"./util.ts\";\n\n/** Wire event template before pubkey/id/sig. */\nexport type EventTemplate = {\n readonly kind: number;\n readonly tags: readonly Tag[];\n readonly content: string;\n readonly created_at: number;\n};\n\n/** Event with pubkey, ready to hash and sign. */\nexport type UnsignedEvent = EventTemplate & {\n readonly pubkey: string;\n};\n\n/**\n * Fully formed NIP-01 event (JSON-friendly plain object).\n * Fields are readonly at the type level; wire interop remains plain JSON.\n */\nexport type Event = UnsignedEvent & {\n readonly id: string;\n readonly sig: string;\n};\n\n/** Cache of events whose signatures have been verified successfully. */\nconst verifiedEvents = new WeakSet<object>();\n\n/** Cache of events known to fail verification. */\nconst failedEvents = new WeakSet<object>();\n\nexport function markVerified(event: Event): void {\n verifiedEvents.add(event);\n failedEvents.delete(event);\n}\n\nexport function markUnverified(event: Event): void {\n failedEvents.add(event);\n verifiedEvents.delete(event);\n}\n\nexport function isMarkedVerified(event: Event): boolean {\n return verifiedEvents.has(event);\n}\n\nexport function isMarkedFailed(event: Event): boolean {\n return failedEvents.has(event);\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> =>\n typeof obj === \"object\" && obj !== null && !Array.isArray(obj);\n\n/** Structural validation of an unsigned event (no crypto). */\nexport function validateEvent(event: unknown): event is UnsignedEvent {\n if (!isRecord(event)) return false;\n if (\n typeof event.kind !== \"number\" ||\n !Number.isInteger(event.kind) ||\n event.kind < 0 ||\n event.kind > 65535\n ) {\n return false;\n }\n if (typeof event.content !== \"string\") return false;\n if (typeof event.created_at !== \"number\" || !Number.isInteger(event.created_at)) return false;\n if (typeof event.pubkey !== \"string\" || !isHex32(event.pubkey)) return false;\n if (!Array.isArray(event.tags)) return false;\n for (const tag of event.tags) {\n if (!isTag(tag)) return false;\n }\n return true;\n}\n\n/** Structural validation of a full event including id and sig hex shape. */\nexport function validateSignedEvent(event: unknown): event is Event {\n if (!validateEvent(event)) return false;\n const e = event as Record<string, unknown>;\n if (typeof e.id !== \"string\" || !isHex32(e.id)) return false;\n if (typeof e.sig !== \"string\" || !isHex64(e.sig)) return false;\n return true;\n}\n\n/**\n * Canonical NIP-01 serialization used for hashing:\n * JSON array `[0, pubkey, created_at, kind, tags, content]` with no extra whitespace.\n */\nexport function serializeEvent(event: UnsignedEvent): string {\n if (!validateEvent(event)) {\n throw new EventValidationError(\"cannot serialize event with invalid shape\");\n }\n return JSON.stringify([\n 0,\n event.pubkey.toLowerCase(),\n event.created_at,\n event.kind,\n event.tags,\n event.content,\n ]);\n}\n\n/** SHA-256 of the canonical serialization, lowercase hex. */\nexport function getEventHash(event: UnsignedEvent): string {\n const serialized = serializeEvent(event);\n return bytesToHex(sha256(utf8Encoder.encode(serialized)));\n}\n\nfunction compareEventsDesc(a: Event, b: Event): 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\n/**\n * Sort events reverse-chronologically by `created_at`, then by `id` lexicographically.\n * Mutates and returns the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort(compareEventsDesc);\n}\n\n/** Non-mutating sort; returns a new array. */\nexport function sortedEvents(events: readonly Event[]): Event[] {\n return events.slice().sort(compareEventsDesc);\n}\n\n/**\n * Negentropy item order: `created_at` ascending, then `id` lexicographic (`<`/`>`).\n * Time axis is opposite {@link sortEvents}; id tie-break is the same direction.\n */\nexport function itemCompare(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) return a.created_at - b.created_at;\n if (a.id < b.id) return -1;\n if (a.id > b.id) return 1;\n return 0;\n}\n\n/**\n * NIP-01 replaceable/addressable winner: higher `created_at`, or equal timestamp\n * and lexicographically lower `id`.\n */\nexport function isReplaceableWinner(\n candidate: { created_at: number; id: string },\n incumbent: { created_at: number; id: string },\n): boolean {\n if (candidate.created_at !== incumbent.created_at) {\n return candidate.created_at > incumbent.created_at;\n }\n return candidate.id < incumbent.id;\n}\n\n/** True when a signed event is the signed form of `unsigned` (kind/tags/content/time/pubkey). */\nexport function signedMatchesUnsigned(signed: Event, unsigned: UnsignedEvent): boolean {\n if (signed.kind !== unsigned.kind) return false;\n if (signed.content !== unsigned.content) return false;\n if (signed.created_at !== unsigned.created_at) return false;\n if (JSON.stringify(signed.tags) !== JSON.stringify(unsigned.tags)) return false;\n if (unsigned.pubkey && signed.pubkey.toLowerCase() !== unsigned.pubkey.toLowerCase()) {\n return false;\n }\n return true;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"filter-BxwjEkZp.mjs","names":[],"sources":["../src/core/filter.ts"],"sourcesContent":["import type { Event } from \"./event.ts\";\nimport { isAddressableKind, isReplaceableKind } from \"./kind.ts\";\n\n/** NIP-01 filter object (immutable-friendly; callers may still pass plain objects). */\nexport type Filter = {\n readonly ids?: readonly string[];\n readonly kinds?: readonly number[];\n readonly authors?: readonly string[];\n readonly since?: number;\n readonly until?: number;\n readonly limit?: number;\n /**\n * NIP-50 full-text search. Relays interpret this.\n * Local `matchFilter` / `query` ignore it.\n */\n readonly search?: string;\n readonly [key: `#${string}`]: readonly string[] | undefined;\n};\n\n/** Local NIP-01 match. `search` is ignored; relays interpret NIP-50. */\nexport function matchFilter(filter: Filter, event: Event): boolean {\n if (filter.ids && !filter.ids.some((id) => id.toLowerCase() === event.id.toLowerCase())) {\n return false;\n }\n if (filter.kinds && !filter.kinds.includes(event.kind)) return false;\n if (\n filter.authors &&\n !filter.authors.some((pk) => pk.toLowerCase() === event.pubkey.toLowerCase())\n ) {\n return false;\n }\n\n for (const key of Object.keys(filter)) {\n if (key[0] !== \"#\") continue;\n const tagName = key.slice(1);\n const values = filter[`#${tagName}`];\n if (!values) continue;\n const hexTag = tagName === \"e\" || tagName === \"p\";\n const hit = event.tags.some((tag) => {\n if (tag[0] !== tagName || tag[1] === undefined) return false;\n if (hexTag) {\n return values.some((v) => v.toLowerCase() === tag[1]!.toLowerCase());\n }\n return values.includes(tag[1]);\n });\n if (!hit) return false;\n }\n\n if (filter.since !== undefined && event.created_at < filter.since) return false;\n if (filter.until !== undefined && event.created_at > filter.until) return false;\n\n return true;\n}\n\nexport function matchFilters(filters: readonly Filter[], event: Event): boolean {\n for (const filter of filters) {\n if (matchFilter(filter, event)) return true;\n }\n return false;\n}\n\n/** Merge filters by unioning list fields; returns a new plain object. */\nexport function mergeFilters(...filters: Filter[]): Filter {\n const result: Record<string, unknown> = {};\n for (const filter of filters) {\n for (const [property, values] of Object.entries(filter)) {\n if (\n property === \"kinds\" ||\n property === \"ids\" ||\n property === \"authors\" ||\n property[0] === \"#\"\n ) {\n const list = (result[property] as (string | number)[] | undefined) ?? [];\n for (const value of values as readonly (string | number)[]) {\n if (!list.includes(value)) list.push(value);\n }\n result[property] = list;\n }\n }\n if (filter.limit !== undefined) {\n const prev = result.limit as number | undefined;\n if (prev === undefined || filter.limit > prev) result.limit = filter.limit;\n }\n if (filter.until !== undefined) {\n const prev = result.until as number | undefined;\n if (prev === undefined || filter.until > prev) result.until = filter.until;\n }\n if (filter.since !== undefined) {\n const prev = result.since as number | undefined;\n if (prev === undefined || filter.since < prev) result.since = filter.since;\n }\n if (filter.search !== undefined) {\n result.search = filter.search;\n }\n }\n return result as Filter;\n}\n\n/**\n * Intrinsic upper bound implied by the filter alone.\n * Returns a positive integer, or `Infinity` when unbounded.\n */\nexport function getFilterLimit(filter: Filter): number {\n if (filter.ids && filter.ids.length === 0) return 0;\n if (filter.kinds && filter.kinds.length === 0) return 0;\n if (filter.authors && filter.authors.length === 0) return 0;\n\n let limit = Infinity;\n if (filter.ids) limit = Math.min(limit, filter.ids.length);\n if (filter.limit !== undefined) limit = Math.min(limit, filter.limit);\n\n if (filter.kinds && filter.authors) {\n const allReplaceable = filter.kinds.every((k) => isReplaceableKind(k) || isAddressableKind(k));\n if (allReplaceable) {\n const dTags = filter[\"#d\"];\n const perAuthor = dTags && dTags.length > 0 ? dTags.length : 1;\n limit = Math.min(limit, filter.authors.length * filter.kinds.length * perAuthor);\n }\n }\n\n return limit;\n}\n\n/** Shallow-clone a filter (for per-relay narrowing). */\nexport function cloneFilter(filter: Filter): Filter {\n return { ...filter };\n}\n\nconst HEX_LIST_KEYS = new Set([\"ids\", \"authors\", \"#e\", \"#p\"]);\n\n/** Lowercase hex lists and sort every array. Omits undefined so `[]` stays distinct from missing. */\nexport function canonicalizeFilter(filter: Filter): Filter {\n const raw = filter as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(raw).sort()) {\n const value = raw[key];\n // omit undefined so a missing key is not `[]` / null\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n if (HEX_LIST_KEYS.has(key)) {\n out[key] = value.map((v) => String(v).toLowerCase()).sort();\n } else if (key === \"kinds\") {\n out[key] = value.map((v) => Number(v)).sort((a, b) => a - b);\n } else {\n out[key] = value.map((v) => String(v)).sort();\n }\n } else {\n out[key] = value;\n }\n }\n return out as Filter;\n}\n\nexport function canonicalizeFilters(filters: readonly Filter[]): Filter[] {\n return filters.map(canonicalizeFilter);\n}\n\n/**\n * Canonical identity for live REQ coalescing.\n * Relays treat a missing list key as unconstrained and `[]` as match-nothing.\n */\nexport function filterFingerprint(filters: readonly Filter[]): string {\n const parts = filters.map((filter) => JSON.stringify(canonicalizeFilter(filter)));\n parts.sort();\n return `[${parts.join(\",\")}]`;\n}\n"],"mappings":"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"}
@@ -1,40 +0,0 @@
1
- import { t as Event } from "./event-B5D2jxt_.mjs";
2
- //#region src/core/filter.d.ts
3
- /** NIP-01 filter object (immutable-friendly; callers may still pass plain objects). */
4
- type Filter = {
5
- readonly ids?: readonly string[];
6
- readonly kinds?: readonly number[];
7
- readonly authors?: readonly string[];
8
- readonly since?: number;
9
- readonly until?: number;
10
- readonly limit?: number;
11
- /**
12
- * NIP-50 full-text search. Relays interpret this.
13
- * Local `matchFilter` / `query` ignore it.
14
- */
15
- readonly search?: string;
16
- readonly [key: `#${string}`]: readonly string[] | undefined;
17
- };
18
- /** Local NIP-01 match. `search` is ignored; relays interpret NIP-50. */
19
- declare function matchFilter(filter: Filter, event: Event): boolean;
20
- declare function matchFilters(filters: readonly Filter[], event: Event): boolean;
21
- /** Merge filters by unioning list fields; returns a new plain object. */
22
- declare function mergeFilters(...filters: Filter[]): Filter;
23
- /**
24
- * Intrinsic upper bound implied by the filter alone.
25
- * Returns a positive integer, or `Infinity` when unbounded.
26
- */
27
- declare function getFilterLimit(filter: Filter): number;
28
- /** Shallow-clone a filter (for per-relay narrowing). */
29
- declare function cloneFilter(filter: Filter): Filter;
30
- /** Lowercase hex lists and sort every array. Omits undefined so `[]` stays distinct from missing. */
31
- declare function canonicalizeFilter(filter: Filter): Filter;
32
- declare function canonicalizeFilters(filters: readonly Filter[]): Filter[];
33
- /**
34
- * Canonical identity for live REQ coalescing.
35
- * Relays treat a missing list key as unconstrained and `[]` as match-nothing.
36
- */
37
- declare function filterFingerprint(filters: readonly Filter[]): string;
38
- //#endregion
39
- export { filterFingerprint as a, matchFilters as c, cloneFilter as i, mergeFilters as l, canonicalizeFilter as n, getFilterLimit as o, canonicalizeFilters as r, matchFilter as s, Filter as t };
40
- //# sourceMappingURL=filter-DUEKP3q1.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gossip-3xkRRCm_.mjs","names":["#routes","#maxRelays","#breakAuthors"],"sources":["../src/gossip/gossip.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { parseDmRelayList } from \"../nips/nip17.ts\";\nimport { parseRelayList, type RelayListItem } from \"../nips/nip65.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\n\nexport type PubkeyRoutes = {\n /** Relays the user writes to (outbox). NIP-65. */\n write: string[];\n /** Relays the user reads from (inbox). NIP-65. */\n read: string[];\n /** Relays for NIP-17 gift-wrap delivery. Kind 10050. */\n dm: string[];\n /** `created_at` of the last accepted kind:10002 list. */\n updatedAt: number;\n /** `created_at` of the last accepted kind:10050 list. */\n dmUpdatedAt: number;\n /** Event id of the last accepted kind:10002 (NIP-01 equal-timestamp tie-break). */\n relayListId?: string;\n /** Event id of the last accepted kind:10050. */\n dmListId?: string;\n};\n\nexport type RoutedFilter = {\n /** url → already-narrowed filter. Empty when nothing routed. */\n perRelay: Map<string, Filter>;\n /**\n * Unrouted work for the caller:\n * - no authors and no #p: original filter (today's \"generic\")\n * - authors/#p all unrouted: original filter (today's \"orphan\")\n * - mixed: narrowed leftover (today's fallback)\n * - all routed: undefined\n */\n remainder?: Filter;\n};\n\nexport type GossipOptions = {\n /** Max relays to keep per direction when ranking. Default 4. */\n maxRelaysPerPubkey?: number;\n};\n\nfunction emptyRoutes(): PubkeyRoutes {\n return {\n write: [],\n read: [],\n dm: [],\n updatedAt: 0,\n dmUpdatedAt: 0,\n };\n}\n\n/**\n * Routing table for NIP-65 (10002) and NIP-17 DM relays (10050).\n * Ingest replaceable list events, then route filters into per-relay REQs.\n */\nexport class Gossip {\n readonly #routes = new Map<string, PubkeyRoutes>();\n readonly #maxRelays: number;\n\n constructor(opts: GossipOptions = {}) {\n this.#maxRelays = opts.maxRelaysPerPubkey ?? 4;\n }\n\n /**\n * Ingest a routing list event.\n * - kind:10002 → write/read (NIP-65)\n * - kind:10050 → dm (NIP-17)\n * Returns true if routes for that list type were updated.\n */\n ingest(event: Event): boolean {\n if (event.kind === Kind.RelayList) {\n let items: RelayListItem[];\n try {\n items = parseRelayList(event);\n } catch {\n return false;\n }\n return this.setRoutes(event.pubkey, items, event.created_at, event.id);\n }\n if (event.kind === Kind.DirectMessageRelaysList) {\n let relays: string[];\n try {\n relays = parseDmRelayList(event);\n } catch {\n return false;\n }\n return this.setDmRoutes(event.pubkey, relays, event.created_at, event.id);\n }\n return false;\n }\n\n setRoutes(\n pubkey: string,\n items: RelayListItem[],\n updatedAt = Math.floor(Date.now() / 1000),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#routes.get(pk) ?? emptyRoutes();\n if (prev.updatedAt > updatedAt) return false;\n if (\n eventId &&\n prev.relayListId &&\n prev.updatedAt === updatedAt &&\n !isReplaceableWinner(\n { created_at: updatedAt, id: eventId },\n { created_at: prev.updatedAt, id: prev.relayListId },\n )\n ) {\n return false;\n }\n\n const write: string[] = [];\n const read: string[] = [];\n for (const item of items) {\n let url: string;\n try {\n url = normalizeURL(item.url);\n } catch {\n continue;\n }\n if (item.write && !write.includes(url)) write.push(url);\n if (item.read && !read.includes(url)) read.push(url);\n }\n\n this.#routes.set(pk, {\n write: write.slice(0, this.#maxRelays),\n read: read.slice(0, this.#maxRelays),\n dm: prev.dm,\n updatedAt,\n dmUpdatedAt: prev.dmUpdatedAt,\n relayListId: eventId ?? prev.relayListId,\n dmListId: prev.dmListId,\n });\n return true;\n }\n\n setDmRoutes(\n pubkey: string,\n relays: readonly string[],\n updatedAt = Math.floor(Date.now() / 1000),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#routes.get(pk) ?? emptyRoutes();\n if (prev.dmUpdatedAt > updatedAt) return false;\n if (\n eventId &&\n prev.dmListId &&\n prev.dmUpdatedAt === updatedAt &&\n !isReplaceableWinner(\n { created_at: updatedAt, id: eventId },\n { created_at: prev.dmUpdatedAt, id: prev.dmListId },\n )\n ) {\n return false;\n }\n\n const dm: string[] = [];\n for (const raw of relays) {\n let url: string;\n try {\n url = normalizeURL(raw);\n } catch {\n continue;\n }\n if (!dm.includes(url)) dm.push(url);\n }\n\n this.#routes.set(pk, {\n write: prev.write,\n read: prev.read,\n dm: dm.slice(0, this.#maxRelays),\n updatedAt: prev.updatedAt,\n dmUpdatedAt: updatedAt,\n relayListId: prev.relayListId,\n dmListId: eventId ?? prev.dmListId,\n });\n return true;\n }\n\n getRoutes(pubkey: string): PubkeyRoutes | undefined {\n return this.#routes.get(pubkey.toLowerCase());\n }\n\n outboxRelays(pubkey: string): string[] {\n return this.#routes.get(pubkey.toLowerCase())?.write ?? [];\n }\n\n inboxRelays(pubkey: string): string[] {\n return this.#routes.get(pubkey.toLowerCase())?.read ?? [];\n }\n\n /** NIP-17 kind:10050 delivery relays for gift-wraps. */\n dmRelays(pubkey: string): string[] {\n return this.#routes.get(pubkey.toLowerCase())?.dm ?? [];\n }\n\n clear(pubkey?: string): void {\n if (pubkey) this.#routes.delete(pubkey.toLowerCase());\n else this.#routes.clear();\n }\n\n get size(): number {\n return this.#routes.size;\n }\n\n /**\n * Route a user-facing filter into per-relay sub-filters plus leftover.\n * - authors → outbox relays, filter narrowed per relay's authors\n * - #p only → inbox relays of those pubkeys\n * - neither / all unrouted → remainder is the original filter\n * - mixed → remainder is the narrowed leftover; all routed → no remainder\n */\n route(filter: Filter): RoutedFilter {\n const authors = filter.authors?.map((a) => a.toLowerCase());\n const pTags = filter[\"#p\"]?.map((p) => p.toLowerCase());\n\n if ((!authors || authors.length === 0) && (!pTags || pTags.length === 0)) {\n return { perRelay: new Map(), remainder: filter };\n }\n\n if (authors && authors.length > 0 && (!pTags || pTags.length === 0)) {\n return this.#breakAuthors(filter, authors, \"write\");\n }\n\n if (pTags && pTags.length > 0 && (!authors || authors.length === 0)) {\n return this.#breakAuthors(\n { ...filter, authors: undefined },\n pTags,\n \"read\",\n (base, pubkeys) => ({ ...base, \"#p\": pubkeys }),\n );\n }\n\n // both authors and #p: send full filter to union of routes\n const unionKeys = new Set([...(authors ?? []), ...(pTags ?? [])]);\n const relays = new Set<string>();\n let anyUnrouted = false;\n for (const pk of unionKeys) {\n const r = this.#routes.get(pk);\n const urls = r ? [...r.write, ...r.read] : [];\n if (urls.length === 0) {\n anyUnrouted = true;\n continue;\n }\n for (const u of urls) relays.add(u);\n }\n if (relays.size === 0) return { perRelay: new Map(), remainder: filter };\n const map = new Map<string, Filter>();\n for (const url of relays) map.set(url, { ...filter });\n return anyUnrouted ? { perRelay: map, remainder: filter } : { perRelay: map };\n }\n\n #breakAuthors(\n filter: Filter,\n pubkeys: string[],\n direction: \"read\" | \"write\",\n narrow: (base: Filter, pubkeys: string[]) => Filter = (base, pks) => ({\n ...base,\n authors: pks,\n }),\n ): RoutedFilter {\n const perRelay = new Map<string, string[]>();\n const unrouted: string[] = [];\n let anyRoute = false;\n\n for (const pk of pubkeys) {\n const routes = this.#routes.get(pk);\n const urls = direction === \"write\" ? routes?.write : routes?.read;\n if (!urls || urls.length === 0) {\n unrouted.push(pk);\n continue;\n }\n anyRoute = true;\n for (const url of urls) {\n const list = perRelay.get(url) ?? [];\n list.push(pk);\n perRelay.set(url, list);\n }\n }\n\n if (!anyRoute) return { perRelay: new Map(), remainder: filter };\n\n const map = new Map<string, Filter>();\n for (const [url, pks] of perRelay) {\n map.set(url, narrow(filter, pks));\n }\n return unrouted.length > 0\n ? { perRelay: map, remainder: narrow(filter, unrouted) }\n : { perRelay: map };\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"http-D2rhokZo.mjs","names":[],"sources":["../src/nips/http.ts"],"sourcesContent":["/**\n * Shared nips HTTP primitive. Not a pack entry (core has zero network).\n * Callers never pass `redirect`; sendManual always sets `\"manual\"`.\n */\n\nexport type ManualFetch = (\n url: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: Blob | FormData | string | null;\n signal?: AbortSignal;\n },\n) => Promise<{\n ok: boolean;\n status: number;\n headers: { get(name: string): string | null };\n json(): Promise<unknown>;\n arrayBuffer(): Promise<ArrayBuffer>;\n}>;\n\ntype ManualInit = NonNullable<Parameters<ManualFetch>[1]>;\n\nexport function requireGlobalFetch(missing: () => Error): ManualFetch {\n if (typeof globalThis.fetch !== \"function\") throw missing();\n return globalThis.fetch.bind(globalThis) as ManualFetch;\n}\n\n/** Always sets redirect:manual. Shared by fetchManual and headStatus. */\nexport async function sendManual(\n fetchImpl: ManualFetch,\n url: string,\n init: ManualInit,\n): Promise<Awaited<ReturnType<ManualFetch>>> {\n return await fetchImpl(url, { ...init, redirect: \"manual\" } as Parameters<ManualFetch>[1]);\n}\n\nexport async function fetchManual(\n fetchImpl: ManualFetch,\n url: string,\n init: ManualInit,\n wrapNetwork: (err: unknown) => Error,\n): Promise<Awaited<ReturnType<ManualFetch>>> {\n try {\n return await sendManual(fetchImpl, url, init);\n } catch (err) {\n if (err instanceof Error && err.name === \"AbortError\") throw err;\n throw wrapNetwork(err);\n }\n}\n"],"mappings":";AAuBA,SAAgB,mBAAmB,SAAmC;CACpE,IAAI,OAAO,WAAW,UAAU,YAAY,MAAM,QAAQ;CAC1D,OAAO,WAAW,MAAM,KAAK,UAAU;AACzC;;AAGA,eAAsB,WACpB,WACA,KACA,MAC2C;CAC3C,OAAO,MAAM,UAAU,KAAK;EAAE,GAAG;EAAM,UAAU;CAAS,CAA+B;AAC3F;AAEA,eAAsB,YACpB,WACA,KACA,MACA,aAC2C;CAC3C,IAAI;EACF,OAAO,MAAM,WAAW,WAAW,KAAK,IAAI;CAC9C,SAAS,KAAK;EACZ,IAAI,eAAe,SAAS,IAAI,SAAS,cAAc,MAAM;EAC7D,MAAM,YAAY,GAAG;CACvB;AACF"}
@@ -1,55 +0,0 @@
1
- import { t as Event } from "./event-B5D2jxt_.mjs";
2
- import { t as Filter } from "./filter-DUEKP3q1.mjs";
3
- import { a as NostrError } from "./error-WEFS-R-4.mjs";
4
- import { t as Pool } from "./pool-DhRi7l-b.mjs";
5
- //#region src/relay/error.d.ts
6
- declare class RelayError extends NostrError {
7
- readonly url?: string;
8
- constructor(message: string, url?: string);
9
- }
10
- declare class RelayConnectionError extends RelayError {}
11
- declare class RelayPublishError extends RelayError {}
12
- declare class RelayClosedError extends RelayError {}
13
- //#endregion
14
- //#region src/relay/url.d.ts
15
- /**
16
- * True for `ws:` / `http:` URLs that are not `.onion`.
17
- * Local-network `ws://` is still insecure; allow those via a trusted set.
18
- */
19
- declare function isInsecureRelayUrl(url: string): boolean;
20
- //#endregion
21
- //#region src/relay/fan-in.d.ts
22
- type RoutedJob = {
23
- urls: readonly string[];
24
- filters: readonly Filter[];
25
- id?: string;
26
- };
27
- type FanInOptions = {
28
- onevent?: (event: Event) => void;
29
- oneose?: () => void;
30
- onclose?: (reason: string) => void;
31
- signal?: AbortSignal;
32
- /** Armed at fan-in only. Never forwarded to relay.subscribe. */
33
- eoseTimeoutMs?: number;
34
- alreadyHaveEvent?: (id: string) => boolean;
35
- receivedEvent?: (id: string) => void;
36
- closeOnEose?: boolean;
37
- /** Optional. ensureRelay uses Pool.#opts.connectTimeoutMs when omitted. */
38
- connectTimeoutMs?: number;
39
- };
40
- /**
41
- * Aggregate EOSE/close across routed jobs. One `seen` set.
42
- * `pending` counts URL list entries (duplicates included).
43
- * `pendingEose` counts unique `jobIndex:url` keys.
44
- */
45
- declare function fanIn(pool: Pool, jobs: readonly RoutedJob[], opts?: FanInOptions): {
46
- close: (reason?: string) => void;
47
- };
48
- declare function fetchRouted(pool: Pool, jobs: readonly RoutedJob[], opts?: {
49
- timeoutMs?: number;
50
- signal?: AbortSignal;
51
- connectTimeoutMs?: number;
52
- }): Promise<Event[]>;
53
- //#endregion
54
- export { isInsecureRelayUrl as a, RelayError as c, fetchRouted as i, RelayPublishError as l, RoutedJob as n, RelayClosedError as o, fanIn as r, RelayConnectionError as s, FanInOptions as t };
55
- //# sourceMappingURL=index-C0C4e-Gn.d.mts.map
@@ -1,275 +0,0 @@
1
- import { n as EventTemplate, r as UnsignedEvent, t as Event } from "./event-B5D2jxt_.mjs";
2
- import { t as EventBuilder } from "./builder-BphJa-Y-.mjs";
3
- import { t as Filter } from "./filter-DUEKP3q1.mjs";
4
- import { t as Gossip } from "./gossip-C5RVB1UB.mjs";
5
- import { a as NostrError } from "./error-WEFS-R-4.mjs";
6
- import { i as PoolPublishResult, t as Pool, u as WebSocketConstructor } from "./pool-DhRi7l-b.mjs";
7
- import { a as OutboxFeed, n as Loaders } from "./index-EwL6fHLo.mjs";
8
- import { t as EventStore } from "./types-Dn24TPFs.mjs";
9
- import { a as Rumor } from "./nip59-DljW_sYh.mjs";
10
- import { i as ReplyTo, r as Recipient } from "./nip17-BWq-Kfq5.mjs";
11
- import { t as NostrSigner } from "./types-BzyADqnw.mjs";
12
- import { t as StorageError } from "./error-B49rbDzT.mjs";
13
- //#region src/client/builder.d.ts
14
- /** Fluent constructor for {@link Client}. */
15
- declare class ClientBuilder {
16
- #private;
17
- signer(signer: NostrSigner): this;
18
- relays(urls: readonly string[]): this;
19
- addRelay(url: string): this;
20
- websocketImplementation(impl: WebSocketConstructor): this;
21
- verifyEvent(fn: (event: Event) => boolean): this;
22
- connectTimeoutMs(ms: number): this;
23
- publishTimeoutMs(ms: number): this;
24
- automaticAuth(enabled: boolean): this;
25
- enableReconnect(enabled: boolean): this;
26
- enablePing(enabled: boolean): this;
27
- pingIntervalMs(ms: number): this;
28
- pingTimeoutMs(ms: number): this;
29
- /**
30
- * Local event store. Defaults to {@link MemoryEventStore}.
31
- * Browser apps that want persistence must pass {@link IndexedDbEventStore} and `await open()`.
32
- */
33
- storage(store: EventStore): this;
34
- /** When false, Client will not auto-write events to storage. Default true. */
35
- persistEvents(enabled: boolean): this;
36
- gossip(gossip: Gossip): this;
37
- onstorageerror(fn: (err: StorageError) => void): this;
38
- build(): Client;
39
- }
40
- //#endregion
41
- //#region src/client/types.d.ts
42
- declare const SyncDirection: {
43
- readonly Up: "up";
44
- readonly Down: "down";
45
- readonly Both: "both";
46
- };
47
- type SyncDirectionName = (typeof SyncDirection)[keyof typeof SyncDirection];
48
- type SyncOptions = {
49
- relays?: readonly string[];
50
- direction?: SyncDirectionName;
51
- /**
52
- * Wall-clock deadline for the Negentropy reconciliation session
53
- * (`NEG-OPEN` through `NEG-CLOSE`), in milliseconds.
54
- * One clock for the whole session — not reset per `NEG-MSG`.
55
- * Default: the relay `publishTimeoutMs`.
56
- * Upload/download phases reuse this value as their per-call timeout.
57
- */
58
- timeoutMs?: number;
59
- signal?: AbortSignal;
60
- dryRun?: boolean;
61
- /** When false, skip observe/storage on downloaded events. Default true. */
62
- observe?: boolean;
63
- };
64
- type SyncSummary = {
65
- local: string[];
66
- remote: string[];
67
- sent: string[];
68
- received: string[];
69
- sendFailures: Record<string, string>;
70
- persistFailures: Record<string, string>;
71
- };
72
- type ClientOptions = {
73
- signer?: NostrSigner;
74
- relays?: readonly string[];
75
- websocketImplementation?: WebSocketConstructor;
76
- /** Injected EVENT verifier. Default is core BIP-340. */
77
- verifyEvent?: (event: Event) => boolean;
78
- connectTimeoutMs?: number;
79
- publishTimeoutMs?: number;
80
- /** When true (default if signer present), answer NIP-42 AUTH automatically. */
81
- automaticAuth?: boolean;
82
- /** When true (default), relays reconnect with backoff after disconnect. */
83
- enableReconnect?: boolean;
84
- /** Keepalive ping. Default false. */
85
- enablePing?: boolean;
86
- pingIntervalMs?: number;
87
- pingTimeoutMs?: number;
88
- gossip?: Gossip;
89
- /**
90
- * Local event store. Defaults to {@link MemoryEventStore}.
91
- * Browser apps that want persistence must pass {@link IndexedDbEventStore} and `await open()`.
92
- */
93
- storage?: EventStore;
94
- /**
95
- * When true (default), every ingested event is written to storage.
96
- * Set false to disable automatic persistence while keeping the store for manual use.
97
- */
98
- persistEvents?: boolean;
99
- /**
100
- * Storage I/O failures: live `putMany` flush and `fetchEvents({ localFirst: true })` query.
101
- * Those paths do not throw.
102
- */
103
- onstorageerror?: (err: StorageError) => void;
104
- };
105
- type FetchEventsOptions = {
106
- relays?: string[];
107
- timeoutMs?: number;
108
- signal?: AbortSignal;
109
- gossip?: boolean;
110
- /**
111
- * When true, query local storage first and merge with network results.
112
- * Defaults to false.
113
- */
114
- localFirst?: boolean;
115
- /** When false, skip writing fetched events to storage/observe. Default true. */
116
- observe?: boolean;
117
- };
118
- type SubscribeOptions = {
119
- relays?: string[];
120
- onevent?: (event: Event) => void;
121
- oneose?: () => void;
122
- onclose?: (reason: string) => void;
123
- signal?: AbortSignal;
124
- id?: string;
125
- /**
126
- * If set, fire `oneose` once after this many ms if not all relays have EOSEd.
127
- * Does not close the subscription.
128
- */
129
- eoseTimeoutMs?: number;
130
- /** Fan out REQs via NIP-65 gossip routes when available. */
131
- gossip?: boolean;
132
- /** When false, skip writing received events to storage/observe. Default true. */
133
- observe?: boolean;
134
- };
135
- type PublishOptions = {
136
- relays?: string[];
137
- timeoutMs?: number;
138
- gossip?: boolean;
139
- /** When false, skip writing the published event to storage/observe. Default true. */
140
- observe?: boolean;
141
- };
142
- type SendPrivateMessageOptions = {
143
- readonly subject?: string;
144
- readonly replyTo?: ReplyTo;
145
- readonly created_at?: number;
146
- readonly timeoutMs?: number;
147
- readonly observe?: boolean;
148
- };
149
- type PrivateMessageSendResult = {
150
- rumor: Rumor;
151
- wraps: ReadonlyArray<{
152
- recipient: string;
153
- wrap: Event;
154
- results: PoolPublishResult[];
155
- }>;
156
- };
157
- type ReceivedPrivateMessage = {
158
- wrap: Event;
159
- rumor: Rumor;
160
- };
161
- type FetchPrivateMessagesOptions = {
162
- readonly since?: number;
163
- readonly until?: number;
164
- readonly timeoutMs?: number;
165
- readonly signal?: AbortSignal;
166
- readonly observe?: boolean;
167
- };
168
- type SubscribePrivateMessagesOptions = {
169
- readonly since?: number;
170
- readonly onevent?: (msg: ReceivedPrivateMessage) => void;
171
- readonly oneose?: () => void;
172
- readonly onclose?: (reason: string) => void;
173
- readonly signal?: AbortSignal;
174
- readonly eoseTimeoutMs?: number;
175
- readonly observe?: boolean;
176
- };
177
- /** Client lifecycle, configuration, or abort failure (not cryptographic). */
178
- declare class ClientError extends NostrError {}
179
- //#endregion
180
- //#region src/client/client.d.ts
181
- /**
182
- * Layer-5 facade: signer + default relays + pool + loaders + gossip + event store.
183
- */
184
- declare class Client {
185
- #private;
186
- readonly pool: Pool;
187
- readonly loaders: Loaders;
188
- readonly gossip: Gossip;
189
- readonly storage: EventStore;
190
- onstorageerror: ((err: StorageError) => void) | null;
191
- constructor(opts?: ClientOptions);
192
- static builder(): ClientBuilder;
193
- get signer(): NostrSigner | undefined;
194
- get relays(): readonly string[];
195
- get isShutdown(): boolean;
196
- setSigner(signer: NostrSigner): void;
197
- addRelay(url: string): void;
198
- removeRelay(url: string): void;
199
- /** Connect all configured relays (best-effort; failures are ignored). */
200
- connect(opts?: {
201
- signal?: AbortSignal;
202
- }): Promise<void>;
203
- shutdown(): Promise<void>;
204
- /**
205
- * Unified ingest pipeline: gossip + replaceable loader cache immediately;
206
- * storage writes are coalesced into a single-flight `putMany`.
207
- */
208
- observe(event: Event): void;
209
- /** Observe many events (deduped by id, order preserved) as one persist batch. */
210
- observeAll(events: readonly Event[]): void;
211
- /**
212
- * Explicitly load kind:10002 and kind:10050 lists for pubkeys and feed gossip.
213
- * Does not background-fetch unknown authors on every subscribe (predictable DX).
214
- */
215
- hydrateGossip(pubkeys: readonly string[], opts?: {
216
- hints?: string[];
217
- force?: boolean;
218
- }): Promise<void>;
219
- /**
220
- * Create an outbox-model feed for the given authors (NIP-65 write relays).
221
- * Live events go through {@link observe}; sync persists via applySync.
222
- */
223
- outbox(opts: {
224
- authors: readonly string[];
225
- kinds?: readonly number[];
226
- onEvent?: (event: Event) => void;
227
- maxRelaysPerAuthor?: number;
228
- }): OutboxFeed;
229
- getPublicKey(): Promise<string>;
230
- signEvent(unsigned: UnsignedEvent): Promise<Event>;
231
- signEventBuilder(builder: EventBuilder): Promise<Event>;
232
- signTemplate(template: EventTemplate): Promise<Event>;
233
- /**
234
- * Sign (if builder) and publish to relays.
235
- * With `gossip: true`, prefers the author's NIP-65 outbox relays, tagged `p`
236
- * inboxes, and up to 5 `e`/`a` tag relay hints when known.
237
- * On any successful OK, observes the event into storage/gossip.
238
- */
239
- publish(eventOrBuilder: Event | EventBuilder, opts?: PublishOptions): Promise<PoolPublishResult[]>;
240
- /**
241
- * Fetch events. With `gossip: true`, routes filters by NIP-65 when possible.
242
- * With `localFirst: true`, merges storage hits with network results.
243
- */
244
- fetchEvents(filter: Filter | Filter[], opts?: FetchEventsOptions): Promise<Event[]>;
245
- /**
246
- * Query local storage only (no network).
247
- */
248
- queryLocal(filter: Filter | Filter[]): Promise<Event[]>;
249
- subscribe(filter: Filter | Filter[], opts?: SubscribeOptions): {
250
- close: (reason?: string) => void;
251
- };
252
- /** Publish a kind:10050 NIP-17 DM relay list (`relay` tags). Not kind 10002. */
253
- setDmRelays(relays: readonly string[], opts?: PublishOptions): Promise<PoolPublishResult[]>;
254
- sendPrivateMessage(recipients: string | Recipient | readonly (string | Recipient)[], content: string, opts?: SendPrivateMessageOptions): Promise<PrivateMessageSendResult>;
255
- fetchPrivateMessages(opts?: FetchPrivateMessagesOptions): Promise<ReceivedPrivateMessage[]>;
256
- subscribePrivateMessages(opts?: SubscribePrivateMessagesOptions): Promise<{
257
- close: (reason?: string) => void;
258
- }>;
259
- /**
260
- * NIP-77 sync against one relay: reconcile, then optionally upload
261
- * local-only events and/or download remote-only events.
262
- * `observe: false` skips putMany and ingestMeta; received ids are still listed.
263
- * `persistEvents: false` skips putMany, still ingestMeta when observe is on.
264
- */
265
- syncToRelay(url: string, filter: Filter, opts?: Omit<SyncOptions, "relays">): Promise<SyncSummary>;
266
- /**
267
- * NIP-77 sync against the given relays (or Client default relays).
268
- * Independent sessions run in parallel. Fulfilled summaries are merged;
269
- * if every relay rejects, throws the first rejection in URL order.
270
- */
271
- sync(filter: Filter, opts?: SyncOptions): Promise<SyncSummary>;
272
- }
273
- //#endregion
274
- export { FetchPrivateMessagesOptions as a, ReceivedPrivateMessage as c, SubscribePrivateMessagesOptions as d, SyncDirection as f, ClientBuilder as g, SyncSummary as h, FetchEventsOptions as i, SendPrivateMessageOptions as l, SyncOptions as m, ClientError as n, PrivateMessageSendResult as o, SyncDirectionName as p, ClientOptions as r, PublishOptions as s, Client as t, SubscribeOptions as u };
275
- //# sourceMappingURL=index-DgOugrpF.d.mts.map