@qntx/nostr 0.5.0 → 0.7.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 (178) 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-9tjI6WQ3.mjs → builder-CdLE4J6O.mjs} +11 -17
  4. package/dist/builder-CdLE4J6O.mjs.map +1 -0
  5. package/dist/{builder-sSflu6nB.d.mts → builder-DgkqDylK.d.mts} +5 -6
  6. package/dist/{client-BtIAtxrL.mjs → client-CILhdfsF.mjs} +104 -314
  7. package/dist/client-CILhdfsF.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/{error-BW7re1UN.d.mts → error-BD_0QFKT.d.mts} +2 -2
  15. package/dist/{error-CR8Urc84.d.mts → error-BquXDmIN.d.mts} +4 -4
  16. package/dist/{error-DrEIM7FO.mjs → error-L_QdmSnV.mjs} +2 -2
  17. package/dist/{error-DrEIM7FO.mjs.map → error-L_QdmSnV.mjs.map} +1 -1
  18. package/dist/{error-B__OMBRY.mjs → error-gtJvV6YS.mjs} +2 -2
  19. package/dist/{error-B__OMBRY.mjs.map → error-gtJvV6YS.mjs.map} +1 -1
  20. package/dist/{event-fU3Y01PR.d.mts → event-DJeb_DIL.d.mts} +4 -2
  21. package/dist/{event-D8Nfoh4K.mjs → event-DinQMmJ1.mjs} +16 -10
  22. package/dist/event-DinQMmJ1.mjs.map +1 -0
  23. package/dist/{filter-C0IMByE3.d.mts → filter-jI_9htF7.d.mts} +2 -2
  24. package/dist/{gossip-DpIvkBuj.d.mts → gossip-C7HLW5kI.d.mts} +35 -26
  25. package/dist/{gossip-oloDMWEj.mjs → gossip-CcTp9VpU.mjs} +23 -33
  26. package/dist/gossip-CcTp9VpU.mjs.map +1 -0
  27. package/dist/gossip.d.mts +1 -1
  28. package/dist/gossip.mjs +1 -1
  29. package/dist/{index-CvkvqB16.d.mts → index-DU9tRFTZ.d.mts} +7 -20
  30. package/dist/{index-q4MDce_r.d.mts → index-DiXwNn2M.d.mts} +6 -8
  31. package/dist/{index-QxvrpMfF.d.mts → index-DocgD9K0.d.mts} +92 -167
  32. package/dist/{index-D1bqFHKB.d.mts → index-InQZpHyQ.d.mts} +18 -18
  33. package/dist/{index-MxC28S4P.d.mts → index-Tx15HGAD.d.mts} +26 -28
  34. package/dist/{index-Dmc8FJgO.d.mts → index-iLT8UzY3.d.mts} +19 -37
  35. package/dist/index.d.mts +23 -21
  36. package/dist/index.mjs +21 -19
  37. package/dist/instance-BcKzxHIP.mjs +220 -0
  38. package/dist/instance-BcKzxHIP.mjs.map +1 -0
  39. package/dist/instance-DjsL8h84.d.mts +15 -0
  40. package/dist/{key-Cv4_pN3x.d.mts → key-D_5OgGT7.d.mts} +11 -7
  41. package/dist/{key-Bps-LqX7.mjs → key-bshqfmZL.mjs} +13 -9
  42. package/dist/key-bshqfmZL.mjs.map +1 -0
  43. package/dist/{loaders-o6YXB_Ri.mjs → loaders-DD0XAGzw.mjs} +73 -82
  44. package/dist/loaders-DD0XAGzw.mjs.map +1 -0
  45. package/dist/loaders.d.mts +3 -3
  46. package/dist/loaders.mjs +2 -2
  47. package/dist/{memory-C2EVx0_O.mjs → memory-D_-5-T31.mjs} +2 -2
  48. package/dist/memory-D_-5-T31.mjs.map +1 -0
  49. package/dist/{memory-index-Do1mz0WF.mjs → memory-index-C6rujtoc.mjs} +30 -247
  50. package/dist/memory-index-C6rujtoc.mjs.map +1 -0
  51. package/dist/message-CWLcFTQ1.mjs +194 -0
  52. package/dist/message-CWLcFTQ1.mjs.map +1 -0
  53. package/dist/{message-iGPIqnn0.d.mts → message-CbBZOhq3.d.mts} +3 -3
  54. package/dist/{nip17-CKIufV98.d.mts → nip17-cTDFfFVt.d.mts} +5 -6
  55. package/dist/{nip19-BevCJZsp.d.mts → nip19-D1OnJ192.d.mts} +3 -9
  56. package/dist/{nip46-DDV4dC56.d.mts → nip46-DrsPI8EX.d.mts} +3 -5
  57. package/dist/nip51-DoPUvQ--.d.mts +87 -0
  58. package/dist/{nip59-zQgdLcEr.d.mts → nip59-BFIihLYY.d.mts} +3 -4
  59. package/dist/nip65-52HiwiyD.d.mts +23 -0
  60. package/dist/{nip77-DH4RhS5r.d.mts → nip77-C9QcZn7b.d.mts} +3 -4
  61. package/dist/nips/blossom.d.mts +12 -8
  62. package/dist/nips/blossom.mjs +30 -9
  63. package/dist/nips/blossom.mjs.map +1 -1
  64. package/dist/nips/nip04.mjs +17 -7
  65. package/dist/nips/nip04.mjs.map +1 -1
  66. package/dist/nips/nip05.d.mts +2 -3
  67. package/dist/nips/nip05.mjs +1 -4
  68. package/dist/nips/nip05.mjs.map +1 -1
  69. package/dist/nips/nip10.d.mts +3 -3
  70. package/dist/nips/nip10.mjs +3 -3
  71. package/dist/nips/nip11.d.mts +1 -1
  72. package/dist/nips/nip11.mjs +4 -4
  73. package/dist/nips/nip11.mjs.map +1 -1
  74. package/dist/nips/nip13.d.mts +7 -7
  75. package/dist/nips/nip13.mjs +32 -10
  76. package/dist/nips/nip13.mjs.map +1 -1
  77. package/dist/nips/nip17.d.mts +1 -1
  78. package/dist/nips/nip17.mjs +13 -33
  79. package/dist/nips/nip17.mjs.map +1 -1
  80. package/dist/nips/nip19.d.mts +2 -2
  81. package/dist/nips/nip19.mjs +16 -28
  82. package/dist/nips/nip19.mjs.map +1 -1
  83. package/dist/nips/nip21.d.mts +6 -1
  84. package/dist/nips/nip21.mjs +15 -5
  85. package/dist/nips/nip21.mjs.map +1 -1
  86. package/dist/nips/nip27.d.mts +4 -4
  87. package/dist/nips/nip27.mjs.map +1 -1
  88. package/dist/nips/nip42.d.mts +1 -1
  89. package/dist/nips/nip42.mjs +2 -1
  90. package/dist/nips/nip42.mjs.map +1 -1
  91. package/dist/nips/nip44.mjs +1 -1
  92. package/dist/nips/nip46.d.mts +2 -2
  93. package/dist/nips/nip46.mjs +15 -19
  94. package/dist/nips/nip46.mjs.map +1 -1
  95. package/dist/nips/nip49.d.mts +5 -5
  96. package/dist/nips/nip49.mjs +4 -4
  97. package/dist/nips/nip49.mjs.map +1 -1
  98. package/dist/nips/nip51.d.mts +2 -86
  99. package/dist/nips/nip51.mjs +7 -23
  100. package/dist/nips/nip51.mjs.map +1 -1
  101. package/dist/nips/nip57.d.mts +7 -5
  102. package/dist/nips/nip57.mjs +12 -17
  103. package/dist/nips/nip57.mjs.map +1 -1
  104. package/dist/nips/nip59.d.mts +1 -1
  105. package/dist/nips/nip59.mjs +16 -16
  106. package/dist/nips/nip59.mjs.map +1 -1
  107. package/dist/nips/nip65.d.mts +2 -2
  108. package/dist/nips/nip65.mjs +14 -37
  109. package/dist/nips/nip65.mjs.map +1 -1
  110. package/dist/nips/nip77.d.mts +1 -1
  111. package/dist/nips/nip77.mjs +33 -32
  112. package/dist/nips/nip77.mjs.map +1 -1
  113. package/dist/nips/nip98.d.mts +5 -5
  114. package/dist/nips/nip98.mjs +8 -8
  115. package/dist/nips/nip98.mjs.map +1 -1
  116. package/dist/nostr_crypto_wasm_bg.wasm +0 -0
  117. package/dist/{pool-DBIYMtwy.d.mts → pool-SzJKPIa3.d.mts} +96 -91
  118. package/dist/pool-YHRZgcQ3.mjs +596 -0
  119. package/dist/pool-YHRZgcQ3.mjs.map +1 -0
  120. package/dist/put-DS0yzVJx.mjs +187 -0
  121. package/dist/put-DS0yzVJx.mjs.map +1 -0
  122. package/dist/{reactive-DFpqtIf_.d.mts → reactive-BJ1ggn4d.d.mts} +9 -22
  123. package/dist/{reactive-DDlThDSZ.mjs → reactive-BJRoVHxU.mjs} +113 -64
  124. package/dist/reactive-BJRoVHxU.mjs.map +1 -0
  125. package/dist/{pool-As20OGxB.mjs → relay-CvxPiQ_m.mjs} +178 -721
  126. package/dist/relay-CvxPiQ_m.mjs.map +1 -0
  127. package/dist/relay.d.mts +4 -4
  128. package/dist/relay.mjs +3 -2
  129. package/dist/serial-Bzpw-xz3.mjs +21 -0
  130. package/dist/serial-Bzpw-xz3.mjs.map +1 -0
  131. package/dist/{signer-CYQutDzD.mjs → signer-yW3slDC2.mjs} +12 -14
  132. package/dist/signer-yW3slDC2.mjs.map +1 -0
  133. package/dist/signer.d.mts +2 -2
  134. package/dist/signer.mjs +2 -2
  135. package/dist/{storage-Dky3wuoH.mjs → storage-CZ52ifcQ.mjs} +17 -42
  136. package/dist/storage-CZ52ifcQ.mjs.map +1 -0
  137. package/dist/storage.d.mts +3 -3
  138. package/dist/storage.mjs +4 -4
  139. package/dist/store.d.mts +1 -1
  140. package/dist/store.mjs +1 -1
  141. package/dist/testing.d.mts +18 -18
  142. package/dist/testing.mjs +9 -9
  143. package/dist/testing.mjs.map +1 -1
  144. package/dist/{types-2pCUprRU.d.mts → types-Cm-XMgtH.d.mts} +6 -6
  145. package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
  146. package/dist/{util-LfQirgDk.mjs → util-yFYzlbrm.mjs} +29 -7
  147. package/dist/util-yFYzlbrm.mjs.map +1 -0
  148. package/dist/{verifier-t5WoWbtY.mjs → verifier-BTLOpo1n.mjs} +3 -3
  149. package/dist/verifier-BTLOpo1n.mjs.map +1 -0
  150. package/dist/verifier-CgTFpgsC.d.mts +22 -0
  151. package/dist/wasm.browser.d.mts +8 -0
  152. package/dist/wasm.browser.mjs +19 -0
  153. package/dist/wasm.browser.mjs.map +1 -0
  154. package/dist/wasm.d.mts +3 -13
  155. package/dist/wasm.mjs +7 -208
  156. package/dist/wasm.mjs.map +1 -1
  157. package/package.json +13 -10
  158. package/dist/abort-Ba8tDrOI.mjs.map +0 -1
  159. package/dist/builder-9tjI6WQ3.mjs.map +0 -1
  160. package/dist/client-BtIAtxrL.mjs.map +0 -1
  161. package/dist/event-D8Nfoh4K.mjs.map +0 -1
  162. package/dist/gossip-oloDMWEj.mjs.map +0 -1
  163. package/dist/key-Bps-LqX7.mjs.map +0 -1
  164. package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
  165. package/dist/memory-C2EVx0_O.mjs.map +0 -1
  166. package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
  167. package/dist/message-DcY6Fi9o.mjs +0 -193
  168. package/dist/message-DcY6Fi9o.mjs.map +0 -1
  169. package/dist/nip65-CjgmdpPB.d.mts +0 -26
  170. package/dist/nips/nip96.d.mts +0 -40
  171. package/dist/nips/nip96.mjs +0 -118
  172. package/dist/nips/nip96.mjs.map +0 -1
  173. package/dist/pool-As20OGxB.mjs.map +0 -1
  174. package/dist/reactive-DDlThDSZ.mjs.map +0 -1
  175. package/dist/signer-CYQutDzD.mjs.map +0 -1
  176. package/dist/storage-Dky3wuoH.mjs.map +0 -1
  177. package/dist/util-LfQirgDk.mjs.map +0 -1
  178. package/dist/verifier-t5WoWbtY.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"gossip-oloDMWEj.mjs","names":[],"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 { normalizeURL } from \"../core/util.ts\";\nimport { parseDmRelayList } from \"../nips/nip17.ts\";\nimport { parseRelayList } from \"../nips/nip65.ts\";\nimport type { RelayListItem } from \"../nips/nip65.ts\";\n\n/** Relay routing state for one pubkey: NIP-65 outbox/inbox plus NIP-17 DM relays. */\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 | undefined;\n /** Event id of the last accepted kind:10050. */\n dmListId?: string | undefined;\n};\n\n/** A filter split by gossip routes: per-relay narrowed filters plus the unrouted remainder. */\nexport type RoutedFilter = {\n /** Url → already-narrowed filter. Empty when nothing routed. */\n perRelay: Map<string, Filter>;\n /**\n * Unrouted work for the caller: - no authors and no #p: original filter (today's \"generic\") -\n * authors/#p all unrouted: original filter (today's \"orphan\") - mixed: narrowed leftover (today's\n * fallback) - 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 * Max pubkeys with tracked routes; the map is LRU — writes and lookups refresh recency, and\n * inserting beyond the cap drops the oldest entry. Default 10_000.\n */\n maxPubkeys?: 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). Ingest replaceable list events,\n * then route filters into per-relay REQs.\n */\nexport class Gossip {\n readonly #routes = new Map<string, PubkeyRoutes>();\n readonly #maxRelays: number;\n readonly #maxPubkeys: number;\n\n constructor(opts: GossipOptions = {}) {\n this.#maxRelays = opts.maxRelaysPerPubkey ?? 4;\n this.#maxPubkeys = opts.maxPubkeys ?? 10_000;\n }\n\n /**\n * Ingest a routing list event. - kind:10002 → write/read (NIP-65) - 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: number = Math.floor(Date.now() / 1000),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#lookup(pk) ?? emptyRoutes();\n if (prev.updatedAt > updatedAt) {\n return false;\n }\n if (\n eventId !== undefined &&\n prev.relayListId !== undefined &&\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)) {\n write.push(url);\n }\n if (item.read && !read.includes(url)) {\n read.push(url);\n }\n }\n\n this.#put(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: ReadonlyArray<string>,\n updatedAt: number = Math.floor(Date.now() / 1000),\n eventId?: string,\n ): boolean {\n const pk = pubkey.toLowerCase();\n const prev = this.#lookup(pk) ?? emptyRoutes();\n if (prev.dmUpdatedAt > updatedAt) {\n return false;\n }\n if (\n eventId !== undefined &&\n prev.dmListId !== undefined &&\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)) {\n dm.push(url);\n }\n }\n\n this.#put(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.#lookup(pubkey.toLowerCase());\n }\n\n outboxRelays(pubkey: string): string[] {\n return this.#lookup(pubkey.toLowerCase())?.write ?? [];\n }\n\n inboxRelays(pubkey: string): string[] {\n return this.#lookup(pubkey.toLowerCase())?.read ?? [];\n }\n\n /** NIP-17 kind:10050 delivery relays for gift-wraps. */\n dmRelays(pubkey: string): string[] {\n return this.#lookup(pubkey.toLowerCase())?.dm ?? [];\n }\n\n clear(pubkey?: string): void {\n if (pubkey !== undefined && pubkey !== \"\") {\n this.#routes.delete(pubkey.toLowerCase());\n } else {\n this.#routes.clear();\n }\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. - authors → outbox relays,\n * filter narrowed per relay's authors - #p only → inbox relays of those pubkeys - neither / all\n * unrouted → remainder is the original filter - mixed → remainder is the narrowed leftover; all\n * 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 const { authors: _authors, ...base } = filter;\n return this.#breakAuthors(base, pTags, \"read\", (b, pubkeys) => ({ ...b, \"#p\": pubkeys }));\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.#lookup(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) {\n relays.add(u);\n }\n }\n if (relays.size === 0) {\n return { perRelay: new Map(), remainder: filter };\n }\n const map = new Map<string, Filter>();\n for (const url of relays) {\n map.set(url, { ...filter });\n }\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.#lookup(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) {\n return { perRelay: new Map(), remainder: filter };\n }\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 /** Read that refreshes LRU recency. */\n #lookup(pk: string): PubkeyRoutes | undefined {\n const routes = this.#routes.get(pk);\n if (routes !== undefined) {\n this.#routes.delete(pk);\n this.#routes.set(pk, routes);\n }\n return routes;\n }\n\n /** Write at the newest LRU end, then trim the oldest beyond the cap. */\n #put(pk: string, routes: PubkeyRoutes): void {\n this.#routes.delete(pk);\n this.#routes.set(pk, routes);\n while (this.#routes.size > this.#maxPubkeys) {\n const oldest = this.#routes.keys().next().value;\n if (oldest === undefined) {\n break;\n }\n this.#routes.delete(oldest);\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"key-Bps-LqX7.mjs","names":["hexToBytes","bytesToHex"],"sources":["../src/core/key.ts"],"sourcesContent":["import { schnorr } from \"@noble/curves/secp256k1.js\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\n\nimport { CryptoError } from \"./error.ts\";\nimport { getEventHash, markVerified, validateEvent } from \"./event.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"./event.ts\";\nimport { assertHex32, assertSecretKeyBytes, bytesToHex, hexToBytes } from \"./util.ts\";\n\n/** 32-byte secret key held as bytes; prefer zeroize when done. */\nexport class SecretKey {\n #bytes: Uint8Array | undefined;\n\n private constructor(bytes: Uint8Array) {\n assertSecretKeyBytes(bytes);\n this.#bytes = new Uint8Array(bytes);\n }\n\n static generate(): SecretKey {\n return new SecretKey(schnorr.utils.randomSecretKey());\n }\n\n static fromBytes(bytes: Uint8Array): SecretKey {\n return new SecretKey(bytes);\n }\n\n static fromHex(hex: string): SecretKey {\n return new SecretKey(hexToBytes(assertHex32(hex, \"secret key\")));\n }\n\n get bytes(): Uint8Array {\n if (!this.#bytes) {\n throw new CryptoError(\"secret key has been zeroized\");\n }\n return new Uint8Array(this.#bytes);\n }\n\n toHex(): string {\n return bytesToHex(this.bytes);\n }\n\n /** Best-effort wipe of the internal buffer. */\n zeroize(): void {\n if (this.#bytes) {\n this.#bytes.fill(0);\n this.#bytes = undefined;\n }\n }\n}\n\n/** Lowercase hex-encoded secp256k1 public key (x-only, 64 chars). */\nexport type PublicKey = string;\n\n/** Validate and normalize a hex public key; throws {@link HexError} on bad input. */\nexport function publicKeyFromHex(hex: string): PublicKey {\n return assertHex32(hex, \"public key\");\n}\n\n/**\n * BIP-340 signing backend. `publicKey` returns the 32-byte x-only public key of a 32-byte secret\n * key; `sign` returns the 64-byte BIP-340 signature of a 32-byte `id` using 32 bytes of auxiliary\n * randomness. Both throw on invalid input (for example an out-of-range secret key).\n */\nexport type SigningBackend = {\n publicKey: (secretKey: Uint8Array) => Uint8Array;\n sign: (id: Uint8Array, secretKey: Uint8Array, auxRand: Uint8Array) => Uint8Array;\n};\n\nconst nobleSigning: SigningBackend = {\n publicKey: (secretKey) => schnorr.getPublicKey(secretKey),\n sign: (id, secretKey, auxRand) => schnorr.sign(id, secretKey, auxRand),\n};\n\n/** Derive the public key for a secret key given as SecretKey, bytes, or hex. */\nexport function getPublicKey(secretKey: SecretKey | Uint8Array | string): PublicKey {\n const bytes =\n secretKey instanceof SecretKey\n ? secretKey.bytes\n : typeof secretKey === \"string\"\n ? hexToBytes(assertHex32(secretKey, \"secret key\"))\n : (assertSecretKeyBytes(secretKey), secretKey);\n return bytesToHex(schnorr.getPublicKey(bytes));\n}\n\n/** Keypair convenience wrapper. */\nexport class Keys {\n readonly secretKey: SecretKey;\n readonly publicKey: PublicKey;\n /**\n * BIP-340 backend that derived {@link publicKey} and signs in {@link finalizeEvent}/\n * {@link signEvent}. Defaults to noble; pass a loaded `NostrWasm` module or a native backend to\n * delegate signing.\n */\n readonly backend: SigningBackend;\n\n private constructor(secretKey: SecretKey, backend: SigningBackend) {\n this.secretKey = secretKey;\n this.backend = backend;\n const pubkey = backend.publicKey(secretKey.bytes);\n if (pubkey.length !== 32) {\n throw new CryptoError(\"signing backend returned an invalid public key\");\n }\n this.publicKey = bytesToHex(pubkey);\n }\n\n static generate(backend: SigningBackend = nobleSigning): Keys {\n return new Keys(SecretKey.generate(), backend);\n }\n\n static fromSecretKey(\n secretKey: SecretKey | Uint8Array | string,\n backend: SigningBackend = nobleSigning,\n ): Keys {\n const sk =\n secretKey instanceof SecretKey\n ? secretKey\n : typeof secretKey === \"string\"\n ? SecretKey.fromHex(secretKey)\n : SecretKey.fromBytes(secretKey);\n return new Keys(sk, backend);\n }\n}\n\nfunction resolveKeys(secretKey: SecretKey | Uint8Array | string | Keys): Keys {\n return secretKey instanceof Keys ? secretKey : Keys.fromSecretKey(secretKey);\n}\n\n/** Fill pubkey/id/sig on a template and return a signed event. */\nexport function finalizeEvent(\n template: EventTemplate,\n secretKey: SecretKey | Uint8Array | string | Keys,\n): Event {\n const keys = resolveKeys(secretKey);\n const unsigned: UnsignedEvent = {\n kind: template.kind,\n tags: template.tags,\n content: template.content,\n created_at: template.created_at,\n pubkey: keys.publicKey,\n };\n return signEvent(unsigned, keys);\n}\n\n/**\n * Sign an already-assembled unsigned event. Rejects when `unsigned.pubkey` does not match the\n * secret key.\n */\nexport function signEvent(\n unsigned: UnsignedEvent,\n secretKey: SecretKey | Uint8Array | string | Keys,\n): Event {\n const keys = resolveKeys(secretKey);\n if (!validateEvent(unsigned)) {\n throw new CryptoError(\"cannot sign invalid unsigned event\");\n }\n\n if (unsigned.pubkey !== keys.publicKey) {\n throw new CryptoError(\"unsigned event pubkey does not match secret key\");\n }\n\n const normalized: UnsignedEvent = {\n kind: unsigned.kind,\n tags: unsigned.tags,\n content: unsigned.content,\n created_at: unsigned.created_at,\n pubkey: keys.publicKey,\n };\n\n const id = getEventHash(normalized);\n const sig = keys.backend.sign(hexToBytes(id), keys.secretKey.bytes, randomBytes(32));\n if (sig.length !== 64) {\n throw new CryptoError(\"signing backend returned an invalid signature\");\n }\n const event: Event = { ...normalized, id, sig: bytesToHex(sig) };\n markVerified(event);\n return event;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"loaders-o6YXB_Ri.mjs","names":[],"sources":["../src/loaders/context.ts","../src/loaders/event.ts","../src/loaders/lists.ts","../src/loaders/profile.ts","../src/loaders/dataloader.ts","../src/loaders/replaceable.ts","../src/loaders/outbox.ts","../src/loaders/index.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\n\nexport type LoaderContextOptions = {\n pool: Pool;\n /** Fallback / discovery relays when no per-user routing is known. */\n relays: ReadonlyArray<string>;\n /** The reactive index loaders read from and feed fetched events into. */\n index: ReactiveEventStore;\n /**\n * Inbound-event sink for fetched events. Defaults to `index.add`; Client supplies its single\n * ingest path so loader fetches get gossip meta and persistence like every other inbound event.\n */\n ingest?: (event: Event, relayUrl: string) => void;\n /** Max age (seconds) before a fetched replaceable is considered stale. Default 2 days. */\n staleAfterSec?: number;\n fetchTimeoutMs?: number;\n};\n\n/** Internal dependency bag for loaders — never a module-level singleton. */\nexport class LoaderContext {\n readonly pool: Pool;\n readonly index: ReactiveEventStore;\n readonly ingest: (event: Event, relayUrl: string) => void;\n readonly staleAfterSec: number;\n readonly fetchTimeoutMs: number;\n #relays: string[];\n\n constructor(opts: LoaderContextOptions) {\n this.pool = opts.pool;\n this.#relays = [...opts.relays];\n this.index = opts.index;\n this.ingest = opts.ingest ?? ((event, relayUrl) => this.index.add(event, relayUrl));\n this.staleAfterSec = opts.staleAfterSec ?? 60 * 60 * 24 * 2;\n this.fetchTimeoutMs = opts.fetchTimeoutMs ?? 4400;\n }\n\n /** Snapshot of discovery/fallback relays. */\n get relays(): ReadonlyArray<string> {\n return this.#relays;\n }\n\n addRelay(url: string): void {\n if (!this.#relays.includes(url)) {\n this.#relays.push(url);\n }\n }\n\n removeRelay(url: string): void {\n this.#relays = this.#relays.filter((r) => r !== url);\n }\n\n setRelays(urls: ReadonlyArray<string>): void {\n this.#relays = [...urls];\n }\n\n isFresh(fetchedAt: number, now: number = Math.floor(Date.now() / 1000)): boolean {\n return now - fetchedAt < this.staleAfterSec;\n }\n}\n","import type { Event } from \"../core/event.ts\";\nimport { isAddressableKind } from \"../core/kind.ts\";\nimport { formatEventAddress } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\nimport { decode, Nip19Error } from \"../nips/nip19.ts\";\nimport type { AddressPointer, EventPointer } from \"../nips/nip19.ts\";\nimport type { LoaderContext } from \"./context.ts\";\n\nexport type EventRef = string | EventPointer | AddressPointer;\n\ntype ParsedRef = {\n filter: { ids?: string[]; authors?: string[]; kinds?: number[]; \"#d\"?: string[] };\n hints: string[];\n /** Resolve the fetched event from the index after the network round. */\n lookup: (ctx: LoaderContext) => Event | undefined;\n cacheKey: string;\n};\n\nfunction dFilter(kind: number, identifier: string): { \"#d\"?: string[] } {\n if (isAddressableKind(kind)) {\n return { \"#d\": [identifier] };\n }\n return {};\n}\n\nfunction addressRef(kind: number, pubkey: string, identifier: string): string {\n return formatEventAddress(kind, pubkey.toLowerCase(), identifier);\n}\n\nfunction parseRef(ref: EventRef): ParsedRef {\n if (typeof ref === \"string\") {\n if (isHex32(ref.toLowerCase())) {\n const id = ref.toLowerCase();\n return {\n filter: { ids: [id] },\n hints: [],\n lookup: (ctx) => ctx.index.get(id),\n cacheKey: `id:${id}`,\n };\n }\n const decoded = decode(ref);\n switch (decoded.type) {\n case \"note\":\n return {\n filter: { ids: [decoded.data] },\n hints: [],\n lookup: (ctx) => ctx.index.get(decoded.data),\n cacheKey: `id:${decoded.data}`,\n };\n case \"nevent\":\n return {\n filter: { ids: [decoded.data.id] },\n hints: decoded.data.relays ?? [],\n lookup: (ctx) => ctx.index.get(decoded.data.id),\n cacheKey: `id:${decoded.data.id}`,\n };\n case \"naddr\": {\n const addr = addressRef(decoded.data.kind, decoded.data.pubkey, decoded.data.identifier);\n return {\n filter: {\n authors: [decoded.data.pubkey.toLowerCase()],\n kinds: [decoded.data.kind],\n ...dFilter(decoded.data.kind, decoded.data.identifier),\n },\n hints: decoded.data.relays ?? [],\n lookup: (ctx) => ctx.index.getByAddress(addr),\n cacheKey: `addr:${addr}`,\n };\n }\n default:\n throw new Nip19Error(`cannot load event from ${decoded.type}`);\n }\n }\n if (\"id\" in ref) {\n const id = ref.id.toLowerCase();\n return {\n filter: { ids: [id] },\n hints: ref.relays ?? [],\n lookup: (ctx) => ctx.index.get(id),\n cacheKey: `id:${id}`,\n };\n }\n const addr = addressRef(ref.kind, ref.pubkey, ref.identifier);\n return {\n filter: {\n authors: [ref.pubkey.toLowerCase()],\n kinds: [ref.kind],\n ...dFilter(ref.kind, ref.identifier),\n },\n hints: ref.relays ?? [],\n lookup: (ctx) => ctx.index.getByAddress(addr),\n cacheKey: `addr:${addr}`,\n };\n}\n\n/**\n * Resolve an event reference (hex id, note, nevent, naddr, or pointer object) through the reactive\n * index, fetching from relays on a miss. The index is the durable store — the loader keeps only\n * in-flight coalescing, so a miss never blocks a later retry.\n */\nexport type EventLoader = {\n load: (ref: EventRef) => Promise<Event | undefined>;\n};\n\nexport function createEventLoader(ctx: LoaderContext): EventLoader {\n const inflight = new Map<string, Promise<Event | undefined>>();\n return {\n // oxlint-disable-next-line typescript/promise-function-async -- parseRef must throw synchronously on malformed refs\n load(ref: EventRef): Promise<Event | undefined> {\n const parsed = parseRef(ref);\n const hit = parsed.lookup(ctx);\n if (hit !== undefined) {\n return Promise.resolve(hit);\n }\n const pending = inflight.get(parsed.cacheKey);\n if (pending !== undefined) {\n return pending;\n }\n const p = (async (): Promise<Event | undefined> => {\n try {\n const relays = [...new Set([...parsed.hints, ...ctx.relays])];\n if (relays.length === 0) {\n return undefined;\n }\n await ctx.pool.fetch(relays, [parsed.filter], {\n timeoutMs: ctx.fetchTimeoutMs,\n onevent: (event, relayUrl) => ctx.ingest(event, relayUrl),\n });\n return parsed.lookup(ctx);\n } finally {\n inflight.delete(parsed.cacheKey);\n }\n })();\n inflight.set(parsed.cacheKey, p);\n return p;\n },\n };\n}\n","import type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { isHex32, normalizeURL } from \"../core/util.ts\";\nimport { parseDmRelayList } from \"../nips/nip17.ts\";\nimport type { RelayListItem } from \"../nips/nip65.ts\";\nimport { parseRelayList } from \"../nips/nip65.ts\";\nimport type { LoadStyle, ReplaceableLoader } from \"./replaceable.ts\";\n\n/** Result of a list loader: the source event, its decoded items, and freshness. */\nexport type ListResult<T> = {\n event: Event | undefined;\n items: T[];\n fresh: boolean;\n};\n\n/** One muted entity decoded from a kind:10000 mute list. */\nexport type MutedEntity =\n | { label: \"pubkey\"; value: string }\n | { label: \"thread\"; value: string }\n | { label: \"hashtag\"; value: string }\n | { label: \"word\"; value: string };\n\nfunction fromTags<T>(\n event: Event | undefined,\n map: (tag: ReadonlyArray<string>) => T | undefined,\n): T[] {\n if (event === undefined) {\n return [];\n }\n const out: T[] = [];\n for (const tag of event.tags) {\n const item = map(tag);\n if (item !== undefined) {\n out.push(item);\n }\n }\n return out;\n}\n\ntype ListLoaderOpts = { hints?: string[] | undefined; style?: LoadStyle | undefined };\n\nexport type ListLoaders = {\n follows: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<string>>;\n muteList: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<MutedEntity>>;\n relayList: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<RelayListItem>>;\n dmRelayList: (pubkey: string, opts?: ListLoaderOpts) => Promise<ListResult<string>>;\n};\n\nexport function createListLoaders(replaceable: (kind: number) => ReplaceableLoader): ListLoaders {\n const followsLoader = replaceable(Kind.Contacts);\n const muteLoader = replaceable(Kind.MuteList);\n const relayListLoader = replaceable(Kind.RelayList);\n const dmRelayListLoader = replaceable(Kind.DirectMessageRelaysList);\n\n return {\n async follows(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<string>> {\n const { event, fresh } = await followsLoader(pubkey, opts);\n return {\n event,\n fresh,\n items: fromTags(event, (tag) =>\n tag[0] === \"p\" && tag[1] !== undefined && isHex32(tag[1].toLowerCase())\n ? tag[1].toLowerCase()\n : undefined,\n ),\n };\n },\n\n async muteList(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<MutedEntity>> {\n const { event, fresh } = await muteLoader(pubkey, opts);\n return {\n event,\n fresh,\n items: fromTags(event, (tag) => {\n if (tag[1] === undefined || tag[1] === \"\") {\n return undefined;\n }\n switch (tag[0]) {\n case \"p\":\n return isHex32(tag[1].toLowerCase())\n ? { label: \"pubkey\", value: tag[1].toLowerCase() }\n : undefined;\n case \"e\":\n return isHex32(tag[1].toLowerCase())\n ? { label: \"thread\", value: tag[1].toLowerCase() }\n : undefined;\n case \"t\":\n return { label: \"hashtag\", value: tag[1] };\n case \"word\":\n return { label: \"word\", value: tag[1] };\n default:\n return undefined;\n }\n }),\n };\n },\n\n async relayList(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<RelayListItem>> {\n const { event, fresh } = await relayListLoader(pubkey, opts);\n let items: RelayListItem[] = [];\n if (event) {\n try {\n items = parseRelayList(event);\n } catch {\n items = [];\n }\n }\n // ensure normalized urls even if parse skipped\n items = items.map((i) => {\n try {\n return { ...i, url: normalizeURL(i.url) };\n } catch {\n return i;\n }\n });\n return { event, fresh, items };\n },\n\n async dmRelayList(pubkey: string, opts?: ListLoaderOpts): Promise<ListResult<string>> {\n const { event, fresh } = await dmRelayListLoader(pubkey, opts);\n let items: string[] = [];\n if (event) {\n try {\n items = parseDmRelayList(event);\n } catch {\n items = [];\n }\n }\n return { event, fresh, items };\n },\n };\n}\n","import type { ProfileMetadata } from \"../core/builder.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { npubEncode } from \"../nips/nip19.ts\";\nimport type { LoadStyle, ReplaceableLoader } from \"./replaceable.ts\";\n\n/** A profile-card view of a pubkey: identity fields plus the kind:0 event. */\nexport type NostrUser = {\n pubkey: string;\n npub: string;\n shortName: string;\n image?: string;\n metadata: ProfileMetadata;\n lastUpdated: number;\n event: Event | undefined;\n fresh: boolean;\n};\n\n/** A {@link NostrUser} with no metadata — used when no kind:0 event exists. */\nexport function bareNostrUser(pubkey: string): NostrUser {\n const pk = pubkey.toLowerCase();\n let npub: string;\n try {\n npub = npubEncode(pk);\n } catch {\n npub = pk;\n }\n return {\n pubkey: pk,\n npub,\n shortName: npub.startsWith(\"npub1\") ? `${npub.slice(0, 8)}…${npub.slice(-4)}` : pk.slice(0, 8),\n metadata: {},\n lastUpdated: 0,\n event: undefined,\n fresh: false,\n };\n}\n\nfunction nonEmpty(value: unknown): string | undefined {\n return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\nfunction parseMetadata(content: string): ProfileMetadata {\n try {\n const obj: unknown = JSON.parse(content);\n if (typeof obj !== \"object\" || obj === null) {\n return {};\n }\n return obj;\n } catch {\n return {};\n }\n}\n\nexport type ProfileLoader = {\n load: (\n pubkey: string,\n opts?: { hints?: string[] | undefined; style?: LoadStyle | undefined },\n ) => Promise<NostrUser>;\n};\n\nexport function createProfileLoader(\n replaceable: (kind: number) => ReplaceableLoader,\n): ProfileLoader {\n const loader = replaceable(Kind.Metadata);\n\n return {\n async load(\n pubkey: string,\n opts?: { hints?: string[] | undefined; style?: LoadStyle | undefined },\n ): Promise<NostrUser> {\n const base = bareNostrUser(pubkey);\n const { event, fresh } = await loader(pubkey, opts);\n if (!event) {\n return { ...base, fresh };\n }\n\n const metadata = parseMetadata(event.content);\n const display = nonEmpty(metadata.display_name) ?? nonEmpty(metadata.name);\n return {\n ...base,\n shortName: display ?? base.shortName,\n ...(metadata.picture === undefined ? {} : { image: metadata.picture }),\n metadata,\n lastUpdated: event.created_at,\n event,\n fresh,\n };\n },\n };\n}\n","import { NostrError } from \"../core/error.ts\";\n\nexport class LoaderError extends NostrError {\n override name = \"LoaderError\";\n}\n\n/** Minimal request coalescer. Batches keys within a microtask; no global state. */\nexport type BatchLoadFn<K, V> = (keys: ReadonlyArray<K>) => Promise<ReadonlyArray<V | Error>>;\n\nexport type DataLoaderOptions<K, C = K> = {\n cacheKeyFn?: (key: K) => C;\n maxBatchSize?: number;\n /** When false, only coalesces in-flight requests (no durable memoization). Default true. */\n cache?: boolean;\n};\n\nexport class DataLoader<K, V, C = K> {\n readonly #batchLoadFn: BatchLoadFn<K, V>;\n readonly #cacheKeyFn: (key: K) => C;\n readonly #maxBatchSize: number;\n readonly #useCache: boolean;\n readonly #cache = new Map<C, Promise<V>>();\n readonly #inflight = new Map<C, Promise<V>>();\n readonly #queue: Array<{\n key: K;\n resolve: (v: V) => void;\n reject: (e: unknown) => void;\n }> = [];\n #scheduled = false;\n\n constructor(batchLoadFn: BatchLoadFn<K, V>, options: DataLoaderOptions<K, C> = {}) {\n this.#batchLoadFn = batchLoadFn;\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the default C = K makes this the identity for callers that omit cacheKeyFn\n this.#cacheKeyFn = options.cacheKeyFn ?? ((k: K) => k as unknown as C);\n this.#maxBatchSize = options.maxBatchSize ?? Number.POSITIVE_INFINITY;\n this.#useCache = options.cache !== false;\n }\n\n async load(key: K): Promise<V> {\n const cacheKey = this.#cacheKeyFn(key);\n if (this.#useCache) {\n const cached = this.#cache.get(cacheKey);\n if (cached) {\n return cached;\n }\n }\n const inflight = this.#inflight.get(cacheKey);\n if (inflight) {\n return inflight;\n }\n\n const pending = new Promise<V>((resolve, reject) => {\n this.#queue.push({ key, resolve, reject });\n if (this.#queue.length >= this.#maxBatchSize) {\n this.#dispatch();\n } else if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => this.#dispatch());\n }\n });\n const promise = (async (): Promise<V> => {\n try {\n return await pending;\n } finally {\n this.#inflight.delete(cacheKey);\n }\n })();\n\n this.#inflight.set(cacheKey, promise);\n if (this.#useCache) {\n this.#cache.set(cacheKey, promise);\n }\n return promise;\n }\n\n clear(key: K): void {\n this.#cache.delete(this.#cacheKeyFn(key));\n this.#inflight.delete(this.#cacheKeyFn(key));\n }\n\n clearAll(): void {\n this.#cache.clear();\n this.#inflight.clear();\n }\n\n #dispatch(): void {\n this.#scheduled = false;\n const batch = this.#queue.splice(0, this.#maxBatchSize);\n if (batch.length === 0) {\n return;\n }\n\n const keys = batch.map((b) => b.key);\n void (async () => {\n let values: ReadonlyArray<V | Error>;\n try {\n values = await this.#batchLoadFn(keys);\n } catch (error: unknown) {\n for (const item of batch) {\n item.reject(error);\n }\n return;\n }\n if (values.length !== keys.length) {\n const err = new LoaderError(\n `DataLoader batch function must return array of length ${keys.length}, got ${values.length}`,\n );\n for (const item of batch) {\n item.reject(err);\n }\n return;\n }\n for (const [i, value] of values.entries()) {\n const item = batch[i];\n if (item === undefined) {\n continue;\n }\n if (value instanceof Error) {\n item.reject(value);\n } else {\n item.resolve(value);\n }\n }\n })();\n\n if (this.#queue.length > 0) {\n this.#scheduled = true;\n queueMicrotask(() => this.#dispatch());\n }\n }\n}\n","import type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { formatEventAddress } from \"../core/tag.ts\";\nimport type { LoaderContext } from \"./context.ts\";\nimport { DataLoader } from \"./dataloader.ts\";\n\n/** Result of a replaceable load: the winning event (or none) and freshness. */\nexport type ReplaceableLoadResult = {\n event: Event | undefined;\n fresh: boolean;\n};\n\n/** `default`: fetch when stale or unfetched; `force`: always fetch; `cache-only`: index only. */\nexport type LoadStyle = \"default\" | \"force\" | \"cache-only\";\n\n/** Loads the winning replaceable/addressable event of one kind for a pubkey. */\nexport type ReplaceableLoader = (\n pubkey: string,\n opts?: { hints?: string[] | undefined; style?: LoadStyle | undefined },\n) => Promise<ReplaceableLoadResult>;\n\n/** FIFO cap on per-address fetch records (misses included). */\nconst MAX_FETCHED_AT = 10_000;\n\n/**\n * Address -> last network fetch. `hit` is true when the index held a winner right after that fetch;\n * a fresh `hit` record whose winner was evicted since must refetch instead of reporting a miss.\n */\ntype FetchedAt = Map<string, { at: number; hit: boolean }>;\n\n/**\n * Batch-fetch replaceable events (kind + authors) via the pool. Fetched events are written into the\n * reactive index (`onevent` records `seenOn`) and the winner is read back from it; the index — not\n * the loader — owns the stored value and winner selection. The loader only tracks per-address\n * `fetchedAt` timestamps so a recent fetch (including a miss) is not repeated within\n * `staleAfterSec`.\n */\nexport function createReplaceableLoader(ctx: LoaderContext, kind: number): ReplaceableLoader {\n type Key = { pubkey: string; hints?: string[] | undefined };\n\n const fetchedAt: FetchedAt = new Map();\n const markFetched = (addr: string, hit: boolean, now: number): void => {\n fetchedAt.delete(addr);\n fetchedAt.set(addr, { at: now, hit });\n while (fetchedAt.size > MAX_FETCHED_AT) {\n const oldest = fetchedAt.keys().next().value;\n if (oldest === undefined) {\n break;\n }\n fetchedAt.delete(oldest);\n }\n };\n\n const loader = new DataLoader<Key, ReplaceableLoadResult, string>(\n async (keys) => {\n const authors = [...new Set(keys.map((k) => k.pubkey))];\n const relays = [...new Set([...keys.flatMap((k) => k.hints ?? []), ...ctx.relays])];\n if (relays.length > 0) {\n const filter: Filter = { kinds: [kind], authors };\n await ctx.pool.fetch(relays, [filter], {\n timeoutMs: ctx.fetchTimeoutMs,\n onevent: (event, relayUrl) => ctx.ingest(event, relayUrl),\n });\n }\n const now = Math.floor(Date.now() / 1000);\n return keys.map((k) => {\n const addr = formatEventAddress(kind, k.pubkey, \"\");\n const event = ctx.index.getByAddress(addr);\n // Record the fetch even when nothing came back: a recent miss must\n // not be retried on every `default` load.\n markFetched(addr, event !== undefined, now);\n return { event, fresh: true };\n });\n },\n {\n cache: false,\n cacheKeyFn: (k) => `${kind}:${k.pubkey}:${(k.hints ?? []).join(\",\")}`,\n maxBatchSize: 50,\n },\n );\n\n return async (pubkey, opts) => {\n const pk = pubkey.toLowerCase();\n const address = formatEventAddress(kind, pk, \"\");\n const style = opts?.style ?? \"default\";\n const current = (): Event | undefined => ctx.index.getByAddress(address);\n\n if (style === \"cache-only\") {\n return { event: current(), fresh: false };\n }\n if (style === \"default\") {\n const rec = fetchedAt.get(address);\n const event = current();\n // A fresh record short-circuits the fetch, unless it was a hit whose\n // winner has since been evicted — that would report a stale miss.\n if (rec !== undefined && ctx.isFresh(rec.at) && (!rec.hit || event !== undefined)) {\n return { event, fresh: false };\n }\n }\n return loader.load({ pubkey: pk, hints: opts?.hints });\n };\n}\n","import { throwIfAborted } from \"../core/abort.ts\";\nimport { NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { sortedEvents } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport { toStorageError } from \"../storage/error.ts\";\nimport type { EventStore, OutboxBound } from \"../storage/types.ts\";\n\nexport type { OutboxBound } from \"../storage/types.ts\";\n\n/** Outbox feed lifecycle error (e.g. use after close). */\nexport class OutboxError extends NostrError {\n override name = \"OutboxError\";\n}\n\n/** Options for {@link createOutboxFeed}. */\nexport type OutboxFeedOptions = {\n pool: Pool;\n gossip: Gossip;\n storage: EventStore;\n /** Fallback relays when an author has no known outbox routes. */\n discoveryRelays: ReadonlyArray<string>;\n authors: ReadonlyArray<string>;\n /** Event kinds to fetch. Default `[Kind.TextNote]`. */\n kinds?: ReadonlyArray<number> | undefined;\n /** Max write-relays to use per author. Default 3. */\n maxRelaysPerAuthor?: number | undefined;\n fetchTimeoutMs?: number | undefined;\n /** Called for live events after observe, and for sync events returned by applySync. */\n onEvent?: ((event: Event) => void) | undefined;\n /** Live ingest (e.g. `client.observe`) with the source relay URL. StartLive never putManys. */\n observe?: ((event: Event, relayUrl: string) => void) | undefined;\n /**\n * Every receipt from every relay — live (including duplicates the feed's own dedupe skips) and\n * sync fetch batches — for seenOn bookkeeping.\n */\n seen?: ((event: Event, relayUrl: string) => void) | undefined;\n /**\n * Sync path only. Awaited. Return events that should advance bounds. Throw → sync throws, bounds\n * unchanged.\n */\n applySync?: ((events: ReadonlyArray<Event>) => Promise<ReadonlyArray<Event>>) | undefined;\n /** Load kind:10002 for authors before sync when routes are missing. Requires a relay-list loader. */\n hydrate?: ((pubkeys: ReadonlyArray<string>) => Promise<void>) | undefined;\n};\n\nfunction boundKey(pubkey: string, kind: number): string {\n return `${pubkey.toLowerCase()}:${kind}`;\n}\n\n/** Same skip/dedup as Gossip.setRoutes so prefer can match Client.relays. */\nfunction canonicalRelayUrls(urls: ReadonlyArray<string>): string[] {\n const out: string[] = [];\n for (const raw of urls) {\n try {\n const url = normalizeURL(raw);\n if (!out.includes(url)) {\n out.push(url);\n }\n } catch {\n // not a relay URL\n }\n }\n return out;\n}\n\n/**\n * Group authors by outbox relay (discovery fallback). `prefer` reorders existing candidates only;\n * it never appends a URL that is not already in the author's outbox or discovery list. Returns\n * Map<relayUrl, authors[]>.\n */\nexport function groupAuthorsByOutboxRelay(\n authors: ReadonlyArray<string>,\n gossip: Gossip,\n discoveryRelays: ReadonlyArray<string>,\n maxRelaysPerAuthor = 3,\n prefer: ReadonlyArray<string> = [],\n): Map<string, string[]> {\n const leftover = new Set(\n (gossip.route({ authors: authors.map((a) => a.toLowerCase()) }).remainder?.authors ?? []).map(\n (a) => a.toLowerCase(),\n ),\n );\n const preferSet = new Set(canonicalRelayUrls(prefer));\n const discovery = canonicalRelayUrls(discoveryRelays);\n const map = new Map<string, string[]>();\n\n for (const author of authors) {\n const pk = author.toLowerCase();\n const urls = leftover.has(pk) ? discovery : gossip.outboxRelays(pk);\n const preferred: string[] = [];\n const rest: string[] = [];\n for (const url of urls) {\n if (preferSet.has(url)) {\n preferred.push(url);\n } else {\n rest.push(url);\n }\n }\n for (const url of [...preferred, ...rest].slice(0, maxRelaysPerAuthor)) {\n const list = map.get(url) ?? [];\n if (!list.includes(pk)) {\n list.push(pk);\n }\n map.set(url, list);\n }\n }\n return map;\n}\n\n/**\n * Outbox-model feed: history sync + live subscription for a set of authors, routed via NIP-65 write\n * relays (with discovery fallback).\n */\nexport class OutboxFeed {\n readonly #pool: Pool;\n readonly #gossip: Gossip;\n readonly #storage: EventStore;\n readonly #discovery: string[];\n readonly #kinds: number[];\n readonly #maxRelays: number;\n readonly #timeoutMs: number;\n readonly #onEvent: ((event: Event) => void) | undefined;\n readonly #observe: ((event: Event, relayUrl: string) => void) | undefined;\n readonly #seen: ((event: Event, relayUrl: string) => void) | undefined;\n readonly #applySync:\n | ((events: ReadonlyArray<Event>) => Promise<ReadonlyArray<Event>>)\n | undefined;\n readonly #hydrate: ((pubkeys: ReadonlyArray<string>) => Promise<void>) | undefined;\n readonly #bounds = new Map<string, OutboxBound>();\n #authors: string[];\n #liveCloser: { close: (reason?: string) => void } | undefined;\n #closed = false;\n\n constructor(opts: OutboxFeedOptions) {\n this.#pool = opts.pool;\n this.#gossip = opts.gossip;\n this.#storage = opts.storage;\n this.#discovery = [...opts.discoveryRelays];\n this.#authors = [...new Set(opts.authors.map((a) => a.toLowerCase()))];\n this.#kinds = [...(opts.kinds ?? [Kind.TextNote])];\n this.#maxRelays = opts.maxRelaysPerAuthor ?? 3;\n this.#timeoutMs = opts.fetchTimeoutMs ?? 4400;\n this.#onEvent = opts.onEvent;\n this.#observe = opts.observe;\n this.#seen = opts.seen;\n this.#applySync = opts.applySync;\n this.#hydrate = opts.hydrate;\n }\n\n get authors(): ReadonlyArray<string> {\n return this.#authors;\n }\n\n get kinds(): ReadonlyArray<number> {\n return this.#kinds;\n }\n\n /** Replace the author set (does not auto-restart live). */\n setAuthors(authors: ReadonlyArray<string>): void {\n this.#authors = [...new Set(authors.map((a) => a.toLowerCase()))];\n }\n\n getBound(pubkey: string, kind: number): OutboxBound | undefined {\n return this.#bounds.get(boundKey(pubkey, kind));\n }\n\n /** Load NIP-65 relay lists for authors that have no outbox routes yet. */\n async hydrate(): Promise<void> {\n this.#assertOpen();\n if (!this.#hydrate) {\n return;\n }\n const missing = this.#authors.filter((pk) => this.#gossip.outboxRelays(pk).length === 0);\n if (missing.length === 0) {\n return;\n }\n await this.#hydrate(missing);\n }\n\n /** One-shot history pull. Persistence is `applySync`; bounds move only for events it returns. */\n async sync(opts?: {\n limit?: number | undefined;\n since?: number | undefined;\n until?: number | undefined;\n signal?: AbortSignal | undefined;\n /** Skip hydrate() even if provided. */\n skipHydrate?: boolean | undefined;\n }): Promise<Event[]> {\n this.#assertOpen();\n if (opts?.skipHydrate !== true) {\n await this.hydrate();\n }\n await this.#hydrateBoundsFromStore();\n\n const byRelay = groupAuthorsByOutboxRelay(\n this.#authors,\n this.#gossip,\n this.#discovery,\n this.#maxRelays,\n this.#pool.connectedUrls(),\n );\n\n if (byRelay.size === 0) {\n return [];\n }\n\n const byId = new Map<string, Event>();\n await Promise.all(\n [...byRelay.entries()].map(async ([url, authors]) => {\n throwIfAborted(opts?.signal);\n\n const filters = this.#syncFilters(authors, opts);\n try {\n const batch = await this.#pool.fetch([url], filters, {\n timeoutMs: this.#timeoutMs,\n signal: opts?.signal,\n onevent: (event, relayUrl) => invokeSafely(() => this.#seen?.(event, relayUrl)),\n });\n for (const event of batch) {\n byId.set(event.id, event);\n }\n } catch (error) {\n // An abort rejects the whole sync; per-relay failures are skipped.\n if (opts?.signal?.aborted === true) {\n throw error;\n }\n }\n }),\n );\n\n const unique = [...byId.values()];\n if (unique.length === 0) {\n return [];\n }\n\n let applied: ReadonlyArray<Event> = [];\n if (this.#applySync) {\n try {\n applied = await this.#applySync(unique);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n const dirty = new Set<string>();\n for (const event of applied) {\n this.#updateBounds(event);\n dirty.add(boundKey(event.pubkey, event.kind));\n invokeSafely(() => this.#onEvent?.(event));\n }\n await this.#persistBounds(dirty);\n return sortedEvents(unique);\n }\n\n /**\n * Start a live subscription across outbox relays for the current authors. Closes any previous\n * live subscription.\n */\n startLive(opts?: { signal?: AbortSignal | undefined; since?: number | undefined }): {\n close: (reason?: string) => void;\n } {\n this.#assertOpen();\n this.#liveCloser?.close(\"restart\");\n\n const byRelay = groupAuthorsByOutboxRelay(\n this.#authors,\n this.#gossip,\n this.#discovery,\n this.#maxRelays,\n this.#pool.connectedUrls(),\n );\n\n const seen = new Set<string>();\n const closers: Array<{ close: (reason?: string) => void }> = [];\n const since = opts?.since ?? Math.floor(Date.now() / 1000) - 60;\n\n for (const [url, authors] of byRelay) {\n const filter: Filter = {\n authors,\n kinds: this.#kinds,\n since,\n };\n closers.push(\n this.#pool.subscribe([url], [filter], {\n signal: opts?.signal,\n onevent: (event, relayUrl) => {\n invokeSafely(() => this.#seen?.(event, relayUrl));\n if (seen.has(event.id)) {\n return;\n }\n seen.add(event.id);\n this.#noteEvent(event, relayUrl);\n },\n }),\n );\n }\n\n const closer = {\n close: (reason?: string) => {\n for (const c of closers) {\n c.close(reason);\n }\n if (this.#liveCloser === closer) {\n this.#liveCloser = undefined;\n }\n },\n };\n this.#liveCloser = closer;\n return closer;\n }\n\n /** Convenience: hydrate + sync + startLive. */\n async start(opts?: {\n limit?: number | undefined;\n signal?: AbortSignal | undefined;\n }): Promise<{ close: (reason?: string) => void; events: Event[] }> {\n const events = await this.sync({ limit: opts?.limit, signal: opts?.signal });\n const live = this.startLive({ signal: opts?.signal });\n return { close: (r) => live.close(r), events };\n }\n\n close(): void {\n this.#closed = true;\n this.#liveCloser?.close(\"feed closed\");\n this.#liveCloser = undefined;\n }\n\n #assertOpen(): void {\n if (this.#closed) {\n throw new OutboxError(\"OutboxFeed is closed\");\n }\n }\n\n async #hydrateBoundsFromStore(): Promise<void> {\n await Promise.all(\n this.#authors.flatMap((pk) =>\n this.#kinds.map(async (kind) => {\n const key = boundKey(pk, kind);\n if (this.#bounds.has(key)) {\n return;\n }\n const bound = await this.#storage.getOutboxBound(pk, kind);\n if (!bound) {\n return;\n }\n await this.#storage.setOutboxBound(pk, kind, bound);\n this.#bounds.set(key, { oldest: bound.oldest, newest: bound.newest });\n }),\n ),\n );\n }\n\n #syncFilters(\n authors: string[],\n opts?: { limit?: number | undefined; since?: number | undefined; until?: number | undefined },\n ): Filter[] {\n const base: Filter = {\n kinds: this.#kinds,\n limit: opts?.limit ?? 50,\n ...(opts?.until === undefined ? {} : { until: opts.until }),\n };\n\n if (opts?.since !== undefined) {\n return [{ ...base, authors, since: opts.since }];\n }\n\n const bounded: string[] = [];\n const unbounded: string[] = [];\n let minBoundedNewest: number | undefined;\n\n for (const pk of authors) {\n let newest: number | undefined;\n let complete = true;\n for (const kind of this.#kinds) {\n const b = this.#bounds.get(boundKey(pk, kind));\n if (!b) {\n complete = false;\n break;\n }\n newest = Math.min(newest ?? b.newest, b.newest);\n }\n if (complete && newest !== undefined) {\n bounded.push(pk);\n if (minBoundedNewest === undefined || newest < minBoundedNewest) {\n minBoundedNewest = newest;\n }\n } else {\n unbounded.push(pk);\n }\n }\n\n if (bounded.length === 0 || minBoundedNewest === undefined) {\n return [{ ...base, authors }];\n }\n if (unbounded.length === 0) {\n return [{ ...base, authors, since: Math.max(0, minBoundedNewest - 1) }];\n }\n return [\n { ...base, authors: bounded, since: Math.max(0, minBoundedNewest - 1) },\n { ...base, authors: unbounded },\n ];\n }\n\n #noteEvent(event: Event, relayUrl: string): void {\n this.#updateBounds(event);\n const bound = this.#bounds.get(boundKey(event.pubkey, event.kind));\n if (bound) {\n void (async () => {\n try {\n await this.#storage.setOutboxBound(event.pubkey, event.kind, {\n oldest: bound.oldest,\n newest: bound.newest,\n });\n } catch {\n // Bound persistence is best-effort.\n }\n })();\n }\n invokeSafely(() => this.#observe?.(event, relayUrl));\n invokeSafely(() => this.#onEvent?.(event));\n }\n\n async #persistBounds(keys: Iterable<string>): Promise<void> {\n const writes: Array<Promise<void>> = [];\n for (const key of keys) {\n const bound = this.#bounds.get(key);\n if (!bound) {\n continue;\n }\n const sep = key.lastIndexOf(\":\");\n writes.push(\n this.#storage.setOutboxBound(key.slice(0, sep), Number(key.slice(sep + 1)), {\n oldest: bound.oldest,\n newest: bound.newest,\n }),\n );\n }\n if (writes.length === 0) {\n return;\n }\n try {\n await Promise.all(writes);\n } catch (error) {\n throw toStorageError(error);\n }\n }\n\n #updateBounds(event: Event): void {\n const key = boundKey(event.pubkey, event.kind);\n const prev = this.#bounds.get(key);\n if (!prev) {\n this.#bounds.set(key, { oldest: event.created_at, newest: event.created_at });\n return;\n }\n if (event.created_at < prev.oldest) {\n prev.oldest = event.created_at;\n }\n if (event.created_at > prev.newest) {\n prev.newest = event.created_at;\n }\n }\n}\n\n/** Factory matching other loaders helpers. */\nexport function createOutboxFeed(opts: OutboxFeedOptions): OutboxFeed {\n return new OutboxFeed(opts);\n}\n","import type { Event } from \"../core/event.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\nimport { LoaderContext } from \"./context.ts\";\nimport { createEventLoader } from \"./event.ts\";\nimport type { EventLoader } from \"./event.ts\";\nimport { createListLoaders } from \"./lists.ts\";\nimport type { ListLoaders } from \"./lists.ts\";\nimport { createProfileLoader } from \"./profile.ts\";\nimport type { ProfileLoader } from \"./profile.ts\";\nimport { createReplaceableLoader } from \"./replaceable.ts\";\nimport type { ReplaceableLoader } from \"./replaceable.ts\";\n\nexport {\n type LoadStyle,\n type ReplaceableLoader,\n type ReplaceableLoadResult,\n} from \"./replaceable.ts\";\nexport { type ListResult, type ListLoaders, type MutedEntity } from \"./lists.ts\";\nexport { bareNostrUser, type NostrUser, type ProfileLoader } from \"./profile.ts\";\nexport { type EventLoader, type EventRef } from \"./event.ts\";\nexport {\n OutboxError,\n OutboxFeed,\n createOutboxFeed,\n groupAuthorsByOutboxRelay,\n type OutboxBound,\n type OutboxFeedOptions,\n} from \"./outbox.ts\";\n\n/** The loader surface returned by {@link createLoaders}. */\nexport type Loaders = {\n follows: ListLoaders[\"follows\"];\n muteList: ListLoaders[\"muteList\"];\n relayList: ListLoaders[\"relayList\"];\n dmRelayList: ListLoaders[\"dmRelayList\"];\n profile: ProfileLoader[\"load\"];\n event: EventLoader[\"load\"];\n /** Generic replaceable loader for any kind; memoized per kind. */\n replaceable: (kind: number) => ReplaceableLoader;\n addRelay: (url: string) => void;\n removeRelay: (url: string) => void;\n};\n\n/** Options for {@link createLoaders}. */\nexport type CreateLoadersOptions = {\n pool: Pool;\n relays: ReadonlyArray<string>;\n /** The reactive index loaders read from and feed fetched events into. */\n index: ReactiveEventStore;\n /**\n * Inbound-event sink for fetched events; defaults to `index.add`. Client wires its single ingest\n * path so loader fetches get gossip meta and persistence like every other inbound event.\n */\n ingest?: (event: Event, relayUrl: string) => void;\n staleAfterSec?: number;\n fetchTimeoutMs?: number;\n};\n\n/** Build an instance-scoped loader suite (no module globals). */\nexport function createLoaders(opts: CreateLoadersOptions): Loaders {\n const context = new LoaderContext(opts);\n const byKind = new Map<number, ReplaceableLoader>();\n const replaceable = (kind: number): ReplaceableLoader => {\n let loader = byKind.get(kind);\n if (loader === undefined) {\n loader = createReplaceableLoader(context, kind);\n byKind.set(kind, loader);\n }\n return loader;\n };\n const lists = createListLoaders(replaceable);\n const profile = createProfileLoader(replaceable);\n const event = createEventLoader(context);\n return {\n follows: async (pubkey, o) => lists.follows(pubkey, o),\n muteList: async (pubkey, o) => lists.muteList(pubkey, o),\n relayList: async (pubkey, o) => lists.relayList(pubkey, o),\n dmRelayList: async (pubkey, o) => lists.dmRelayList(pubkey, o),\n profile: async (pubkey, o) => profile.load(pubkey, o),\n // oxlint-disable-next-line typescript/promise-function-async -- must pass through load's synchronous throw on malformed refs\n event: (ref) => event.load(ref),\n replaceable,\n addRelay: (url) => context.addRelay(url),\n removeRelay: (url) => context.removeRelay(url),\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"memory-C2EVx0_O.mjs","names":[],"sources":["../src/storage/memory.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { MemoryIndex } from \"./memory-index.ts\";\nimport type { EventStore, NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\n/** In-memory event store: async {@link EventStore} facade over the synchronous {@link MemoryIndex}. */\nexport class MemoryEventStore implements EventStore {\n readonly #index = new MemoryIndex();\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async put(raw: Event): Promise<PutResult> {\n return this.#index.put(raw);\n }\n\n /** Sequential `put` in input order. No transaction: a throw leaves earlier events applied. */\n async putMany(events: ReadonlyArray<Event>): Promise<PutResult[]> {\n const results: PutResult[] = [];\n for (const event of events) {\n // oxlint-disable-next-line no-await-in-loop -- putMany applies puts sequentially in input order\n results.push(await this.put(event));\n }\n return results;\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async get(id: string): Promise<Event | undefined> {\n return this.#index.get(id);\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async query(filters: Filter[]): Promise<Event[]> {\n return this.#index.query(filters);\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async count(filters: Filter[]): Promise<number> {\n return this.#index.count(filters);\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async negentropyItems(filter: Filter): Promise<NegentropyItem[]> {\n return this.#index.negentropyItems(filter);\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async remove(ids: string[]): Promise<number> {\n return this.#index.remove(ids);\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async getOutboxBound(pubkey: string, kind: number): Promise<OutboxBound | undefined> {\n return this.#index.getOutboxBound(pubkey, kind);\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async setOutboxBound(pubkey: string, kind: number, bound: OutboxBound): Promise<void> {\n this.#index.setOutboxBound(pubkey, kind, bound);\n }\n\n // oxlint-disable-next-line typescript/require-await -- EventStore is async; MemoryIndex is synchronous\n async clear(): Promise<void> {\n this.#index.clear();\n }\n\n get size(): number {\n return this.#index.size;\n }\n}\n"],"mappings":";;;AAMA,IAAa,mBAAb,MAAoD;CAClD,SAAkB,IAAI,YAAY;CAGlC,MAAM,IAAI,KAAgC;EACxC,OAAO,KAAK,OAAO,IAAI,GAAG;CAC5B;;CAGA,MAAM,QAAQ,QAAoD;EAChE,MAAM,UAAuB,CAAC;EAC9B,KAAK,MAAM,SAAS,QAElB,QAAQ,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC;EAEpC,OAAO;CACT;CAGA,MAAM,IAAI,IAAwC;EAChD,OAAO,KAAK,OAAO,IAAI,EAAE;CAC3B;CAGA,MAAM,MAAM,SAAqC;EAC/C,OAAO,KAAK,OAAO,MAAM,OAAO;CAClC;CAGA,MAAM,MAAM,SAAoC;EAC9C,OAAO,KAAK,OAAO,MAAM,OAAO;CAClC;CAGA,MAAM,gBAAgB,QAA2C;EAC/D,OAAO,KAAK,OAAO,gBAAgB,MAAM;CAC3C;CAGA,MAAM,OAAO,KAAgC;EAC3C,OAAO,KAAK,OAAO,OAAO,GAAG;CAC/B;CAGA,MAAM,eAAe,QAAgB,MAAgD;EACnF,OAAO,KAAK,OAAO,eAAe,QAAQ,IAAI;CAChD;CAGA,MAAM,eAAe,QAAgB,MAAc,OAAmC;EACpF,KAAK,OAAO,eAAe,QAAQ,MAAM,KAAK;CAChD;CAGA,MAAM,QAAuB;EAC3B,KAAK,OAAO,MAAM;CACpB;CAEA,IAAI,OAAe;EACjB,OAAO,KAAK,OAAO;CACrB;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"memory-index-Do1mz0WF.mjs","names":[],"sources":["../src/storage/deletion.ts","../src/storage/put.ts","../src/storage/memory-index.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { eventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\n\nexport type DeletionPlan = {\n /** Stored events validated as same-pubkey (not kind 5) and to be removed now. */\n removeIds: string[];\n /** Referenced ids not in the store; remember deletion pubkey until the event arrives. */\n pendingIds: Array<{ id: string; pubkey: string }>;\n /** Replaceable/addressable coordinates tombstoned up to `until` (deletion created_at). */\n coordinates: Array<{ key: string; until: number }>;\n};\n\n/**\n * NIP-09 application plan for a kind 5 event.\n *\n * - `e` tags: delete only when the target exists, is not itself kind 5, and shares pubkey.\n * - Unknown `e` ids are pending until the event arrives (client MUST validate pubkey).\n * - `a` tags: tombstone that coordinate up to this deletion's `created_at` when pubkey matches.\n * - Deleting a deletion request is a no-op.\n */\nexport function planDeletion(\n deletion: Pick<Event, \"pubkey\" | \"created_at\" | \"tags\">,\n getById: (id: string) => Pick<Event, \"id\" | \"pubkey\" | \"kind\"> | undefined,\n): DeletionPlan {\n const { pubkey } = deletion;\n const removeIds: string[] = [];\n const pendingIds: Array<{ id: string; pubkey: string }> = [];\n const coordinates: Array<{ key: string; until: number }> = [];\n const seenIds = new Set<string>();\n const seenCoords = new Set<string>();\n\n for (const tag of deletion.tags) {\n if (tag[0] === \"e\" && tag[1] !== undefined && isHex32(tag[1].toLowerCase())) {\n const id = tag[1].toLowerCase();\n if (seenIds.has(id)) {\n continue;\n }\n seenIds.add(id);\n const existing = getById(id);\n if (!existing) {\n pendingIds.push({ id, pubkey });\n continue;\n }\n if (existing.kind === Kind.EventDeletion) {\n continue;\n }\n if (existing.pubkey !== pubkey) {\n continue;\n }\n removeIds.push(id);\n continue;\n }\n\n if (tag[0] === \"a\" && tag[1] !== undefined) {\n const coord = parseEventAddress(tag[1]);\n if (!coord || coord.pubkey !== pubkey) {\n continue;\n }\n const key = `${coord.kind}:${coord.pubkey}:${coord.identifier}`;\n if (seenCoords.has(key)) {\n continue;\n }\n seenCoords.add(key);\n coordinates.push({ key, until: deletion.created_at });\n }\n }\n\n return { removeIds, pendingIds, coordinates };\n}\n\n/** In-memory NIP-09 tombstones shared by MemoryEventStore and IndexedDbEventStore. */\nexport class DeletionState {\n readonly ids: Set<string> = new Set<string>();\n readonly pending: Map<string, string> = new Map<string, string>();\n readonly coordinates: Map<string, number> = new Map<string, number>();\n\n clear(): void {\n this.ids.clear();\n this.pending.clear();\n this.coordinates.clear();\n }\n\n covers(event: Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\">): boolean {\n if (this.ids.has(event.id)) {\n return true;\n }\n if (event.kind === Kind.EventDeletion) {\n return false;\n }\n const pendingPk = this.pending.get(event.id);\n if (pendingPk !== undefined && event.pubkey === pendingPk) {\n return true;\n }\n const addr = eventAddress(event);\n if (addr === undefined) {\n return false;\n }\n const until = this.coordinates.get(addr);\n return until !== undefined && event.created_at <= until;\n }\n\n absorb(plan: DeletionPlan): void {\n for (const id of plan.removeIds) {\n this.ids.add(id);\n this.pending.delete(id);\n }\n for (const p of plan.pendingIds) {\n if (!this.ids.has(p.id)) {\n this.pending.set(p.id, p.pubkey);\n }\n }\n for (const c of plan.coordinates) {\n const prev = this.coordinates.get(c.key) ?? Number.NEGATIVE_INFINITY;\n // Re-absorbing a coordinate moves it to the newest end so FIFO\n // tombstone caps evict the oldest marker, not the freshest.\n this.coordinates.delete(c.key);\n this.coordinates.set(c.key, Math.max(prev, c.until));\n }\n }\n}\n\nexport function coordinateRemovals(\n coordinates: ReadonlyArray<{ key: string; until: number }>,\n getCurrent: (key: string) => Pick<Event, \"id\" | \"created_at\"> | undefined,\n): string[] {\n const ids: string[] = [];\n for (const c of coordinates) {\n const current = getCurrent(c.key);\n if (current && current.created_at <= c.until) {\n ids.push(current.id);\n }\n }\n return ids;\n}\n","import type { Event } from \"../core/event.ts\";\nimport { isReplaceableWinner, validateSignedEvent } from \"../core/event.ts\";\nimport { isEphemeralKind, Kind } from \"../core/kind.ts\";\nimport { eventAddress } from \"../core/tag.ts\";\nimport { coordinateRemovals, planDeletion } from \"./deletion.ts\";\nimport type { DeletionPlan, DeletionState } from \"./deletion.ts\";\nimport type { PutResult } from \"./types.ts\";\n\nexport function outboxBoundKey(pubkey: string, kind: number): string {\n return `${pubkey.toLowerCase()}:${kind}`;\n}\n\nexport type PutDecision =\n | { action: \"skip\"; result: \"duplicate\" | \"ephemeral\" | \"rejected\" | \"invalid\"; event: Event }\n | { action: \"tombstone\"; result: \"duplicate\"; event: Event }\n | { action: \"delete\"; result: \"deleted\"; event: Event; plan: DeletionPlan; coordIds: string[] }\n | {\n action: \"insert\";\n result: \"accepted\" | \"replaced\";\n event: Event;\n address?: string;\n replaceId?: string;\n };\n\nexport type PutLookup = {\n deletion: DeletionState;\n getById: (\n id: string,\n ) => Pick<Event, \"id\" | \"pubkey\" | \"kind\" | \"created_at\" | \"tags\"> | undefined;\n /**\n * Current replaceable winner for an address. `evicted: true` marks an eviction watermark: the\n * winning event body was dropped, but its id/created_at still reject older versions, and a re-put\n * of the watermarked id re-inserts it.\n */\n getReplaceable: (\n address: string,\n ) => (Pick<Event, \"id\" | \"created_at\"> & { evicted?: boolean }) | undefined;\n};\n\nexport function decidePut(raw: Event, lookup: PutLookup): PutDecision {\n // Canonical-input precondition: non-canonical events are never indexed.\n if (!validateSignedEvent(raw)) {\n return { action: \"skip\", result: \"invalid\", event: raw };\n }\n const event = raw;\n if (lookup.deletion.ids.has(event.id) || lookup.getById(event.id)) {\n return { action: \"skip\", result: \"duplicate\", event };\n }\n if (event.kind === Kind.EventDeletion) {\n const plan = planDeletion(event, lookup.getById);\n const coordIds = coordinateRemovals(plan.coordinates, lookup.getReplaceable);\n return { action: \"delete\", result: \"deleted\", event, plan, coordIds };\n }\n if (lookup.deletion.covers(event)) {\n return { action: \"tombstone\", result: \"duplicate\", event };\n }\n if (isEphemeralKind(event.kind)) {\n return { action: \"skip\", result: \"ephemeral\", event };\n }\n const address = eventAddress(event);\n if (address !== undefined) {\n const prev = lookup.getReplaceable(address);\n // A stored incumbent rejects stale versions; an eviction watermark does\n // the same, except a re-put of the watermarked id re-inserts it.\n if (prev && prev.id !== event.id && !isReplaceableWinner(event, prev)) {\n return { action: \"skip\", result: \"rejected\", event };\n }\n if (prev !== undefined && prev.evicted !== true) {\n return { action: \"insert\", result: \"replaced\", event, address, replaceId: prev.id };\n }\n return { action: \"insert\", result: \"accepted\", event, address };\n }\n return { action: \"insert\", result: \"accepted\", event };\n}\n\nexport function applyPutMemory(\n s: {\n deletion: DeletionState;\n indexInsert: (event: Event) => void;\n indexRemove: (id: string) => boolean;\n },\n d: PutDecision,\n): PutResult {\n if (d.action === \"skip\") {\n return d.result;\n }\n if (d.action === \"tombstone\") {\n s.deletion.ids.add(d.event.id);\n s.deletion.pending.delete(d.event.id);\n return \"duplicate\";\n }\n if (d.action === \"delete\") {\n s.deletion.pending.delete(d.event.id);\n s.deletion.absorb(d.plan);\n for (const id of d.plan.removeIds) {\n s.indexRemove(id);\n }\n for (const id of d.coordIds) {\n s.deletion.ids.add(id);\n s.indexRemove(id);\n }\n s.indexInsert(d.event);\n return \"deleted\";\n }\n if (d.replaceId !== undefined) {\n s.indexRemove(d.replaceId);\n }\n s.indexInsert(d.event);\n return d.result;\n}\n","import type { Event } from \"../core/event.ts\";\nimport { compareEventsDesc, itemCompare, sortEvents } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { matchFilter } from \"../core/filter.ts\";\nimport { eventAddress, formatEventAddress, parseEventAddress } from \"../core/tag.ts\";\nimport { DeletionState } from \"./deletion.ts\";\nimport { applyPutMemory, decidePut, outboxBoundKey } from \"./put.ts\";\nimport type { PutLookup } from \"./put.ts\";\nimport type { NegentropyItem, OutboxBound, PutResult } from \"./types.ts\";\n\nexport type MemoryIndexOptions = {\n /**\n * FIFO cap applied independently to each deletion-state collection: tombstoned event ids, pending\n * e-tag ids, and coordinate tombstones. Default unbounded. Trade-off: once an entry is trimmed, a\n * re-arriving old deleted event or replaceable version can be accepted again.\n */\n maxTombstones?: number;\n /**\n * FIFO cap on replaceable/addressable winner watermarks recorded by {@link evict}. A watermark\n * keeps only `{ id, created_at }` per address so stale versions stay rejected after the winner's\n * body is evicted, and a re-put of the watermarked id re-inserts it. Default 0 (no watermark\n * retention); callers that evict should pass a bound. Once a watermark is trimmed, an older\n * evicted version can be accepted again — the same trade-off as deletion tombstones.\n */\n maxWatermarks?: number;\n /** Fires after every physical index insert (accept, replace, deletion event). */\n onInsert?: (event: Event) => void;\n /** Fires after every physical index remove (replace, delete, evict, remove, clear). */\n onRemove?: (event: Event) => void;\n};\n\n/**\n * Synchronous in-memory event index with NIP-01 replaceable / addressable / ephemeral handling and\n * NIP-09 deletion (kind 5) application. Shared by {@link MemoryEventStore} (async facade) and\n * `ReactiveEventStore`.\n */\nexport class MemoryIndex {\n readonly #byId = new Map<string, Event>();\n readonly #byPubkey = new Map<string, Set<string>>();\n readonly #byKind = new Map<number, Set<string>>();\n readonly #byKindPubkey = new Map<string, Set<string>>(); // `${kind}:${pubkey}` → ids\n readonly #byEpTag = new Map<string, Set<string>>(); // `${\"e\"|\"p\"}:${value.toLowerCase()}` → ids\n readonly #replaceable = new Map<string, string>(); // address -> event id\n // address -> last evicted winner; insertion order is the FIFO trim order\n readonly #watermarks = new Map<string, { id: string; created_at: number }>();\n // winner id -> address reverse index for O(1) watermark drops on deletion\n readonly #watermarkIds = new Map<string, string>();\n readonly #deletion = new DeletionState();\n readonly #outboxBounds = new Map<string, OutboxBound>();\n readonly #maxTombstones: number | undefined;\n readonly #maxWatermarks: number;\n readonly #onInsert: ((event: Event) => void) | undefined;\n readonly #onRemove: ((event: Event) => void) | undefined;\n\n constructor(opts?: MemoryIndexOptions) {\n this.#maxTombstones = opts?.maxTombstones;\n this.#maxWatermarks = opts?.maxWatermarks ?? 0;\n this.#onInsert = opts?.onInsert === undefined ? undefined : (event) => opts.onInsert?.(event);\n this.#onRemove = opts?.onRemove === undefined ? undefined : (event) => opts.onRemove?.(event);\n }\n\n put(raw: Event): PutResult {\n const lookup: PutLookup = {\n deletion: this.#deletion,\n getById: (id) => this.#byId.get(id),\n getReplaceable: (addr) => {\n const id = this.#replaceable.get(addr);\n const ev = id === undefined ? undefined : this.#byId.get(id);\n if (ev) {\n return { id: ev.id, created_at: ev.created_at };\n }\n const watermark = this.#watermarks.get(addr);\n return watermark === undefined ? undefined : { ...watermark, evicted: true };\n },\n };\n const decision = decidePut(raw, lookup);\n if (decision.action === \"delete\") {\n // Pending e-tag targets and coordinate tombstones never reach\n // indexRemove — drop their watermarks here.\n for (const p of decision.plan.pendingIds) {\n this.#dropWatermarkId(p.id);\n }\n for (const c of decision.plan.coordinates) {\n this.#dropWatermark(c.key);\n }\n }\n const result = applyPutMemory(\n {\n deletion: this.#deletion,\n indexInsert: (e) => this.#indexInsert(e),\n indexRemove: (id) => this.#indexRemove(id),\n },\n decision,\n );\n this.#trimTombstones();\n return result;\n }\n\n /** Sequential `put` in input order. */\n putMany(events: ReadonlyArray<Event>): PutResult[] {\n const results: PutResult[] = [];\n for (const event of events) {\n results.push(this.put(event));\n }\n return results;\n }\n\n get(id: string): Event | undefined {\n const key = id.toLowerCase();\n if (this.#deletion.ids.has(key)) {\n return undefined;\n }\n return this.#byId.get(key);\n }\n\n /** Current event at a `kind:pubkey:d` coordinate (replaceable or addressable). */\n getByAddress(address: string): Event | undefined {\n const coord = parseEventAddress(address);\n if (!coord) {\n return undefined;\n }\n const id = this.#replaceable.get(\n formatEventAddress(coord.kind, coord.pubkey, coord.identifier),\n );\n if (id === undefined) {\n return undefined;\n }\n const key = id.toLowerCase();\n if (this.#deletion.ids.has(key)) {\n return undefined;\n }\n return this.#byId.get(key);\n }\n\n query(filters: ReadonlyArray<Filter>): Event[] {\n const seen = new Set<string>();\n const events: Event[] = [];\n for (const filter of filters) {\n for (const event of this.#matchedEvents(filter)) {\n if (seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n events.push(event);\n }\n }\n sortEvents(events);\n return events;\n }\n\n count(filters: ReadonlyArray<Filter>): number {\n const seen = new Set<string>();\n for (const filter of filters) {\n for (const event of this.#matchedEvents(filter)) {\n seen.add(event.id);\n }\n }\n return seen.size;\n }\n\n negentropyItems(filter: Filter): NegentropyItem[] {\n if (filter.limit === 0) {\n return [];\n }\n const items: NegentropyItem[] = [];\n this.#eachCandidate(filter, (event) => {\n if (this.#deletion.covers(event)) {\n return;\n }\n if (!matchFilter(filter, event)) {\n return;\n }\n items.push({ id: event.id, created_at: event.created_at });\n });\n if (filter.limit !== undefined) {\n items.sort(queryItemOrder);\n if (items.length > filter.limit) {\n items.length = filter.limit;\n }\n }\n items.sort(itemCompare);\n return items;\n }\n\n /** Remove by id and tombstone (like a local NIP-09). Returns removed count. */\n remove(ids: ReadonlyArray<string>): number {\n let n = 0;\n for (const raw of ids) {\n const id = raw.toLowerCase();\n if (this.#indexRemove(id)) {\n n += 1;\n }\n this.#deletion.ids.add(id);\n this.#deletion.pending.delete(id);\n }\n this.#trimTombstones();\n return n;\n }\n\n /**\n * Remove by id without writing a tombstone (LRU eviction). When the evicted event is the current\n * winner of a replaceable/addressable address, a `{ id, created_at }` watermark is recorded so\n * stale versions stay rejected while the body is gone; the watermarks are FIFO-bounded by\n * `maxWatermarks`.\n */\n evict(ids: ReadonlyArray<string>): number {\n let n = 0;\n for (const raw of ids) {\n const id = raw.toLowerCase();\n const event = this.#byId.get(id);\n if (event === undefined) {\n continue;\n }\n const addr = eventAddress(event);\n const winner = addr !== undefined && this.#replaceable.get(addr) === id;\n if (this.#indexRemove(id)) {\n n += 1;\n }\n if (winner) {\n this.#setWatermark(addr, { id, created_at: event.created_at });\n }\n }\n return n;\n }\n\n /**\n * True when the id is tombstoned, or the coordinate has a deletion marker that still covers the\n * current event at that address: a replacement published after the marker's `until` clears the\n * deletion. Coordinates are matched on the canonical lowercase pubkey.\n */\n isDeleted(idOrAddress: string): boolean {\n if (this.#deletion.ids.has(idOrAddress.toLowerCase())) {\n return true;\n }\n const coord = parseEventAddress(idOrAddress);\n if (!coord) {\n return false;\n }\n const address = formatEventAddress(coord.kind, coord.pubkey, coord.identifier);\n const until = this.#deletion.coordinates.get(address);\n if (until === undefined) {\n return false;\n }\n const id = this.#replaceable.get(address);\n if (id === undefined) {\n return true;\n }\n const event = this.#byId.get(id);\n return event === undefined || event.created_at <= until;\n }\n\n getOutboxBound(pubkey: string, kind: number): OutboxBound | undefined {\n const persisted = this.#outboxBounds.get(outboxBoundKey(pubkey, kind));\n if (persisted) {\n return { oldest: persisted.oldest, newest: persisted.newest };\n }\n return this.#deriveOutboxBound(pubkey, kind);\n }\n\n setOutboxBound(pubkey: string, kind: number, bound: OutboxBound): void {\n this.#outboxBounds.set(outboxBoundKey(pubkey, kind), {\n oldest: bound.oldest,\n newest: bound.newest,\n });\n }\n\n clear(): void {\n if (this.#onRemove) {\n for (const event of this.#byId.values()) {\n this.#onRemove(event);\n }\n }\n this.#byId.clear();\n this.#byPubkey.clear();\n this.#byKind.clear();\n this.#byKindPubkey.clear();\n this.#byEpTag.clear();\n this.#replaceable.clear();\n this.#watermarks.clear();\n this.#watermarkIds.clear();\n this.#deletion.clear();\n this.#outboxBounds.clear();\n }\n\n get size(): number {\n return this.#byId.size;\n }\n\n #indexInsert(event: Event): void {\n this.#byId.set(event.id, event);\n const { pubkey } = event;\n addToSet(this.#byPubkey, pubkey, event.id);\n addToSet(this.#byKind, event.kind, event.id);\n addToSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, event.id);\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) {\n continue;\n }\n addToSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, event.id);\n }\n const addr = eventAddress(event);\n if (addr !== undefined) {\n this.#replaceable.set(addr, event.id);\n this.#dropWatermark(addr);\n }\n this.#onInsert?.(event);\n }\n\n #indexRemove(id: string): boolean {\n const key = id;\n // A removed/deleted id must not keep a stale watermark alive; eviction\n // re-records its winner watermark right after this call.\n this.#dropWatermarkId(key);\n const event = this.#byId.get(key);\n if (!event) {\n return false;\n }\n this.#byId.delete(key);\n const { pubkey } = event;\n removeFromSet(this.#byPubkey, pubkey, key);\n removeFromSet(this.#byKind, event.kind, key);\n removeFromSet(this.#byKindPubkey, `${event.kind}:${pubkey}`, key);\n for (const tag of event.tags) {\n if ((tag[0] !== \"e\" && tag[0] !== \"p\") || tag[1] === undefined) {\n continue;\n }\n removeFromSet(this.#byEpTag, `${tag[0]}:${tag[1].toLowerCase()}`, key);\n }\n const addr = eventAddress(event);\n if (addr !== undefined && this.#replaceable.get(addr) === key) {\n this.#replaceable.delete(addr);\n }\n this.#onRemove?.(event);\n return true;\n }\n\n #matchedEvents(filter: Filter): Event[] {\n if (filter.limit === 0) {\n return [];\n }\n const matched: Event[] = [];\n this.#eachCandidate(filter, (event) => {\n if (this.#deletion.covers(event)) {\n return;\n }\n if (!matchFilter(filter, event)) {\n return;\n }\n matched.push(event);\n });\n sortEvents(matched);\n return filter.limit === undefined ? matched : matched.slice(0, filter.limit);\n }\n\n #deriveOutboxBound(pubkey: string, kind: number): OutboxBound | undefined {\n const byPk = this.#byPubkey.get(pubkey.toLowerCase());\n const byKind = this.#byKind.get(kind);\n if (!byPk || !byKind) {\n return undefined;\n }\n let oldest: number | undefined;\n let newest: number | undefined;\n for (const id of byPk) {\n if (!byKind.has(id) || this.#deletion.ids.has(id)) {\n continue;\n }\n const event = this.#byId.get(id);\n if (!event || this.#deletion.covers(event)) {\n continue;\n }\n if (oldest === undefined || event.created_at < oldest) {\n oldest = event.created_at;\n }\n if (newest === undefined || event.created_at > newest) {\n newest = event.created_at;\n }\n }\n if (oldest === undefined || newest === undefined) {\n return undefined;\n }\n return { oldest, newest };\n }\n\n #eachCandidate(filter: Filter, visit: (event: Event) => void): void {\n if (filter.ids) {\n const seen = new Set<string>();\n for (const raw of filter.ids) {\n const event = this.#byId.get(raw.toLowerCase());\n if (!event || seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n visit(event);\n }\n return;\n }\n\n if (filter.authors && filter.kinds) {\n const seen = new Set<string>();\n for (const pk of filter.authors) {\n const pubkey = pk.toLowerCase();\n for (const kind of filter.kinds) {\n const ids = this.#byKindPubkey.get(`${kind}:${pubkey}`);\n if (!ids) {\n continue;\n }\n for (const id of ids) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n }\n return;\n }\n\n if (filter.authors) {\n const seen = new Set<string>();\n for (const pk of filter.authors) {\n const byPk = this.#byPubkey.get(pk.toLowerCase());\n if (!byPk) {\n continue;\n }\n for (const id of byPk) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n return;\n }\n\n if (filter.kinds) {\n const seen = new Set<string>();\n for (const kind of filter.kinds) {\n const byKind = this.#byKind.get(kind);\n if (!byKind) {\n continue;\n }\n for (const id of byKind) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = this.#byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n return;\n }\n\n const eTags = filter[\"#e\"];\n const pTags = filter[\"#p\"];\n if (eTags !== undefined || pTags !== undefined) {\n const seen = new Set<string>();\n visitEpTagIds(this.#byEpTag, this.#byId, \"e\", eTags, seen, visit);\n visitEpTagIds(this.#byEpTag, this.#byId, \"p\", pTags, seen, visit);\n return;\n }\n\n // #t/#d and other non-e/p tags are not indexed (e/p only). A generic tag\n // store is extra put/remove amp; hashtag-only queries scan #byId.\n for (const event of this.#byId.values()) {\n visit(event);\n }\n }\n\n #setWatermark(addr: string, watermark: { id: string; created_at: number }): void {\n if (this.#maxWatermarks === 0) {\n return;\n }\n this.#dropWatermark(addr);\n this.#watermarks.set(addr, watermark);\n this.#watermarkIds.set(watermark.id, addr);\n while (this.#watermarks.size > this.#maxWatermarks) {\n const oldest = this.#watermarks.keys().next().value;\n if (oldest === undefined) {\n break;\n }\n this.#dropWatermark(oldest);\n }\n }\n\n #dropWatermark(addr: string): void {\n const watermark = this.#watermarks.get(addr);\n if (watermark === undefined) {\n return;\n }\n this.#watermarks.delete(addr);\n this.#watermarkIds.delete(watermark.id);\n }\n\n #dropWatermarkId(id: string): void {\n const addr = this.#watermarkIds.get(id);\n if (addr === undefined) {\n return;\n }\n this.#watermarkIds.delete(id);\n this.#watermarks.delete(addr);\n }\n\n #trimTombstones(): void {\n const cap = this.#maxTombstones;\n if (cap === undefined) {\n return;\n }\n let excess = this.#deletion.ids.size - cap;\n for (const id of this.#deletion.ids) {\n if (excess <= 0) {\n break;\n }\n this.#deletion.ids.delete(id);\n this.#deletion.pending.delete(id);\n excess--;\n }\n excess = this.#deletion.pending.size - cap;\n for (const id of this.#deletion.pending.keys()) {\n if (excess <= 0) {\n break;\n }\n this.#deletion.pending.delete(id);\n excess--;\n }\n excess = this.#deletion.coordinates.size - cap;\n for (const key of this.#deletion.coordinates.keys()) {\n if (excess <= 0) {\n break;\n }\n this.#deletion.coordinates.delete(key);\n excess--;\n }\n }\n}\n\nfunction visitEpTagIds(\n byEpTag: Map<string, Set<string>>,\n byId: Map<string, Event>,\n name: \"e\" | \"p\",\n values: ReadonlyArray<string> | undefined,\n seen: Set<string>,\n visit: (event: Event) => void,\n): void {\n if (values === undefined) {\n return;\n }\n for (const value of values) {\n const ids = byEpTag.get(`${name}:${value.toLowerCase()}`);\n if (!ids) {\n continue;\n }\n for (const id of ids) {\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n const event = byId.get(id);\n if (event) {\n visit(event);\n }\n }\n }\n}\n\nfunction addToSet<K>(map: Map<K, Set<string>>, key: K, id: string): void {\n let set = map.get(key);\n if (!set) {\n set = new Set();\n map.set(key, set);\n }\n set.add(id);\n}\n\nfunction removeFromSet<K>(map: Map<K, Set<string>>, key: K, id: string): void {\n const set = map.get(key);\n if (!set) {\n return;\n }\n set.delete(id);\n if (set.size === 0) {\n map.delete(key);\n }\n}\n\nfunction queryItemOrder(a: NegentropyItem, b: NegentropyItem): number {\n return compareEventsDesc(a, b);\n}\n"],"mappings":"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"}
@@ -1,193 +0,0 @@
1
- import { a as hexToBytes, c as isRecord, h as MessageError, i as bytesToHex } from "./util-LfQirgDk.mjs";
2
- import { m as validateSignedEvent } from "./event-D8Nfoh4K.mjs";
3
- //#region src/core/message.ts
4
- /** Validate a subscription id; throws {@link MessageError} unless 1..64 chars. */
5
- function assertSubscriptionId(id) {
6
- if (id.length === 0 || id.length > 64) throw new MessageError(`subscription id length must be 1..64`);
7
- return id;
8
- }
9
- /** Validate the given id, or mint a random 16-hex-char subscription id. */
10
- function createSubscriptionId(id) {
11
- if (id !== void 0) return assertSubscriptionId(id);
12
- const bytes = /* @__PURE__ */ new Uint8Array(8);
13
- crypto.getRandomValues(bytes);
14
- return bytesToHex(bytes);
15
- }
16
- const HLL_BYTES = 256;
17
- const HLL_HEX_LEN = HLL_BYTES * 2;
18
- const HEX_RE = /^[0-9a-fA-F]+$/;
19
- /**
20
- * Register-wise max of NIP-45 HyperLogLog sketches. Output is always lowercase 512 hex. Empty input
21
- * is the zero sketch (identity).
22
- */
23
- function mergeCountHll(hexes) {
24
- const merged = new Uint8Array(HLL_BYTES);
25
- for (const hex of hexes) {
26
- if (hex.length !== HLL_HEX_LEN || !HEX_RE.test(hex)) throw new MessageError("invalid NIP-45 HLL sketch: expected 512-char hex");
27
- const bytes = hexToBytes(hex);
28
- for (const [i, b] of bytes.entries()) {
29
- const m = merged[i];
30
- if (m !== void 0 && b > m) merged[i] = b;
31
- }
32
- }
33
- return bytesToHex(merged);
34
- }
35
- /** Serialize a client->relay message to its NIP-01 JSON wire form. */
36
- function encodeClientMessage(message) {
37
- return JSON.stringify(message);
38
- }
39
- /** Serialize a relay->client message to its NIP-01 JSON wire form. */
40
- function encodeRelayMessage(message) {
41
- return JSON.stringify(message);
42
- }
43
- /** Parse a client->relay JSON message; throws {@link MessageError} on malformed input. */
44
- function parseClientMessage(raw) {
45
- let data;
46
- try {
47
- data = JSON.parse(raw);
48
- } catch {
49
- throw new MessageError("client message is not valid JSON");
50
- }
51
- if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== "string") throw new MessageError("client message must be a non-empty JSON array");
52
- const [type] = data;
53
- switch (type) {
54
- case "EVENT":
55
- if (data.length !== 2 || !validateSignedEvent(data[1])) throw new MessageError("invalid EVENT client message");
56
- return ["EVENT", data[1]];
57
- case "REQ":
58
- if (data.length < 3 || typeof data[1] !== "string") throw new MessageError("invalid REQ client message");
59
- return [
60
- "REQ",
61
- data[1],
62
- ...parseWireFilters(data.slice(2), "REQ")
63
- ];
64
- case "CLOSE":
65
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid CLOSE client message");
66
- return ["CLOSE", data[1]];
67
- case "AUTH":
68
- if (data.length !== 2 || !validateSignedEvent(data[1])) throw new MessageError("invalid AUTH client message");
69
- return ["AUTH", data[1]];
70
- case "COUNT":
71
- if (data.length < 3 || typeof data[1] !== "string") throw new MessageError("invalid COUNT client message");
72
- return [
73
- "COUNT",
74
- data[1],
75
- ...parseWireFilters(data.slice(2), "COUNT")
76
- ];
77
- case "NEG-OPEN":
78
- if (data.length === 5) throw new MessageError("obsolete 5-element NEG-OPEN; expected [NEG-OPEN, id, filter, hex]");
79
- if (data.length !== 4 || typeof data[1] !== "string" || !isFilterObject(data[2]) || !isNegHex(data[3])) throw new MessageError("invalid NEG-OPEN client message");
80
- return [
81
- "NEG-OPEN",
82
- data[1],
83
- data[2],
84
- data[3].toLowerCase()
85
- ];
86
- case "NEG-MSG":
87
- if (data.length !== 3 || typeof data[1] !== "string" || !isNegHex(data[2])) throw new MessageError("invalid NEG-MSG client message");
88
- return [
89
- "NEG-MSG",
90
- data[1],
91
- data[2].toLowerCase()
92
- ];
93
- case "NEG-CLOSE":
94
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid NEG-CLOSE client message");
95
- return ["NEG-CLOSE", data[1]];
96
- default: throw new MessageError(`unknown client message type: ${String(type)}`);
97
- }
98
- }
99
- /** Parse a relay->client JSON message; throws {@link MessageError} on malformed input. */
100
- function parseRelayMessage(raw) {
101
- let data;
102
- try {
103
- data = JSON.parse(raw);
104
- } catch {
105
- throw new MessageError("relay message is not valid JSON");
106
- }
107
- if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== "string") throw new MessageError("relay message must be a non-empty JSON array");
108
- const items = data;
109
- const [type] = items;
110
- switch (type) {
111
- case "EVENT":
112
- if (data.length !== 3 || typeof data[1] !== "string" || !validateSignedEvent(data[2])) throw new MessageError("invalid EVENT relay message");
113
- return [
114
- "EVENT",
115
- data[1],
116
- data[2]
117
- ];
118
- case "OK":
119
- if (data.length !== 4 || typeof data[1] !== "string" || typeof data[2] !== "boolean" || typeof data[3] !== "string") throw new MessageError("invalid OK relay message");
120
- return [
121
- "OK",
122
- data[1],
123
- data[2],
124
- data[3]
125
- ];
126
- case "EOSE":
127
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid EOSE relay message");
128
- return ["EOSE", data[1]];
129
- case "CLOSED":
130
- if (data.length !== 3 || typeof data[1] !== "string" || typeof data[2] !== "string") throw new MessageError("invalid CLOSED relay message");
131
- return [
132
- "CLOSED",
133
- data[1],
134
- data[2]
135
- ];
136
- case "NOTICE":
137
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid NOTICE relay message");
138
- return ["NOTICE", data[1]];
139
- case "AUTH":
140
- if (data.length !== 2 || typeof data[1] !== "string") throw new MessageError("invalid AUTH relay message");
141
- return ["AUTH", data[1]];
142
- case "COUNT": {
143
- const payload = items.at(2);
144
- if (data.length !== 3 || typeof data[1] !== "string" || !isRecord(payload) || typeof payload["count"] !== "number") throw new MessageError("invalid COUNT relay message");
145
- const result = { count: payload["count"] };
146
- if (typeof payload["approximate"] === "boolean") result.approximate = payload["approximate"];
147
- const hll = parseCountHll(payload["hll"]);
148
- if (hll !== void 0) result.hll = hll;
149
- return [
150
- "COUNT",
151
- data[1],
152
- result
153
- ];
154
- }
155
- case "NEG-MSG":
156
- if (data.length !== 3 || typeof data[1] !== "string" || !isNegHex(data[2])) throw new MessageError("invalid NEG-MSG relay message");
157
- return [
158
- "NEG-MSG",
159
- data[1],
160
- data[2].toLowerCase()
161
- ];
162
- case "NEG-ERR":
163
- if (data.length !== 3 && data.length !== 4 || typeof data[1] !== "string" || typeof data[2] !== "string") throw new MessageError("invalid NEG-ERR relay message");
164
- return [
165
- "NEG-ERR",
166
- data[1],
167
- data[2]
168
- ];
169
- default: throw new MessageError(`unknown relay message type: ${String(type)}`);
170
- }
171
- }
172
- function parseCountHll(value) {
173
- if (typeof value !== "string" || value.length !== HLL_HEX_LEN || !HEX_RE.test(value)) return;
174
- return value.toLowerCase();
175
- }
176
- function parseWireFilters(items, kind) {
177
- const filters = [];
178
- for (const item of items) {
179
- if (!isFilterObject(item)) throw new MessageError(`invalid ${kind} filter`);
180
- filters.push(item);
181
- }
182
- return filters;
183
- }
184
- function isFilterObject(value) {
185
- return isRecord(value);
186
- }
187
- function isNegHex(value) {
188
- return typeof value === "string" && value.length > 0 && value.length % 2 === 0 && HEX_RE.test(value);
189
- }
190
- //#endregion
191
- export { mergeCountHll as a, encodeRelayMessage as i, createSubscriptionId as n, parseClientMessage as o, encodeClientMessage as r, parseRelayMessage as s, assertSubscriptionId as t };
192
-
193
- //# sourceMappingURL=message-DcY6Fi9o.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"message-DcY6Fi9o.mjs","names":["items"],"sources":["../src/core/message.ts"],"sourcesContent":["import { MessageError } from \"./error.ts\";\nimport type { Event } from \"./event.ts\";\nimport { validateSignedEvent } from \"./event.ts\";\nimport type { Filter } from \"./filter.ts\";\nimport { SUBSCRIPTION_ID_MAX_CHARS } from \"./limits.ts\";\nimport { bytesToHex, hexToBytes, isRecord } from \"./util.ts\";\n\n/** NIP-01 subscription id: 1..64 chars. */\nexport type SubscriptionId = string;\n\n/** Validate a subscription id; throws {@link MessageError} unless 1..64 chars. */\nexport function assertSubscriptionId(id: string): SubscriptionId {\n if (id.length === 0 || id.length > SUBSCRIPTION_ID_MAX_CHARS) {\n throw new MessageError(`subscription id length must be 1..${SUBSCRIPTION_ID_MAX_CHARS}`);\n }\n return id;\n}\n\n/** Validate the given id, or mint a random 16-hex-char subscription id. */\nexport function createSubscriptionId(id?: string): SubscriptionId {\n if (id !== undefined) {\n return assertSubscriptionId(id);\n }\n const bytes = new Uint8Array(8);\n crypto.getRandomValues(bytes);\n return bytesToHex(bytes);\n}\n\n/** Client → relay messages (NIP-01 + NIP-42 AUTH + NIP-45 COUNT + NIP-77). */\nexport type ClientMessage =\n | [\"EVENT\", Event]\n | [\"REQ\", SubscriptionId, ...Filter[]]\n | [\"CLOSE\", SubscriptionId]\n | [\"AUTH\", Event]\n | [\"COUNT\", SubscriptionId, ...Filter[]]\n | [\"NEG-OPEN\", SubscriptionId, Filter, string]\n | [\"NEG-MSG\", SubscriptionId, string]\n | [\"NEG-CLOSE\", SubscriptionId];\n\n/** Relay → client messages. */\nexport type RelayMessage =\n | [\"EVENT\", SubscriptionId, Event]\n | [\"OK\", string, boolean, string]\n | [\"EOSE\", SubscriptionId]\n | [\"CLOSED\", SubscriptionId, string]\n | [\"NOTICE\", string]\n | [\"AUTH\", string]\n | [\"COUNT\", SubscriptionId, CountResult]\n | [\"NEG-MSG\", SubscriptionId, string]\n | [\"NEG-ERR\", SubscriptionId, string];\n\n/** NIP-45 COUNT reply payload. */\nexport type CountResult = {\n count: number;\n approximate?: boolean;\n /**\n * Optional 512-char hex HyperLogLog sketch (256 registers). Merge sketches with `mergeCountHll`.\n * Estimation is unspecified by NIP-45 and not provided.\n */\n hll?: string;\n};\n\nconst HLL_BYTES = 256; // 512 hex chars (NIP-45; hex only, not base64)\nconst HLL_HEX_LEN = HLL_BYTES * 2;\nconst HEX_RE = /^[0-9a-fA-F]+$/;\n\n/**\n * Register-wise max of NIP-45 HyperLogLog sketches. Output is always lowercase 512 hex. Empty input\n * is the zero sketch (identity).\n */\nexport function mergeCountHll(hexes: ReadonlyArray<string>): string {\n const merged = new Uint8Array(HLL_BYTES);\n for (const hex of hexes) {\n if (hex.length !== HLL_HEX_LEN || !HEX_RE.test(hex)) {\n throw new MessageError(\"invalid NIP-45 HLL sketch: expected 512-char hex\");\n }\n const bytes = hexToBytes(hex);\n for (const [i, b] of bytes.entries()) {\n const m = merged[i];\n if (m !== undefined && b > m) {\n merged[i] = b;\n }\n }\n }\n return bytesToHex(merged);\n}\n\n/** Serialize a client->relay message to its NIP-01 JSON wire form. */\nexport function encodeClientMessage(message: ClientMessage): string {\n return JSON.stringify(message);\n}\n\n/** Serialize a relay->client message to its NIP-01 JSON wire form. */\nexport function encodeRelayMessage(message: RelayMessage): string {\n return JSON.stringify(message);\n}\n\n/** Parse a client->relay JSON message; throws {@link MessageError} on malformed input. */\nexport function parseClientMessage(raw: string): ClientMessage {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new MessageError(\"client message is not valid JSON\");\n }\n if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== \"string\") {\n throw new MessageError(\"client message must be a non-empty JSON array\");\n }\n const items: unknown[] = data;\n const [type] = items;\n switch (type) {\n case \"EVENT\":\n if (data.length !== 2 || !validateSignedEvent(data[1])) {\n throw new MessageError(\"invalid EVENT client message\");\n }\n return [\"EVENT\", data[1]];\n\n case \"REQ\":\n if (data.length < 3 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid REQ client message\");\n }\n return [\"REQ\", data[1], ...parseWireFilters(data.slice(2), \"REQ\")];\n\n case \"CLOSE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid CLOSE client message\");\n }\n return [\"CLOSE\", data[1]];\n\n case \"AUTH\":\n if (data.length !== 2 || !validateSignedEvent(data[1])) {\n throw new MessageError(\"invalid AUTH client message\");\n }\n return [\"AUTH\", data[1]];\n\n case \"COUNT\":\n if (data.length < 3 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid COUNT client message\");\n }\n return [\"COUNT\", data[1], ...parseWireFilters(data.slice(2), \"COUNT\")];\n\n case \"NEG-OPEN\":\n if (data.length === 5) {\n throw new MessageError(\"obsolete 5-element NEG-OPEN; expected [NEG-OPEN, id, filter, hex]\");\n }\n if (\n data.length !== 4 ||\n typeof data[1] !== \"string\" ||\n !isFilterObject(data[2]) ||\n !isNegHex(data[3])\n ) {\n throw new MessageError(\"invalid NEG-OPEN client message\");\n }\n return [\"NEG-OPEN\", data[1], data[2], data[3].toLowerCase()];\n\n case \"NEG-MSG\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || !isNegHex(data[2])) {\n throw new MessageError(\"invalid NEG-MSG client message\");\n }\n return [\"NEG-MSG\", data[1], data[2].toLowerCase()];\n\n case \"NEG-CLOSE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid NEG-CLOSE client message\");\n }\n return [\"NEG-CLOSE\", data[1]];\n\n default:\n throw new MessageError(`unknown client message type: ${String(type)}`);\n }\n}\n\n/** Parse a relay->client JSON message; throws {@link MessageError} on malformed input. */\nexport function parseRelayMessage(raw: string): RelayMessage {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new MessageError(\"relay message is not valid JSON\");\n }\n if (!Array.isArray(data) || data.length === 0 || typeof data[0] !== \"string\") {\n throw new MessageError(\"relay message must be a non-empty JSON array\");\n }\n const items: unknown[] = data;\n const [type] = items;\n switch (type) {\n case \"EVENT\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || !validateSignedEvent(data[2])) {\n throw new MessageError(\"invalid EVENT relay message\");\n }\n return [\"EVENT\", data[1], data[2]];\n\n case \"OK\":\n if (\n data.length !== 4 ||\n typeof data[1] !== \"string\" ||\n typeof data[2] !== \"boolean\" ||\n typeof data[3] !== \"string\"\n ) {\n throw new MessageError(\"invalid OK relay message\");\n }\n return [\"OK\", data[1], data[2], data[3]];\n\n case \"EOSE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid EOSE relay message\");\n }\n return [\"EOSE\", data[1]];\n\n case \"CLOSED\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || typeof data[2] !== \"string\") {\n throw new MessageError(\"invalid CLOSED relay message\");\n }\n return [\"CLOSED\", data[1], data[2]];\n\n case \"NOTICE\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid NOTICE relay message\");\n }\n return [\"NOTICE\", data[1]];\n\n case \"AUTH\":\n if (data.length !== 2 || typeof data[1] !== \"string\") {\n throw new MessageError(\"invalid AUTH relay message\");\n }\n return [\"AUTH\", data[1]];\n\n case \"COUNT\": {\n const payload = items.at(2);\n if (\n data.length !== 3 ||\n typeof data[1] !== \"string\" ||\n !isRecord(payload) ||\n typeof payload[\"count\"] !== \"number\"\n ) {\n throw new MessageError(\"invalid COUNT relay message\");\n }\n const result: CountResult = { count: payload[\"count\"] };\n if (typeof payload[\"approximate\"] === \"boolean\") {\n result.approximate = payload[\"approximate\"];\n }\n const hll = parseCountHll(payload[\"hll\"]);\n if (hll !== undefined) {\n result.hll = hll;\n }\n return [\"COUNT\", data[1], result];\n }\n case \"NEG-MSG\":\n if (data.length !== 3 || typeof data[1] !== \"string\" || !isNegHex(data[2])) {\n throw new MessageError(\"invalid NEG-MSG relay message\");\n }\n return [\"NEG-MSG\", data[1], data[2].toLowerCase()];\n\n case \"NEG-ERR\":\n if (\n (data.length !== 3 && data.length !== 4) ||\n typeof data[1] !== \"string\" ||\n typeof data[2] !== \"string\"\n ) {\n throw new MessageError(\"invalid NEG-ERR relay message\");\n }\n return [\"NEG-ERR\", data[1], data[2]];\n\n default:\n throw new MessageError(`unknown relay message type: ${String(type)}`);\n }\n}\n\nfunction parseCountHll(value: unknown): string | undefined {\n if (typeof value !== \"string\" || value.length !== HLL_HEX_LEN || !HEX_RE.test(value)) {\n return undefined;\n }\n return value.toLowerCase();\n}\n\nfunction parseWireFilters(items: unknown[], kind: string): Filter[] {\n const filters: Filter[] = [];\n for (const item of items) {\n if (!isFilterObject(item)) {\n throw new MessageError(`invalid ${kind} filter`);\n }\n filters.push(item);\n }\n return filters;\n}\n\nfunction isFilterObject(value: unknown): value is Filter {\n return isRecord(value);\n}\n\nfunction isNegHex(value: unknown): value is string {\n return (\n typeof value === \"string\" && value.length > 0 && value.length % 2 === 0 && HEX_RE.test(value)\n );\n}\n"],"mappings":"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"}
@@ -1,26 +0,0 @@
1
- import { _ as Tag, t as Event } from "./event-fU3Y01PR.mjs";
2
- import { t as EventBuilder } from "./builder-sSflu6nB.mjs";
3
- //#region src/nips/nip65.d.ts
4
- /** NIP-65 `r` tag marker: read-only, write-only, or unmarked (both). */
5
- type RelayMarker = "read" | "write" | "readwrite";
6
- /** One NIP-65 relay-list entry: normalized URL plus read/write flags. */
7
- type RelayListItem = {
8
- url: string;
9
- read: boolean;
10
- write: boolean;
11
- };
12
- /** The NIP-65 marker an item's read/write flags map to. */
13
- declare function markerOf(item: RelayListItem): RelayMarker;
14
- /** Parse a kind:10002 NIP-65 event into relay list entries. */
15
- declare function parseRelayList(event: Event): RelayListItem[];
16
- /** Encode relay list items as NIP-65 `r` tags. */
17
- declare function relayListToTags(items: RelayListItem[]): Tag[];
18
- /** Build an unsigned kind:10002 EventBuilder from relay list items. */
19
- declare function relayListEventBuilder(items: RelayListItem[]): EventBuilder;
20
- /** URLs of the read-enabled items. */
21
- declare function readRelays(items: RelayListItem[]): string[];
22
- /** URLs of the write-enabled items. */
23
- declare function writeRelays(items: RelayListItem[]): string[];
24
- //#endregion
25
- export { readRelays as a, writeRelays as c, parseRelayList as i, RelayMarker as n, relayListEventBuilder as o, markerOf as r, relayListToTags as s, RelayListItem as t };
26
- //# sourceMappingURL=nip65-CjgmdpPB.d.mts.map
@@ -1,40 +0,0 @@
1
- import { a as NostrError } from "../error-CR8Urc84.mjs";
2
- import { t as ManualFetch } from "../http-C4HwIu4B.mjs";
3
- //#region src/nips/nip96.d.ts
4
- export type Nip96Fetch = ManualFetch;
5
- export type Nip96ServerInfo = {
6
- api_url: string;
7
- download_url?: string;
8
- delegated_to_url?: string;
9
- content_types?: string[];
10
- };
11
- export type Nip96UploadResult = {
12
- status: "success";
13
- url: string;
14
- tags: string[][];
15
- } | {
16
- status: "processing";
17
- processingUrl: string;
18
- tags: string[][];
19
- };
20
- export declare class Nip96Error extends NostrError {
21
- name: string;
22
- }
23
- /**
24
- * Parse `{ status, processing_url, nip94_event.tags }`. A `url` tag is required unless the response
25
- * reports delayed processing (`status: "processing"` or HTTP 202 with `processing_url`).
26
- */
27
- export declare function parseNip96UploadResponse(json: unknown, httpStatus?: number): Nip96UploadResult;
28
- /** GET `${service}/.well-known/nostr/nip96.json`; follows `delegated_to_url` one hop. */
29
- export declare function fetchNip96Info(serviceUrl: string, opts?: {
30
- fetch?: Nip96Fetch;
31
- signal?: AbortSignal;
32
- }): Promise<Nip96ServerInfo>;
33
- /** POST `apiUrl` as multipart `file`. `authorization` is a prebuilt NIP-98 header. */
34
- export declare function uploadNip96(apiUrl: string, file: Blob, authorization: string, opts?: {
35
- fetch?: Nip96Fetch;
36
- signal?: AbortSignal;
37
- extraFields?: Record<string, string>;
38
- }): Promise<Nip96UploadResult>;
39
- //#endregion
40
- //# sourceMappingURL=nip96.d.mts.map