@qntx/nostr 0.6.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 (177) 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-CadO5fAX.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-CNl2q67H.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-lvXeER5t.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-DQE8BoWt.mjs → instance-BcKzxHIP.mjs} +26 -27
  38. package/dist/instance-BcKzxHIP.mjs.map +1 -0
  39. package/dist/{instance-CIca-ynn.d.mts → instance-DjsL8h84.d.mts} +5 -5
  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/{pool-DBIYMtwy.d.mts → pool-SzJKPIa3.d.mts} +96 -91
  117. package/dist/pool-YHRZgcQ3.mjs +596 -0
  118. package/dist/pool-YHRZgcQ3.mjs.map +1 -0
  119. package/dist/put-DS0yzVJx.mjs +187 -0
  120. package/dist/put-DS0yzVJx.mjs.map +1 -0
  121. package/dist/{reactive-7aqeip4A.d.mts → reactive-BJ1ggn4d.d.mts} +7 -22
  122. package/dist/{reactive-BfhPp9a3.mjs → reactive-BJRoVHxU.mjs} +80 -65
  123. package/dist/reactive-BJRoVHxU.mjs.map +1 -0
  124. package/dist/{pool-As20OGxB.mjs → relay-CvxPiQ_m.mjs} +178 -721
  125. package/dist/relay-CvxPiQ_m.mjs.map +1 -0
  126. package/dist/relay.d.mts +4 -4
  127. package/dist/relay.mjs +3 -2
  128. package/dist/serial-Bzpw-xz3.mjs +21 -0
  129. package/dist/serial-Bzpw-xz3.mjs.map +1 -0
  130. package/dist/{signer-CYQutDzD.mjs → signer-yW3slDC2.mjs} +12 -14
  131. package/dist/signer-yW3slDC2.mjs.map +1 -0
  132. package/dist/signer.d.mts +2 -2
  133. package/dist/signer.mjs +2 -2
  134. package/dist/{storage-Dky3wuoH.mjs → storage-CZ52ifcQ.mjs} +17 -42
  135. package/dist/storage-CZ52ifcQ.mjs.map +1 -0
  136. package/dist/storage.d.mts +3 -3
  137. package/dist/storage.mjs +4 -4
  138. package/dist/store.d.mts +1 -1
  139. package/dist/store.mjs +1 -1
  140. package/dist/testing.d.mts +18 -18
  141. package/dist/testing.mjs +9 -9
  142. package/dist/testing.mjs.map +1 -1
  143. package/dist/{types-2pCUprRU.d.mts → types-Cm-XMgtH.d.mts} +6 -6
  144. package/dist/{types-CRQEk0Oi.d.mts → types-DNmiscfd.d.mts} +2 -2
  145. package/dist/{util-LfQirgDk.mjs → util-yFYzlbrm.mjs} +29 -7
  146. package/dist/util-yFYzlbrm.mjs.map +1 -0
  147. package/dist/{verifier-t5WoWbtY.mjs → verifier-BTLOpo1n.mjs} +3 -3
  148. package/dist/verifier-BTLOpo1n.mjs.map +1 -0
  149. package/dist/verifier-CgTFpgsC.d.mts +22 -0
  150. package/dist/wasm.browser.d.mts +3 -3
  151. package/dist/wasm.browser.mjs +3 -3
  152. package/dist/wasm.d.mts +3 -3
  153. package/dist/wasm.mjs +3 -3
  154. package/dist/wasm.mjs.map +1 -1
  155. package/package.json +5 -9
  156. package/dist/abort-Ba8tDrOI.mjs.map +0 -1
  157. package/dist/builder-9tjI6WQ3.mjs.map +0 -1
  158. package/dist/client-CadO5fAX.mjs.map +0 -1
  159. package/dist/event-D8Nfoh4K.mjs.map +0 -1
  160. package/dist/gossip-oloDMWEj.mjs.map +0 -1
  161. package/dist/instance-DQE8BoWt.mjs.map +0 -1
  162. package/dist/key-Bps-LqX7.mjs.map +0 -1
  163. package/dist/loaders-o6YXB_Ri.mjs.map +0 -1
  164. package/dist/memory-C2EVx0_O.mjs.map +0 -1
  165. package/dist/memory-index-Do1mz0WF.mjs.map +0 -1
  166. package/dist/message-DcY6Fi9o.mjs +0 -193
  167. package/dist/message-DcY6Fi9o.mjs.map +0 -1
  168. package/dist/nip65-CjgmdpPB.d.mts +0 -26
  169. package/dist/nips/nip96.d.mts +0 -40
  170. package/dist/nips/nip96.mjs +0 -118
  171. package/dist/nips/nip96.mjs.map +0 -1
  172. package/dist/pool-As20OGxB.mjs.map +0 -1
  173. package/dist/reactive-BfhPp9a3.mjs.map +0 -1
  174. package/dist/signer-CYQutDzD.mjs.map +0 -1
  175. package/dist/storage-Dky3wuoH.mjs.map +0 -1
  176. package/dist/util-LfQirgDk.mjs.map +0 -1
  177. package/dist/verifier-t5WoWbtY.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qntx/nostr",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Nostr protocol implementation and SDK in TypeScript",
5
5
  "keywords": [
6
6
  "decentralized",
@@ -149,11 +149,6 @@
149
149
  "import": "./dist/nips/nip77.mjs",
150
150
  "default": "./dist/nips/nip77.mjs"
151
151
  },
152
- "./nips/nip96": {
153
- "types": "./dist/nips/nip96.d.mts",
154
- "import": "./dist/nips/nip96.mjs",
155
- "default": "./dist/nips/nip96.mjs"
156
- },
157
152
  "./nips/nip98": {
158
153
  "types": "./dist/nips/nip98.d.mts",
159
154
  "import": "./dist/nips/nip98.mjs",
@@ -184,12 +179,12 @@
184
179
  "import": "./dist/testing.mjs",
185
180
  "default": "./dist/testing.mjs"
186
181
  },
182
+ "./package.json": "./package.json",
187
183
  "./wasm.browser": {
188
184
  "types": "./dist/wasm.browser.d.mts",
189
185
  "import": "./dist/wasm.browser.mjs",
190
186
  "default": "./dist/wasm.browser.mjs"
191
187
  },
192
- "./package.json": "./package.json",
193
188
  "./wasm": {
194
189
  "types": "./dist/wasm.d.mts",
195
190
  "browser": "./dist/wasm.browser.mjs",
@@ -207,8 +202,9 @@
207
202
  "test": "vp test",
208
203
  "test:wasm": "WASM_TEST=1 vp test wasm-tests/verify.ts wasm-tests/node-load.ts wasm-tests/sign.ts wasm-tests/browser-entry.ts",
209
204
  "bench:wasm": "WASM_TEST=1 vp test wasm-tests/bench.ts",
210
- "bench:store": "STORE_BENCH=1 vp test bench/reactive-store.bench.ts",
211
- "bench:store:sqlite": "STORE_BENCH=1 vp test bench/sqlite-store.bench.ts",
205
+ "bench:store": "BENCH=1 vp test bench/reactive-store.bench.ts",
206
+ "bench:store:sqlite": "BENCH=1 vp test bench/sqlite-store.bench.ts",
207
+ "bench:nip27": "BENCH=1 vp test bench/nip27.bench.ts",
212
208
  "lint": "vp check",
213
209
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.src.json",
214
210
  "smoke:hermes": "bun scripts/hermes-smoke.ts",
@@ -1 +0,0 @@
1
- {"version":3,"file":"abort-Ba8tDrOI.mjs","names":[],"sources":["../src/core/abort.ts"],"sourcesContent":["export function abortReason(signal: AbortSignal): unknown {\n if (signal.reason !== undefined) {\n return signal.reason;\n }\n const error = new Error(\"This operation was aborted\");\n error.name = \"AbortError\";\n return error;\n}\n\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted === true) {\n throw abortReason(signal);\n }\n}\n\n/**\n * Race `promise` against the caller's `signal` without cancelling the shared work: on abort the\n * returned promise rejects with `signal.reason` (or an `AbortError`-named Error) while `promise`\n * itself keeps running. With no signal the original promise is returned.\n */\nexport async function raceSignal<T>(\n promise: Promise<T>,\n signal: AbortSignal | undefined,\n): Promise<T> {\n if (!signal) {\n return promise;\n }\n if (signal.aborted) {\n throw abortReason(signal);\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = (): void => {\n // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- the rejection is the caller's signal.reason verbatim\n reject(abortReason(signal));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n void (async (): Promise<void> => {\n try {\n resolve(await promise);\n } catch (error) {\n // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- forwards the raced promise's own rejection\n reject(error);\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n })();\n });\n}\n"],"mappings":";AAAA,SAAgB,YAAY,QAA8B;CACxD,IAAI,OAAO,WAAW,KAAA,GACpB,OAAO,OAAO;CAEhB,MAAM,wBAAQ,IAAI,MAAM,4BAA4B;CACpD,MAAM,OAAO;CACb,OAAO;AACT;AAEA,SAAgB,eAAe,QAAuC;CACpE,IAAI,QAAQ,YAAY,MACtB,MAAM,YAAY,MAAM;AAE5B;;;;;;AAOA,eAAsB,WACpB,SACA,QACY;CACZ,IAAI,CAAC,QACH,OAAO;CAET,IAAI,OAAO,SACT,MAAM,YAAY,MAAM;CAE1B,OAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,gBAAsB;GAE1B,OAAO,YAAY,MAAM,CAAC;EAC5B;EACA,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACxD,CAAM,YAA2B;GAC/B,IAAI;IACF,QAAQ,MAAM,OAAO;GACvB,SAAS,OAAO;IAEd,OAAO,KAAK;GACd,UAAU;IACR,OAAO,oBAAoB,SAAS,OAAO;GAC7C;EACF,EAAA,CAAG;CACL,CAAC;AACH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"builder-9tjI6WQ3.mjs","names":[],"sources":["../src/core/builder.ts"],"sourcesContent":["import { EventValidationError } from \"./error.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"./event.ts\";\nimport { finalizeEvent } from \"./key.ts\";\nimport type { Keys, SecretKey } from \"./key.ts\";\nimport { Kind, isAddressableKind, isReplaceableKind } from \"./kind.ts\";\nimport { Tag, formatEventAddress, getDTag, parseEventAddress } from \"./tag.ts\";\nimport { normalizeURL } from \"./util.ts\";\n\nfunction hasProtectedTag(event: Event): boolean {\n for (const tag of event.tags) {\n if (tag[0] === \"-\") {\n return true;\n }\n }\n return false;\n}\n\n/** NIP-18 e third entry MUST be a relay URL; empty string is not one. */\nfunction requireRelayUrl(relayHint: string | undefined): string {\n if (relayHint === undefined || relayHint === \"\") {\n throw new EventValidationError(\"relayHint must be a relay URL\");\n }\n return normalizeURL(relayHint);\n}\n\n/** Profile metadata JSON (kind 0 content). NIP-05 is not verified here. */\nexport type ProfileMetadata = {\n name?: string;\n display_name?: string;\n about?: string;\n picture?: string;\n banner?: string;\n website?: string;\n nip05?: string;\n lud06?: string;\n lud16?: string;\n};\n\n/**\n * Fluent builder for event templates. Decouples intent (kind/content/tags) from signing (Keys /\n * NostrSigner).\n */\nexport class EventBuilder {\n #kind: number;\n #content: string;\n readonly #tags: Tag[];\n #createdAt: number | undefined;\n\n constructor(kind: number, content = \"\") {\n this.#kind = kind;\n this.#content = content;\n this.#tags = [];\n this.#createdAt = undefined;\n }\n\n static textNote(content: string): EventBuilder {\n return new EventBuilder(Kind.TextNote, content);\n }\n\n static metadata(meta: ProfileMetadata): EventBuilder {\n return new EventBuilder(Kind.Metadata, JSON.stringify(meta));\n }\n\n static contacts(pubkeys: string[]): EventBuilder {\n const b = new EventBuilder(Kind.Contacts, \"\");\n for (const pk of pubkeys) {\n b.#tags.push(Tag.p(pk));\n }\n return b;\n }\n\n /**\n * Kind-5 deletion. Each target is a bare event id string (or `{ id, kind }`), or `{ address }`\n * for a `kind:pubkey:d` coordinate. Known kinds are emitted as deduped `k` tags (NIP-09 SHOULD).\n */\n static deletion(\n targets: ReadonlyArray<string | { id: string; kind?: number } | { address: string }>,\n reason = \"\",\n ): EventBuilder {\n const b = new EventBuilder(Kind.EventDeletion, reason);\n const kinds = new Set<number>();\n for (const target of targets) {\n if (typeof target === \"string\") {\n b.#tags.push(Tag.e(target));\n continue;\n }\n if (\"address\" in target) {\n const parsed = parseEventAddress(target.address);\n if (parsed) {\n kinds.add(parsed.kind);\n }\n b.#tags.push(Tag.a(target.address));\n continue;\n }\n b.#tags.push(Tag.e(target.id));\n if (target.kind !== undefined) {\n kinds.add(target.kind);\n }\n }\n for (const kind of kinds) {\n b.#tags.push(Tag.k(kind));\n }\n return b;\n }\n\n static reaction(target: Event, content = \"+\", opts?: { relayHint?: string }): EventBuilder {\n const hint = opts?.relayHint === undefined ? undefined : requireRelayUrl(opts.relayHint);\n let coord: string | undefined;\n if (isAddressableKind(target.kind)) {\n const d = getDTag(target.tags);\n if (d === undefined) {\n throw new EventValidationError(\"addressable event is missing d tag\");\n }\n coord = formatEventAddress(target.kind, target.pubkey, d);\n }\n\n const b = new EventBuilder(Kind.Reaction, content);\n // NIP-25 e is [e, id, relay, pubkey]; Tag.e third arg is a NIP-10 marker.\n b.#tags.push([\"e\", target.id.toLowerCase(), hint ?? \"\", target.pubkey.toLowerCase()]);\n b.#tags.push(Tag.p(target.pubkey, hint));\n b.#tags.push([\"k\", String(target.kind)]);\n if (coord !== undefined) {\n b.#tags.push(Tag.a(coord, hint));\n }\n return b;\n }\n\n static repost(target: Event, opts: { relayHint: string }): EventBuilder {\n if (target.kind !== Kind.TextNote) {\n throw new EventValidationError(\"non-kind-1 uses EventBuilder.genericRepost\");\n }\n const hint = requireRelayUrl(opts?.relayHint);\n const content = hasProtectedTag(target) ? \"\" : JSON.stringify(target);\n const b = new EventBuilder(Kind.Repost, content);\n b.#tags.push(Tag.e(target.id, hint));\n b.#tags.push(Tag.p(target.pubkey));\n return b;\n }\n\n static genericRepost(target: Event, opts: { relayHint: string; pPubkey?: string }): EventBuilder {\n if (target.kind === Kind.TextNote) {\n throw new EventValidationError(\"kind 1 uses EventBuilder.repost\");\n }\n const hint = requireRelayUrl(opts?.relayHint);\n const replaceable = isReplaceableKind(target.kind);\n const addressable = isAddressableKind(target.kind);\n const d = getDTag(target.tags);\n if (addressable && d === undefined) {\n throw new EventValidationError(\"addressable event is missing d tag\");\n }\n\n const content =\n hasProtectedTag(target) || replaceable || addressable ? \"\" : JSON.stringify(target);\n const b = new EventBuilder(Kind.GenericRepost, content);\n b.#tags.push(Tag.e(target.id, hint));\n b.#tags.push(Tag.p(opts?.pPubkey ?? target.pubkey));\n b.#tags.push(Tag.k(target.kind));\n if (replaceable || addressable) {\n b.#tags.push(Tag.a(formatEventAddress(target.kind, target.pubkey, d ?? \"\")));\n }\n return b;\n }\n\n kind(kind: number): this {\n this.#kind = kind;\n return this;\n }\n\n content(content: string): this {\n this.#content = content;\n return this;\n }\n\n tag(tag: Tag): this {\n this.#tags.push(tag);\n return this;\n }\n\n tags(tags: Iterable<Tag>): this {\n for (const t of tags) {\n this.#tags.push(t);\n }\n return this;\n }\n\n createdAt(ts: number): this {\n this.#createdAt = ts;\n return this;\n }\n\n get currentKind(): number {\n return this.#kind;\n }\n\n get currentContent(): string {\n return this.#content;\n }\n\n get currentTags(): ReadonlyArray<Tag> {\n return this.#tags;\n }\n\n get currentCreatedAt(): number | undefined {\n return this.#createdAt;\n }\n\n /** Snapshot as EventTemplate (uses wall clock if created_at unset). */\n toTemplate(): EventTemplate {\n return {\n kind: this.#kind,\n content: this.#content,\n tags: [...this.#tags],\n created_at: this.#createdAt ?? Math.floor(Date.now() / 1000),\n };\n }\n\n /** Build unsigned event with the given pubkey. */\n buildUnsigned(pubkey: string): UnsignedEvent {\n const template = this.toTemplate();\n return {\n ...template,\n pubkey: pubkey.toLowerCase(),\n };\n }\n\n /** Sign with local Keys (synchronous). */\n signWithKeys(keys: Keys | SecretKeyLike): Event {\n return finalizeEvent(this.toTemplate(), keys);\n }\n\n /**\n * Sign via any NostrSigner-shaped object. Accepts a structural type so core does not depend on\n * the signer module.\n */\n async sign(signer: {\n getPublicKey: () => Promise<string>;\n signEvent: (unsigned: UnsignedEvent) => Promise<Event>;\n }): Promise<Event> {\n const pubkey = await signer.getPublicKey();\n return signer.signEvent(this.buildUnsigned(pubkey));\n }\n}\n\ntype SecretKeyLike = SecretKey | Uint8Array | string;\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-CadO5fAX.mjs","names":[],"sources":["../src/client/builder.ts","../src/client/dm.ts","../src/client/types.ts","../src/client/gossip-io.ts","../src/client/sync.ts","../src/client/client.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport type { InvalidEventPolicy } from \"../relay/pool.ts\";\nimport type { WebSocketConstructor } from \"../relay/websocket.ts\";\nimport type { NostrSigner } from \"../signer/types.ts\";\nimport type { StorageError } from \"../storage/error.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\nimport type { Client } from \"./client.ts\";\nimport type { ClientOptions } from \"./types.ts\";\n\n/**\n * Fluent constructor for {@link Client}. The constructor is supplied by {@link Client.builder} so\n * this module never imports the `Client` value, keeping the client/builder dependency\n * one-directional.\n */\nexport class ClientBuilder {\n #opts: ClientOptions = {};\n readonly #newClient: (opts: ClientOptions) => Client;\n\n constructor(newClient: (opts: ClientOptions) => Client) {\n this.#newClient = newClient;\n }\n\n signer(signer: NostrSigner): this {\n this.#opts = { ...this.#opts, signer };\n return this;\n }\n\n relays(urls: ReadonlyArray<string>): this {\n this.#opts = { ...this.#opts, relays: [...urls] };\n return this;\n }\n\n addRelay(url: string): this {\n this.#opts = { ...this.#opts, relays: [...(this.#opts.relays ?? []), url] };\n return this;\n }\n\n websocketImplementation(impl: WebSocketConstructor): this {\n this.#opts = { ...this.#opts, websocketImplementation: impl };\n return this;\n }\n\n verifyEvent(fn: (event: Event) => boolean): this {\n this.#opts = { ...this.#opts, verifyEvent: fn };\n return this;\n }\n\n connectTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, connectTimeoutMs: ms };\n return this;\n }\n\n publishTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, publishTimeoutMs: ms };\n return this;\n }\n\n automaticAuth(enabled: boolean): this {\n this.#opts = { ...this.#opts, automaticAuth: enabled };\n return this;\n }\n\n /** Allow `ws://` relays. Default false. */\n allowInsecure(allow: boolean): this {\n this.#opts = { ...this.#opts, allowInsecure: allow };\n return this;\n }\n\n /** `ws://` URLs allowed despite `allowInsecure` being off. */\n trustedInsecureUrls(urls: ReadonlyArray<string>): this {\n this.#opts = { ...this.#opts, trustedInsecureUrls: urls };\n return this;\n }\n\n /** Close idle relays after this many ms. Unset = disabled. */\n idleTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, idleTimeoutMs: ms };\n return this;\n }\n\n /** Soft cap on connected non-pinned relays. */\n maxRelays(n: number): this {\n this.#opts = { ...this.#opts, maxRelays: n };\n return this;\n }\n\n /** Relays never closed by idle cleanup or `maxRelays` eviction. */\n pinnedUrls(urls: ReadonlyArray<string>): this {\n this.#opts = { ...this.#opts, pinnedUrls: urls };\n return this;\n }\n\n /** Forwarded to the pool: suspend a relay delivering too many invalid events. */\n invalidEventPolicy(policy: InvalidEventPolicy): this {\n this.#opts = { ...this.#opts, invalidEventPolicy: policy };\n return this;\n }\n\n /** Forwarded to the pool: fired once when a relay is suspended, with the lift time. */\n onRelaySuspended(cb: (url: string, until: number) => void): this {\n this.#opts = { ...this.#opts, onRelaySuspended: cb };\n return this;\n }\n\n enableReconnect(enabled: boolean): this {\n this.#opts = { ...this.#opts, enableReconnect: enabled };\n return this;\n }\n\n enablePing(enabled: boolean): this {\n this.#opts = { ...this.#opts, enablePing: enabled };\n return this;\n }\n\n pingIntervalMs(ms: number): this {\n this.#opts = { ...this.#opts, pingIntervalMs: ms };\n return this;\n }\n\n pingTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, pingTimeoutMs: ms };\n return this;\n }\n\n /**\n * Local event store. Defaults to {@link MemoryEventStore}. Browser apps that want persistence must\n * pass {@link IndexedDbEventStore} and `await open()`.\n */\n storage(store: EventStore): this {\n this.#opts = { ...this.#opts, storage: store };\n return this;\n }\n\n /**\n * Synchronous reactive index mirroring ingested events. Defaults to a new\n * {@link ReactiveEventStore}.\n */\n index(index: ReactiveEventStore): this {\n this.#opts = { ...this.#opts, index };\n return this;\n }\n\n /** When false, Client will not auto-write events to storage. Default true. */\n persistEvents(enabled: boolean): this {\n this.#opts = { ...this.#opts, persistEvents: enabled };\n return this;\n }\n\n gossip(gossip: Gossip): this {\n this.#opts = { ...this.#opts, gossip };\n return this;\n }\n\n onstorageerror(fn: (err: StorageError) => void): this {\n this.#opts = { ...this.#opts, onstorageerror: fn };\n return this;\n }\n\n build(): Client {\n return this.#newClient(this.#opts);\n }\n}\n","import type { EventBuilder } from \"../core/builder.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { itemCompare } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport {\n Nip17Error,\n buildChatMessageRumor,\n dmRelayListEventBuilder,\n normalizeRecipients,\n requireDmRelays,\n wrapDirectMessage,\n} from \"../nips/nip17.ts\";\nimport type { Recipient } from \"../nips/nip17.ts\";\nimport { unwrap } from \"../nips/nip59.ts\";\nimport type { Nip59Crypto } from \"../nips/nip59.ts\";\nimport type { Pool, PoolPublishResult } from \"../relay/pool.ts\";\nimport type {\n FetchPrivateMessagesOptions,\n PrivateMessageSendResult,\n PublishOptions,\n ReceivedPrivateMessage,\n SendPrivateMessageOptions,\n SubscribePrivateMessagesOptions,\n} from \"./types.ts\";\n\nexport type DmDeps = {\n pool: Pool;\n gossip: Gossip;\n hydrateGossip: (pubkeys: ReadonlyArray<string>) => Promise<void>;\n ingest: (event: Event, relayUrl?: string) => void;\n markSeen: (id: string, relayUrl: string) => void;\n assertAlive: () => void;\n requireNip59Crypto: () => Nip59Crypto;\n throwIfAborted: (signal?: AbortSignal) => void;\n wantObserve: (flag?: boolean) => boolean;\n publish: (\n eventOrBuilder: Event | EventBuilder,\n opts?: PublishOptions,\n ) => Promise<PoolPublishResult[]>;\n};\n\nexport function giftWrapRelays(gossip: Gossip, event: Event): string[] {\n const targets: string[] = [];\n for (const tag of event.tags) {\n if (tag[0] === \"p\" && tag[1] !== undefined && tag[1] !== \"\") {\n targets.push(tag[1].toLowerCase());\n }\n }\n if (targets.length === 0) {\n throw new Nip17Error(\"gift wrap has no p-tag recipient\");\n }\n const [target] = targets;\n if (targets.length !== 1 || target === undefined) {\n throw new Nip17Error(\"gift wrap must have exactly one p-tag recipient\");\n }\n const relays = gossip.dmRelays(target);\n if (relays.length === 0) {\n throw new Nip17Error(\"no kind 10050 in gossip; use sendPrivateMessage or pass relays\");\n }\n return relays;\n}\n\nexport async function setDmRelays(\n deps: DmDeps,\n relays: ReadonlyArray<string>,\n opts?: PublishOptions,\n): Promise<PoolPublishResult[]> {\n return deps.publish(dmRelayListEventBuilder(relays), { gossip: true, ...opts });\n}\n\nexport async function sendPrivateMessage(\n deps: DmDeps,\n recipients: string | Recipient | ReadonlyArray<string | Recipient>,\n content: string,\n opts?: SendPrivateMessageOptions,\n): Promise<PrivateMessageSendResult> {\n deps.assertAlive();\n const crypto = deps.requireNip59Crypto();\n const sender = await crypto.getPublicKey();\n const list = normalizeRecipients(recipients);\n if (list.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n\n const targets = new Set<string>([sender.toLowerCase(), ...list.map((r) => r.pubkey)]);\n await deps.hydrateGossip([...targets]);\n for (const pk of targets) {\n requireDmRelays(pk, deps.gossip.dmRelays(pk));\n }\n\n const rumor = buildChatMessageRumor(sender, list, content, {\n created_at: opts?.created_at,\n subject: opts?.subject,\n replyTo: opts?.replyTo,\n });\n const wraps = await wrapDirectMessage(crypto, list, rumor);\n const sent = await Promise.all(\n wraps.map(async ({ recipient, wrap }) => {\n const relays = requireDmRelays(recipient, deps.gossip.dmRelays(recipient));\n const results = await deps.pool.publish(relays, wrap, { timeoutMs: opts?.timeoutMs });\n if (results.some((r) => r.result?.ok === true) && deps.wantObserve(opts?.observe)) {\n deps.ingest(wrap);\n }\n return { recipient, wrap, results };\n }),\n );\n return { rumor, wraps: sent };\n}\n\nexport async function fetchPrivateMessages(\n deps: DmDeps,\n opts?: FetchPrivateMessagesOptions,\n): Promise<ReceivedPrivateMessage[]> {\n deps.assertAlive();\n const crypto = deps.requireNip59Crypto();\n const self = await crypto.getPublicKey();\n deps.throwIfAborted(opts?.signal);\n await deps.hydrateGossip([self]);\n deps.throwIfAborted(opts?.signal);\n const relays = requireDmRelays(self, deps.gossip.dmRelays(self));\n const urls = new Map<string, Set<string>>();\n const events = await deps.pool.fetch(\n relays,\n [\n {\n kinds: [Kind.GiftWrap],\n \"#p\": [self],\n since: opts?.since,\n until: opts?.until,\n },\n ],\n {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent: (event, relayUrl) => {\n let set = urls.get(event.id);\n if (!set) {\n urls.set(event.id, (set = new Set()));\n }\n set.add(relayUrl);\n },\n },\n );\n\n const byRumor = new Map<string, ReceivedPrivateMessage>();\n for (const wrap of events) {\n try {\n // oxlint-disable-next-line no-await-in-loop -- unwraps run serially so ingest/seenOn keep relay order\n const rumor = await unwrap(crypto, wrap);\n const wrapUrls = urls.get(wrap.id);\n const firstUrl = wrapUrls?.values().next().value;\n if (deps.wantObserve(opts?.observe)) {\n deps.ingest(wrap, firstUrl);\n // Every other relay that delivered the same wrap is recorded too.\n if (wrapUrls) {\n for (const url of wrapUrls) {\n if (url !== firstUrl) {\n deps.markSeen(wrap.id, url);\n }\n }\n }\n }\n byRumor.set(rumor.id, {\n wrap,\n rumor,\n ...(firstUrl === undefined ? {} : { relayUrl: firstUrl }),\n });\n } catch {\n // junk / forgery / key mismatch — not stored\n }\n }\n\n return [...byRumor.values()].sort((a, b) => itemCompare(a.rumor, b.rumor));\n}\n\nexport async function subscribePrivateMessages(\n deps: DmDeps,\n opts?: SubscribePrivateMessagesOptions,\n): Promise<{ close: (reason?: string) => void }> {\n deps.assertAlive();\n const crypto = deps.requireNip59Crypto();\n const self = await crypto.getPublicKey();\n deps.throwIfAborted(opts?.signal);\n await deps.hydrateGossip([self]);\n deps.throwIfAborted(opts?.signal);\n const relays = requireDmRelays(self, deps.gossip.dmRelays(self));\n\n const seen = new Set<string>();\n // Wrap ids processed (observed) already; receipts before that are buffered\n // so every delivering relay URL lands in seenOn once the wrap is indexed.\n const processed = new Set<string>();\n const pendingUrls = new Map<string, string[]>();\n const flushSeen = (wrapId: string): void => {\n processed.add(wrapId);\n const extra = pendingUrls.get(wrapId);\n if (extra === undefined) {\n return;\n }\n pendingUrls.delete(wrapId);\n for (const url of extra) {\n deps.markSeen(wrapId, url);\n }\n };\n let tail = Promise.resolve();\n let closed = false;\n const markClosed = (): void => {\n closed = true;\n };\n if (opts?.signal?.aborted === true) {\n markClosed();\n } else {\n opts?.signal?.addEventListener(\"abort\", markClosed, { once: true });\n }\n\n const inner = deps.pool.subscribe(\n relays,\n // 21059 is ephemeral (relays MUST NOT store); live inbox has to REQ it.\n [{ kinds: [Kind.GiftWrap, Kind.GiftWrapEphemeral], \"#p\": [self], since: opts?.since }],\n {\n signal: opts?.signal,\n oneose: opts?.oneose,\n onclose: (reason) => {\n markClosed();\n opts?.onclose?.(reason);\n },\n eoseTimeoutMs: opts?.eoseTimeoutMs,\n receivedEvent: (id, relayUrl) => {\n if (closed) {\n return;\n }\n if (processed.has(id)) {\n deps.markSeen(id, relayUrl);\n return;\n }\n const list = pendingUrls.get(id);\n if (list === undefined) {\n pendingUrls.set(id, [relayUrl]);\n } else if (!list.includes(relayUrl)) {\n list.push(relayUrl);\n }\n },\n onevent: (wrap, relayUrl) => {\n if (closed) {\n return;\n }\n const prev = tail;\n let release: (() => void) | undefined;\n tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n void (async () => {\n await prev;\n try {\n if (closed) {\n return;\n }\n const rumor = await unwrap(crypto, wrap);\n if (closed) {\n return;\n }\n if (seen.has(rumor.id)) {\n flushSeen(wrap.id);\n return;\n }\n seen.add(rumor.id);\n if (deps.wantObserve(opts?.observe)) {\n deps.ingest(wrap, relayUrl);\n }\n flushSeen(wrap.id);\n opts?.onevent?.({ wrap, rumor, relayUrl });\n } catch {\n // junk / forgery — not stored; keep the queue alive if a handler throws\n } finally {\n release?.();\n }\n })();\n },\n },\n );\n\n return {\n close: (reason?: string) => {\n markClosed();\n inner.close(reason);\n },\n };\n}\n","import { NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport type { ReplyTo } from \"../nips/nip17.ts\";\nimport type { Rumor } from \"../nips/nip59.ts\";\nimport type { InvalidEventPolicy, PoolPublishResult } from \"../relay/pool.ts\";\nimport type { WebSocketConstructor } from \"../relay/websocket.ts\";\nimport type { NostrSigner } from \"../signer/types.ts\";\nimport type { StorageError } from \"../storage/error.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\n\n/** Direction of a {@link Client.sync} run: upload, download, or both. */\nexport const SyncDirection = {\n Up: \"up\",\n Down: \"down\",\n Both: \"both\",\n} as const;\n\n/** Union of the {@link SyncDirection} values. */\nexport type SyncDirectionName = (typeof SyncDirection)[keyof typeof SyncDirection];\n\n/** Options for {@link Client.sync} (NIP-77 set reconciliation). */\nexport type SyncOptions = {\n relays?: ReadonlyArray<string>;\n direction?: SyncDirectionName;\n /**\n * Wall-clock deadline for the Negentropy reconciliation session (`NEG-OPEN` through `NEG-CLOSE`),\n * in milliseconds. One clock for the whole session — not reset per `NEG-MSG`. Default: the relay\n * `publishTimeoutMs`. Upload/download phases reuse this value as their per-call timeout.\n */\n timeoutMs?: number;\n signal?: AbortSignal;\n dryRun?: boolean;\n /** When false, skip observe/storage on downloaded events. Default true. */\n observe?: boolean;\n};\n\n/** Outcome of a {@link Client.sync} run: ids compared, sent, received, and failures. */\nexport type SyncSummary = {\n local: string[];\n remote: string[];\n sent: string[];\n received: string[];\n sendFailures: Record<string, string>;\n persistFailures: Record<string, string>;\n};\n\n/** Options for {@link Client.builder} / `ClientOptions` accepted by the builder. */\nexport type ClientOptions = {\n signer?: NostrSigner;\n relays?: ReadonlyArray<string>;\n websocketImplementation?: WebSocketConstructor;\n /** Injected EVENT verifier. Default is core BIP-340. */\n verifyEvent?: (event: Event) => boolean;\n connectTimeoutMs?: number;\n publishTimeoutMs?: number;\n /**\n * When true (default), answer NIP-42 AUTH automatically. The signer is read at challenge time, so\n * `setSigner()` applies to live connections; challenges received while no signer is set are\n * ignored.\n */\n automaticAuth?: boolean;\n /** When true (default), relays reconnect with backoff after disconnect. */\n enableReconnect?: boolean;\n /** Keepalive ping. Default false. */\n enablePing?: boolean;\n pingIntervalMs?: number;\n pingTimeoutMs?: number;\n /** Forwarded to the pool: when false (default), `ws://` relays are rejected. */\n allowInsecure?: boolean;\n /** Forwarded to the pool: `ws://` URLs allowed despite `allowInsecure`. */\n trustedInsecureUrls?: ReadonlyArray<string>;\n /** Forwarded to the pool: close idle relays after this many ms. */\n idleTimeoutMs?: number;\n /** Forwarded to the pool: soft cap on connected non-pinned relays. */\n maxRelays?: number;\n /** Forwarded to the pool: relays never closed by idle cleanup or `maxRelays`. */\n pinnedUrls?: ReadonlyArray<string>;\n /**\n * Forwarded to the pool: a relay exceeding `limit` verification failures inside `windowMs` is\n * disconnected and suspended for `cooldownMs`.\n */\n invalidEventPolicy?: InvalidEventPolicy;\n /** Forwarded to the pool: fired once when a relay is suspended, with the lift time. */\n onRelaySuspended?: (url: string, until: number) => void;\n gossip?: Gossip;\n /**\n * Local event store. Defaults to {@link MemoryEventStore}. Browser apps that want persistence must\n * pass {@link IndexedDbEventStore} and `await open()`.\n */\n storage?: EventStore;\n /**\n * Synchronous reactive index that mirrors every ingested event before callbacks and persistence.\n * Defaults to a new {@link ReactiveEventStore}.\n */\n index?: ReactiveEventStore;\n /**\n * When true (default), every ingested event is written to storage. Set false to disable automatic\n * persistence while keeping the store for manual use.\n */\n persistEvents?: boolean;\n /**\n * Storage I/O failures: live `putMany` flush and `fetchEvents({ localFirst: true })` query. Those\n * paths do not throw.\n */\n onstorageerror?: (err: StorageError) => void;\n};\n\nexport type FetchEventsOptions = {\n relays?: string[];\n timeoutMs?: number;\n signal?: AbortSignal;\n gossip?: boolean;\n /** When true, query local storage first and merge with network results. Defaults to false. */\n localFirst?: boolean;\n /** When false, skip writing fetched events to storage/observe. Default true. */\n observe?: boolean;\n /**\n * Every event of every relay batch, including duplicates across relays. `relayUrl` is the\n * normalized URL of the relay that delivered it.\n */\n onevent?: (event: Event, relayUrl: string) => void;\n};\n\n/** Options for {@link Client.fetchEach}: one-shot per-relay fetch, no gossip routing. */\nexport type FetchEachOptions = {\n relays?: string[];\n timeoutMs?: number;\n signal?: AbortSignal;\n /** When false, skip writing fetched events to storage/observe. Default true. */\n observe?: boolean;\n};\n\nexport type SubscribeOptions = {\n relays?: string[];\n /**\n * First receipt of each event only (deduped across relays). `relayUrl` is the normalized URL of\n * the relay that delivered it first.\n */\n onevent?: (event: Event, relayUrl: string) => void;\n /** Every receipt from every relay, including duplicates skipped by dedupe. */\n receivedEvent?: (id: string, relayUrl: string) => void;\n oneose?: () => void;\n onclose?: (reason: string) => void;\n signal?: AbortSignal;\n id?: string;\n /**\n * If set, fire `oneose` once after this many ms if not all relays have EOSEd. Does not close the\n * subscription.\n */\n eoseTimeoutMs?: number;\n /** Fan out REQs via NIP-65 gossip routes when available. */\n gossip?: boolean;\n /** When false, skip writing received events to storage/observe. Default true. */\n observe?: boolean;\n};\n\nexport type PublishOptions = {\n relays?: string[];\n timeoutMs?: number;\n gossip?: boolean;\n /** When false, skip writing the published event to storage/observe. Default true. */\n observe?: boolean;\n};\n\n/** Options for sending a NIP-17 private message. */\nexport type SendPrivateMessageOptions = {\n readonly subject?: string;\n readonly replyTo?: ReplyTo;\n readonly created_at?: number;\n readonly timeoutMs?: number;\n readonly observe?: boolean;\n};\n\n/** Result of sending a NIP-17 DM: the rumor plus per-recipient wrap publish results. */\nexport type PrivateMessageSendResult = {\n rumor: Rumor;\n wraps: ReadonlyArray<{\n recipient: string;\n wrap: Event;\n results: PoolPublishResult[];\n }>;\n};\n\n/** An unwrapped NIP-17 private message: the received gift wrap and its inner rumor. */\nexport type ReceivedPrivateMessage = {\n wrap: Event;\n rumor: Rumor;\n /** Normalized URL of the relay that delivered the wrap, when known. */\n relayUrl?: string;\n};\n\n/** Options for fetching NIP-17 private-message history. */\nexport type FetchPrivateMessagesOptions = {\n readonly since?: number;\n readonly until?: number;\n readonly timeoutMs?: number;\n readonly signal?: AbortSignal;\n readonly observe?: boolean;\n};\n\n/** Options for a live NIP-17 private-message subscription. */\nexport type SubscribePrivateMessagesOptions = {\n readonly since?: number;\n readonly onevent?: (msg: ReceivedPrivateMessage) => void;\n readonly oneose?: () => void;\n readonly onclose?: (reason: string) => void;\n readonly signal?: AbortSignal;\n readonly eoseTimeoutMs?: number;\n readonly observe?: boolean;\n};\n\n/** Client lifecycle, configuration, or abort failure (not cryptographic). */\nexport class ClientError extends NostrError {\n override name = \"ClientError\";\n}\n","import type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport { fanIn, fetchRouted } from \"../relay/fan-in.ts\";\nimport type { FanInOptions, RoutedJob } from \"../relay/fan-in.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport { ClientError } from \"./types.ts\";\n\n/** Remainder is one job on defaults; throw before any REQ when defaults are empty. */\nexport function jobsForFilters(\n gossip: Gossip,\n filters: Filter[],\n defaultRelays: () => string[],\n): RoutedJob[] {\n const routed = filters.map((f) => gossip.route(f));\n const needsDefaults = routed.some((r) => r.remainder !== undefined);\n const defaults = needsDefaults ? defaultRelays() : undefined;\n const jobs: RoutedJob[] = [];\n for (const r of routed) {\n for (const [url, sub] of r.perRelay) {\n jobs.push({ urls: [url], filters: [sub] });\n }\n if (r.remainder !== undefined) {\n if (defaults === undefined) {\n throw new ClientError(\"unrouted remainder without default relays\");\n }\n jobs.push({ urls: defaults, filters: [r.remainder] });\n }\n }\n return jobs;\n}\n\nexport async function fetchGossip(\n pool: Pool,\n gossip: Gossip,\n filters: Filter[],\n defaultRelays: () => string[],\n opts?: {\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n onevent?: ((event: Event, relayUrl: string) => void) | undefined;\n },\n): Promise<Event[]> {\n return fetchRouted(pool, jobsForFilters(gossip, filters, defaultRelays), {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent: opts?.onevent,\n });\n}\n\nexport function subscribeGossip(\n pool: Pool,\n gossip: Gossip,\n filters: Filter[],\n defaultRelays: () => string[],\n opts: FanInOptions,\n): { close: (reason?: string) => void } {\n const jobs = jobsForFilters(gossip, filters, defaultRelays);\n if (jobs.length === 0) {\n queueMicrotask(() => opts.oneose?.());\n return {\n close: () => {\n /* empty */\n },\n };\n }\n return fanIn(pool, jobs, opts);\n}\n","import type { Event } from \"../core/event.ts\";\nimport { canonicalizeFilter } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { storageFromItems } from \"../nips/nip77.ts\";\nimport type { NegentropyStorageVector } from \"../nips/nip77.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport type { EventStore, PutResult } from \"../storage/types.ts\";\nimport { SyncDirection } from \"./types.ts\";\nimport type { SyncOptions, SyncSummary } from \"./types.ts\";\n\nexport type SyncDeps = {\n pool: Pool;\n storage: EventStore;\n persistEvents: boolean;\n assertAlive: () => void;\n throwIfAborted: (signal?: AbortSignal) => void;\n wantObserve: (flag?: boolean) => boolean;\n /**\n * The client's single ingest path: index (with relay URL) → meta → persistence. `persist: false`\n * when this function already wrote storage via an awaited `putMany`.\n */\n ingest: (event: Event, relayUrl?: string, opts?: { persist?: boolean }) => void;\n /** Record a relay sighting for an id already in the index. */\n markSeen: (id: string, relayUrl: string) => void;\n defaultRelays: (urls?: string[]) => string[];\n};\n\nconst SYNC_ID_BATCH = 100;\nconst SYNC_UPLOAD_CONCURRENCY = 8;\n\nfunction uniqueIds(ids: ReadonlyArray<string>): string[] {\n return [...new Set(ids)];\n}\n\nfunction emptySummary(): SyncSummary {\n return {\n local: [],\n remote: [],\n sent: [],\n received: [],\n sendFailures: {},\n persistFailures: {},\n };\n}\n\nfunction mergeSyncSummary(into: SyncSummary, other: SyncSummary): SyncSummary {\n const sendFailures = { ...into.sendFailures, ...other.sendFailures };\n const persistFailures = { ...into.persistFailures, ...other.persistFailures };\n return {\n local: uniqueIds([...into.local, ...other.local]),\n remote: uniqueIds([...into.remote, ...other.remote]),\n sent: uniqueIds([...into.sent, ...other.sent]),\n received: uniqueIds([...into.received, ...other.received]),\n sendFailures,\n persistFailures,\n };\n}\n\n/**\n * NIP-77 sync against one relay: reconcile, then optionally upload local-only events and/or\n * download remote-only events. `observe: false` skips putMany and ingest; received ids are still\n * listed. `persistEvents: false` skips putMany, still ingests when observe is on.\n */\nexport async function syncToRelay(\n deps: SyncDeps,\n url: string,\n filter: Filter,\n opts?: Omit<SyncOptions, \"relays\">,\n): Promise<SyncSummary> {\n deps.assertAlive();\n deps.throwIfAborted(opts?.signal);\n const direction = opts?.direction ?? SyncDirection.Down;\n const canon = canonicalizeFilter(filter);\n const items = await deps.storage.negentropyItems(canon);\n const storage: NegentropyStorageVector = storageFromItems(items);\n const relay = await deps.pool.ensureRelay(url, { signal: opts?.signal });\n const { have, need } = await relay.negReconcile(canon, storage, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n });\n\n const summary: SyncSummary = {\n local: have,\n remote: need,\n sent: [],\n received: [],\n sendFailures: {},\n persistFailures: {},\n };\n\n if (opts?.dryRun === true) {\n return summary;\n }\n\n if ((direction === SyncDirection.Up || direction === SyncDirection.Both) && have.length > 0) {\n const found = await deps.storage.query([{ ids: have }]);\n const foundById = new Map(found.map((event) => [event.id, event]));\n for (const id of have) {\n if (!foundById.has(id)) {\n summary.sendFailures[id] = \"event not found in local store\";\n }\n }\n for (let i = 0; i < found.length; i += SYNC_UPLOAD_CONCURRENCY) {\n const chunk = found.slice(i, i + SYNC_UPLOAD_CONCURRENCY);\n // Upload chunks are rate-limited serially.\n // oxlint-disable-next-line no-await-in-loop\n await Promise.all(\n chunk.map(async (event) => {\n try {\n const results = await deps.pool.publish([url], event, {\n timeoutMs: opts?.timeoutMs,\n });\n const ok = results.some((r) => r.result?.ok === true);\n if (ok) {\n summary.sent.push(event.id);\n } else {\n summary.sendFailures[event.id] =\n results[0]?.error ?? results[0]?.result?.message ?? \"publish failed\";\n }\n } catch (error) {\n summary.sendFailures[event.id] = error instanceof Error ? error.message : String(error);\n }\n }),\n );\n }\n }\n\n if ((direction === SyncDirection.Down || direction === SyncDirection.Both) && need.length > 0) {\n const shouldObserve = deps.wantObserve(opts?.observe);\n for (let i = 0; i < need.length; i += SYNC_ID_BATCH) {\n const batch = need.slice(i, i + SYNC_ID_BATCH);\n deps.throwIfAborted(opts?.signal);\n // Every relay that delivered an event is recorded once it is ingested.\n const urlsById = new Map<string, string[]>();\n // Id batches are fetched serially so backpressure stays bounded.\n // oxlint-disable-next-line no-await-in-loop\n const events = await deps.pool.fetch([url], [{ ids: batch }], {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent: shouldObserve\n ? (event, relayUrl) => {\n const urls = urlsById.get(event.id);\n if (urls === undefined) {\n urlsById.set(event.id, [relayUrl]);\n } else if (!urls.includes(relayUrl)) {\n urls.push(relayUrl);\n }\n }\n : undefined,\n });\n if (!shouldObserve) {\n for (const event of events) {\n summary.received.push(event.id);\n }\n continue;\n }\n const ingestAll = (event: Event): void => {\n const urls = urlsById.get(event.id) ?? [];\n deps.ingest(event, urls[0], { persist: false });\n for (const url of urls.slice(1)) {\n deps.markSeen(event.id, url);\n }\n };\n if (!deps.persistEvents) {\n for (const event of events) {\n ingestAll(event);\n summary.received.push(event.id);\n }\n continue;\n }\n // The awaited putMany reports persistFailures; accepted events then go\n // through the ingest path with persistence already done.\n let results: PutResult[];\n try {\n // Persisted serially per batch so failures land on the right ids.\n // oxlint-disable-next-line no-await-in-loop\n results = await deps.storage.putMany(events);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n for (const event of events) {\n summary.persistFailures[event.id] = message;\n }\n break;\n }\n for (const [j, event] of events.entries()) {\n if (results[j] === \"rejected\" || results[j] === \"invalid\") {\n continue;\n }\n ingestAll(event);\n summary.received.push(event.id);\n }\n }\n }\n\n return summary;\n}\n\n/**\n * NIP-77 sync against the given relays (or Client default relays). Independent sessions run in\n * parallel. Fulfilled summaries are merged; if every relay rejects, throws the first rejection in\n * URL order.\n */\nexport async function sync(\n deps: SyncDeps,\n filter: Filter,\n opts?: SyncOptions,\n): Promise<SyncSummary> {\n deps.assertAlive();\n const urls = deps.defaultRelays(opts?.relays ? [...opts.relays] : undefined);\n const results = await Promise.allSettled(\n urls.map(async (url) => syncToRelay(deps, url, filter, opts)),\n );\n let merged = emptySummary();\n let fulfilled = 0;\n let firstRejection: unknown;\n for (const result of results) {\n if (result.status === \"fulfilled\") {\n fulfilled += 1;\n merged = mergeSyncSummary(merged, result.value);\n } else {\n firstRejection ??= result.reason;\n }\n }\n if (urls.length > 0 && fulfilled === 0) {\n throw firstRejection;\n }\n return merged;\n}\n","import { throwIfAborted } from \"../core/abort.ts\";\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { sortedEvents } from \"../core/event.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"../core/event.ts\";\nimport { canonicalizeFilters, matchFilters } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { Gossip } from \"../gossip/index.ts\";\nimport { createLoaders, createOutboxFeed } from \"../loaders/index.ts\";\nimport type { Loaders, OutboxFeed } from \"../loaders/index.ts\";\nimport type { Recipient } from \"../nips/nip17.ts\";\nimport { isGiftWrapKind, requireNip59Crypto } from \"../nips/nip59.ts\";\nimport type { Nip59Crypto } from \"../nips/nip59.ts\";\nimport { Pool } from \"../relay/pool.ts\";\nimport type { PoolFetchResult, PoolPublishResult } from \"../relay/pool.ts\";\nimport { NoSignerError } from \"../signer/error.ts\";\nimport type { NostrSigner } from \"../signer/types.ts\";\nimport { toStorageError } from \"../storage/error.ts\";\nimport type { StorageError } from \"../storage/error.ts\";\nimport { MemoryIndex } from \"../storage/memory-index.ts\";\nimport { MemoryEventStore } from \"../storage/memory.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\nimport { ReactiveEventStore } from \"../store/reactive.ts\";\nimport { ClientBuilder } from \"./builder.ts\";\nimport {\n fetchPrivateMessages,\n giftWrapRelays,\n sendPrivateMessage,\n setDmRelays,\n subscribePrivateMessages,\n} from \"./dm.ts\";\nimport type { DmDeps } from \"./dm.ts\";\nimport { fetchGossip, subscribeGossip } from \"./gossip-io.ts\";\nimport { sync, syncToRelay } from \"./sync.ts\";\nimport type { SyncDeps } from \"./sync.ts\";\nimport { ClientError } from \"./types.ts\";\nimport type {\n ClientOptions,\n FetchEachOptions,\n FetchEventsOptions,\n FetchPrivateMessagesOptions,\n PrivateMessageSendResult,\n PublishOptions,\n ReceivedPrivateMessage,\n SendPrivateMessageOptions,\n SubscribeOptions,\n SubscribePrivateMessagesOptions,\n SyncOptions,\n SyncSummary,\n} from \"./types.ts\";\n\n/** Layer-5 facade: signer + default relays + pool + loaders + gossip + event store. */\nexport class Client {\n readonly pool: Pool;\n readonly loaders: Loaders;\n readonly gossip: Gossip;\n readonly storage: EventStore;\n readonly index: ReactiveEventStore;\n onstorageerror: ((err: StorageError) => void) | undefined;\n #signer: NostrSigner | undefined;\n #relays: string[];\n #shutdown = false;\n readonly #persistEvents: boolean;\n readonly #persistQueue: Event[] = [];\n #flushing: Promise<void> | undefined;\n\n constructor(opts: ClientOptions = {}) {\n this.#signer = opts.signer;\n this.#relays = [];\n for (const raw of opts.relays ?? []) {\n const url = normalizeURL(raw);\n if (!this.#relays.includes(url)) {\n this.#relays.push(url);\n }\n }\n this.gossip = opts.gossip ?? new Gossip();\n this.storage = opts.storage ?? new MemoryEventStore();\n this.index = opts.index ?? new ReactiveEventStore();\n this.#persistEvents = opts.persistEvents ?? true;\n this.onstorageerror = opts.onstorageerror;\n this.pool = new Pool({\n websocketImplementation: opts.websocketImplementation,\n verifyEvent: opts.verifyEvent,\n enablePing: opts.enablePing,\n pingIntervalMs: opts.pingIntervalMs,\n pingTimeoutMs: opts.pingTimeoutMs,\n connectTimeoutMs: opts.connectTimeoutMs,\n publishTimeoutMs: opts.publishTimeoutMs,\n enableReconnect: opts.enableReconnect ?? true,\n allowInsecure: opts.allowInsecure,\n trustedInsecureUrls: opts.trustedInsecureUrls,\n idleTimeoutMs: opts.idleTimeoutMs,\n maxRelays: opts.maxRelays,\n pinnedUrls: opts.pinnedUrls,\n invalidEventPolicy: opts.invalidEventPolicy,\n onRelaySuspended: opts.onRelaySuspended,\n // The sign function resolves the signer at challenge time, so\n // setSigner() takes effect on already-connected relays; challenges\n // without a signer are ignored by the relay (NoSignerError).\n automaticallyAuth:\n (opts.automaticAuth ?? true)\n ? () => async (template) => {\n const signer = this.#signer;\n if (!signer) {\n throw new NoSignerError(\"no signer configured for AUTH\");\n }\n const pk = await signer.getPublicKey();\n return signer.signEvent({ ...template, pubkey: pk });\n }\n : undefined,\n });\n this.loaders = createLoaders({\n pool: this.pool,\n relays: this.#relays,\n index: this.index,\n ingest: (event, relayUrl) => this.#ingest(event, relayUrl),\n });\n }\n\n static builder(): ClientBuilder {\n return new ClientBuilder((opts) => new Client(opts));\n }\n\n get signer(): NostrSigner | undefined {\n return this.#signer;\n }\n\n get relays(): ReadonlyArray<string> {\n return this.#relays;\n }\n\n get isShutdown(): boolean {\n return this.#shutdown;\n }\n\n setSigner(signer: NostrSigner | undefined): void {\n this.#signer = signer;\n // Re-arm relays whose AUTH challenge was ignored or rejected.\n this.pool.resetAuth();\n }\n\n addRelay(url: string): void {\n const normalized = normalizeURL(url);\n if (!this.#relays.includes(normalized)) {\n this.#relays.push(normalized);\n this.loaders.addRelay(normalized);\n }\n }\n\n removeRelay(url: string): void {\n const normalized = normalizeURL(url);\n this.#relays = this.#relays.filter((r) => r !== normalized);\n this.loaders.removeRelay(normalized);\n this.pool.close([normalized]);\n }\n\n /** Connect all configured relays (best-effort; failures are ignored). */\n async connect(opts?: { signal?: AbortSignal }): Promise<void> {\n this.#assertAlive();\n await Promise.allSettled(\n this.#relays.map(async (url) => this.pool.ensureRelay(url, { signal: opts?.signal })),\n );\n }\n\n async shutdown(): Promise<void> {\n this.#shutdown = true;\n while (this.#flushing !== undefined) {\n // oxlint-disable-next-line no-await-in-loop -- waits for the current flush to settle before closing\n await this.#flushing;\n }\n this.pool.close();\n }\n\n #assertAlive(): void {\n if (this.#shutdown) {\n throw new ClientError(\"client is shut down\");\n }\n }\n\n #defaultRelays(urls?: string[]): string[] {\n const list = urls ?? this.#relays;\n if (list.length === 0) {\n throw new ClientError(\"no relays configured\");\n }\n return list;\n }\n\n #wantObserve(flag?: boolean): boolean {\n return flag !== false;\n }\n\n /** Ingest one inbound event: index (with relay URL) → meta → persistence. */\n observe(event: Event, relayUrl?: string): void {\n this.#ingest(event, relayUrl);\n }\n\n /** Ingest many events (deduped by id, order preserved) as one persist batch. */\n observeAll(events: ReadonlyArray<Event>): void {\n const seen = new Set<string>();\n for (const event of events) {\n if (seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n this.#ingest(event);\n }\n }\n\n /**\n * The single inbound-event pipeline: reactive index add (with the source relay URL recorded in\n * seenOn), gossip routing meta, then the persistence decision. `persist: false` skips the storage\n * queue for callers that persist via their own awaited `putMany` (down-sync); `meta: false` is a\n * pure sighting/index add.\n */\n #ingest(event: Event, relayUrl?: string, opts?: { persist?: boolean; meta?: boolean }): void {\n this.index.add(event, relayUrl);\n if (opts?.meta !== false) {\n this.#ingestMeta(event);\n }\n if (opts?.persist === false || !this.#persistEvents) {\n return;\n }\n this.#persistQueue.push(event);\n this.#armFlush();\n }\n\n /** Record a relay sighting for an id already in the index. */\n #markSeen(id: string, relayUrl: string): void {\n this.index.markSeen(id, relayUrl);\n }\n\n #ingestMeta(event: Event): void {\n this.gossip.ingest(event);\n }\n\n #armFlush(): void {\n if (this.#flushing) {\n return;\n }\n this.#flushing = this.#flushLoop();\n }\n\n async #flushLoop(): Promise<void> {\n try {\n await new Promise<void>((resolve) => {\n queueMicrotask(resolve);\n });\n while (this.#persistQueue.length > 0) {\n const batch = this.#persistQueue.splice(0);\n try {\n // oxlint-disable-next-line no-await-in-loop -- queued batches are persisted in order\n await this.storage.putMany(batch);\n } catch (error) {\n invokeSafely(() => this.onstorageerror?.(toStorageError(error)));\n }\n }\n } finally {\n this.#flushing = undefined;\n if (this.#persistQueue.length > 0) {\n this.#armFlush();\n }\n }\n }\n\n /**\n * Explicitly load kind:10002 and kind:10050 lists for pubkeys and feed gossip. Does not\n * background-fetch unknown authors on every subscribe (predictable DX).\n */\n async hydrateGossip(\n pubkeys: ReadonlyArray<string>,\n opts?: { hints?: string[]; force?: boolean },\n ): Promise<void> {\n this.#assertAlive();\n await Promise.all(\n pubkeys.map(async (pk) => {\n const style = opts?.force === true ? \"force\" : \"default\";\n const [relayList, dmList] = await Promise.all([\n this.loaders.relayList(pk, { hints: opts?.hints, style }),\n this.loaders.dmRelayList(pk, { hints: opts?.hints, style }),\n ]);\n if (relayList.event) {\n this.observe(relayList.event);\n }\n if (dmList.event) {\n this.observe(dmList.event);\n }\n }),\n );\n }\n\n /**\n * Create an outbox-model feed for the given authors (NIP-65 write relays). Live events go through\n * {@link observe}; sync persists via applySync.\n */\n outbox(opts: {\n authors: ReadonlyArray<string>;\n kinds?: ReadonlyArray<number>;\n onEvent?: (event: Event) => void;\n maxRelaysPerAuthor?: number;\n }): OutboxFeed {\n this.#assertAlive();\n return createOutboxFeed({\n pool: this.pool,\n gossip: this.gossip,\n storage: this.storage,\n discoveryRelays: this.#relays,\n authors: opts.authors,\n kinds: opts.kinds,\n onEvent: opts.onEvent,\n maxRelaysPerAuthor: opts.maxRelaysPerAuthor,\n observe: (event, relayUrl) => this.observe(event, relayUrl),\n seen: (event, relayUrl) => this.#ingest(event, relayUrl, { persist: false, meta: false }),\n applySync: async (events) => {\n if (!this.#persistEvents) {\n for (const event of events) {\n this.#ingest(event, undefined, { persist: false });\n }\n return [];\n }\n const results = await this.storage.putMany(events);\n const applied: Event[] = [];\n for (const [i, event] of events.entries()) {\n const result = results[i];\n if (result === \"rejected\" || result === \"ephemeral\" || result === \"invalid\") {\n continue;\n }\n this.#ingest(event, undefined, { persist: false });\n applied.push(event);\n }\n return applied;\n },\n hydrate: async (pubkeys) => this.hydrateGossip(pubkeys),\n });\n }\n\n async getPublicKey(): Promise<string> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return this.#signer.getPublicKey();\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return this.#signer.signEvent(unsigned);\n }\n\n async signEventBuilder(builder: EventBuilder): Promise<Event> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return builder.sign(this.#signer);\n }\n\n async signTemplate(template: EventTemplate): Promise<Event> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n const pubkey = await this.#signer.getPublicKey();\n return this.#signer.signEvent({ ...template, pubkey });\n }\n\n /**\n * Sign (if builder) and publish to relays. With `gossip: true`, prefers the author's NIP-65\n * outbox relays, tagged `p` inboxes, and up to 5 `e`/`a` tag relay hints when known. On any\n * successful OK, observes the event into storage/gossip.\n */\n async publish(\n eventOrBuilder: Event | EventBuilder,\n opts?: PublishOptions,\n ): Promise<PoolPublishResult[]> {\n this.#assertAlive();\n const event =\n eventOrBuilder instanceof EventBuilder\n ? await this.signEventBuilder(eventOrBuilder)\n : eventOrBuilder;\n\n let relays = opts?.relays;\n if (!relays && isGiftWrapKind(event.kind)) {\n relays = giftWrapRelays(this.gossip, event);\n } else if (!relays && opts?.gossip === true) {\n const urls: string[] = [];\n const add = (list: ReadonlyArray<string>) => {\n for (const url of list) {\n if (!urls.includes(url)) {\n urls.push(url);\n }\n }\n };\n add(this.gossip.outboxRelays(event.pubkey));\n for (const tag of event.tags) {\n if (tag[0] === \"p\" && tag[1] !== undefined) {\n add(this.gossip.inboxRelays(tag[1]));\n }\n }\n let hintCount = 0;\n for (const tag of event.tags) {\n if (hintCount >= 5) {\n break;\n }\n if ((tag[0] !== \"e\" && tag[0] !== \"a\") || tag[2] === undefined) {\n continue;\n }\n try {\n const url = normalizeURL(tag[2]);\n if (!urls.includes(url)) {\n urls.push(url);\n hintCount += 1;\n }\n } catch {\n // not a relay URL\n }\n }\n if (urls.length > 0) {\n relays = urls;\n }\n }\n const results = await this.pool.publish(this.#defaultRelays(relays), event, {\n timeoutMs: opts?.timeoutMs,\n });\n\n const anyOk = results.some((r) => r.result?.ok === true);\n if (anyOk && this.#wantObserve(opts?.observe)) {\n this.observe(event);\n }\n return results;\n }\n\n /**\n * Fetch events. With `gossip: true`, routes filters by NIP-65 when possible. With `localFirst:\n * true`, merges storage hits with network results.\n */\n async fetchEvents(filter: Filter | Filter[], opts?: FetchEventsOptions): Promise<Event[]> {\n this.#assertAlive();\n const filters = canonicalizeFilters(Array.isArray(filter) ? filter : [filter]);\n const shouldObserve = this.#wantObserve(opts?.observe);\n const candidates: Event[] = [];\n\n if (opts?.localFirst === true) {\n // Persisted storage is merged with the index below; storage may hold a\n // stale replaceable version, so it is only a candidate — the index wins.\n try {\n const local = await this.storage.query(filters);\n for (const e of local) {\n candidates.push(e);\n if (shouldObserve) {\n this.#ingest(e, undefined, { persist: false, meta: false });\n }\n }\n } catch (error) {\n invokeSafely(() => this.onstorageerror?.(toStorageError(error)));\n }\n }\n\n // Every inbound event lands in the index (with its relay URL) before the\n // caller's onevent runs; the batch below re-ingests for persistence.\n const onevent = (event: Event, relayUrl: string) => {\n if (shouldObserve) {\n this.#ingest(event, relayUrl, { persist: false, meta: false });\n }\n opts?.onevent?.(event, relayUrl);\n };\n\n const batch =\n opts?.gossip !== true || opts.relays !== undefined\n ? await this.pool.fetch(this.#defaultRelays(opts?.relays), filters, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent,\n })\n : await fetchGossip(this.pool, this.gossip, filters, () => this.#defaultRelays(), {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent,\n });\n for (const e of batch) {\n candidates.push(e);\n if (shouldObserve) {\n this.observe(e);\n }\n }\n\n // Merge through a scratch index so NIP-01 replaceable winners, kind-5\n // deletions, dedupe and per-filter limits all apply to the result.\n // Ephemeral kinds are never stored; matching ones join the result directly.\n const tmp = new MemoryIndex();\n const ephemeral = new Map<string, Event>();\n const merged =\n opts?.localFirst === true ? [...this.index.query(filters), ...candidates] : candidates;\n for (const e of merged) {\n if (tmp.put(e) === \"ephemeral\" && matchFilters(filters, e)) {\n ephemeral.set(e.id, e);\n }\n }\n return sortedEvents([...tmp.query(filters), ...ephemeral.values()]);\n }\n\n /**\n * One-shot fetch with per-relay results: each entry reports its events and end reason (`eose`,\n * `closed`, `timeout`, or `failed` when the relay could not be reached). Events are ingested into\n * the index with each relay's URL in `seenOn` and persisted exactly like {@link fetchEvents}\n * (`observe` defaults to true). No gossip routing and no local merge.\n */\n async fetchEach(filter: Filter | Filter[], opts?: FetchEachOptions): Promise<PoolFetchResult[]> {\n this.#assertAlive();\n const filters = canonicalizeFilters(Array.isArray(filter) ? filter : [filter]);\n const results = await this.pool.fetchEach(this.#defaultRelays(opts?.relays), filters, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n });\n if (this.#wantObserve(opts?.observe)) {\n const unique: Event[] = [];\n const seen = new Set<string>();\n for (const result of results) {\n for (const event of result.events) {\n this.#ingest(event, result.url, { persist: false, meta: false });\n if (!seen.has(event.id)) {\n seen.add(event.id);\n unique.push(event);\n }\n }\n }\n for (const event of unique) {\n this.observe(event);\n }\n }\n return results;\n }\n\n /** Query local storage only (no network). */\n async queryLocal(filter: Filter | Filter[]): Promise<Event[]> {\n this.#assertAlive();\n const filters = canonicalizeFilters(Array.isArray(filter) ? filter : [filter]);\n return this.storage.query(filters);\n }\n\n subscribe(\n filter: Filter | Filter[],\n opts?: SubscribeOptions,\n ): { close: (reason?: string) => void } {\n this.#assertAlive();\n const filters = canonicalizeFilters(Array.isArray(filter) ? filter : [filter]);\n const shouldObserve = this.#wantObserve(opts?.observe);\n\n const wrapEvent = (event: Event, relayUrl: string) => {\n if (shouldObserve) {\n this.observe(event, relayUrl);\n }\n opts?.onevent?.(event, relayUrl);\n };\n\n const wrapReceived = (id: string, relayUrl: string) => {\n if (shouldObserve) {\n this.#markSeen(id, relayUrl);\n }\n opts?.receivedEvent?.(id, relayUrl);\n };\n\n if (opts?.gossip !== true || opts.relays !== undefined) {\n return this.pool.subscribe(this.#defaultRelays(opts?.relays), filters, {\n onevent: wrapEvent,\n receivedEvent: wrapReceived,\n oneose: opts?.oneose,\n onclose: opts?.onclose,\n signal: opts?.signal,\n id: opts?.id,\n eoseTimeoutMs: opts?.eoseTimeoutMs,\n });\n }\n\n return subscribeGossip(this.pool, this.gossip, filters, () => this.#defaultRelays(), {\n onevent: wrapEvent,\n receivedEvent: wrapReceived,\n oneose: opts?.oneose,\n onclose: opts?.onclose,\n signal: opts?.signal,\n eoseTimeoutMs: opts?.eoseTimeoutMs,\n });\n }\n\n #requireNip59Crypto(): Nip59Crypto {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return requireNip59Crypto(this.#signer);\n }\n\n #throwIfAborted(signal?: AbortSignal): void {\n throwIfAborted(signal);\n }\n\n #dmDeps(): DmDeps {\n return {\n pool: this.pool,\n gossip: this.gossip,\n hydrateGossip: async (pubkeys) => this.hydrateGossip(pubkeys),\n ingest: (event, relayUrl) => this.#ingest(event, relayUrl),\n markSeen: (id, relayUrl) => this.#markSeen(id, relayUrl),\n assertAlive: () => this.#assertAlive(),\n requireNip59Crypto: () => this.#requireNip59Crypto(),\n throwIfAborted: (signal) => this.#throwIfAborted(signal),\n wantObserve: (flag) => this.#wantObserve(flag),\n publish: async (eventOrBuilder, opts) => this.publish(eventOrBuilder, opts),\n };\n }\n\n #syncDeps(): SyncDeps {\n return {\n pool: this.pool,\n storage: this.storage,\n persistEvents: this.#persistEvents,\n assertAlive: () => this.#assertAlive(),\n throwIfAborted: (signal) => this.#throwIfAborted(signal),\n wantObserve: (flag) => this.#wantObserve(flag),\n ingest: (event, relayUrl, opts) => this.#ingest(event, relayUrl, opts),\n markSeen: (id, relayUrl) => this.#markSeen(id, relayUrl),\n defaultRelays: (urls) => this.#defaultRelays(urls),\n };\n }\n\n /** Publish a kind:10050 NIP-17 DM relay list (`relay` tags). Not kind 10002. */\n async setDmRelays(\n relays: ReadonlyArray<string>,\n opts?: PublishOptions,\n ): Promise<PoolPublishResult[]> {\n return setDmRelays(this.#dmDeps(), relays, opts);\n }\n\n async sendPrivateMessage(\n recipients: string | Recipient | ReadonlyArray<string | Recipient>,\n content: string,\n opts?: SendPrivateMessageOptions,\n ): Promise<PrivateMessageSendResult> {\n return sendPrivateMessage(this.#dmDeps(), recipients, content, opts);\n }\n\n async fetchPrivateMessages(\n opts?: FetchPrivateMessagesOptions,\n ): Promise<ReceivedPrivateMessage[]> {\n return fetchPrivateMessages(this.#dmDeps(), opts);\n }\n\n async subscribePrivateMessages(\n opts?: SubscribePrivateMessagesOptions,\n ): Promise<{ close: (reason?: string) => void }> {\n return subscribePrivateMessages(this.#dmDeps(), opts);\n }\n\n /**\n * NIP-77 sync against one relay: reconcile, then optionally upload local-only events and/or\n * download remote-only events. `observe: false` skips putMany and ingest; received ids are still\n * listed. `persistEvents: false` skips putMany, still ingests when observe is on.\n */\n async syncToRelay(\n url: string,\n filter: Filter,\n opts?: Omit<SyncOptions, \"relays\">,\n ): Promise<SyncSummary> {\n return syncToRelay(this.#syncDeps(), url, filter, opts);\n }\n\n /**\n * NIP-77 sync against the given relays (or Client default relays). Independent sessions run in\n * parallel. Fulfilled summaries are merged; if every relay rejects, throws the first rejection in\n * URL order.\n */\n async sync(filter: Filter, opts?: SyncOptions): Promise<SyncSummary> {\n return sync(this.#syncDeps(), filter, opts);\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-D8Nfoh4K.mjs","names":[],"sources":["../src/core/tag.ts","../src/core/event.ts"],"sourcesContent":["import { isAddressableKind, isReplaceableKind } from \"./kind.ts\";\nimport { isHex32 } from \"./util.ts\";\n\n/** A NIP-01 tag: first element is the name, rest are values. */\nexport type Tag = ReadonlyArray<string>;\n\n/** Mutable tag builder input. */\nexport type TagInput = string[];\n\n/** Type guard: a non-empty array of strings. */\nexport function isTag(value: unknown): value is Tag {\n if (!Array.isArray(value) || value.length === 0) {\n return false;\n }\n for (const item of value) {\n if (typeof item !== \"string\") {\n return false;\n }\n }\n return true;\n}\n\n/** First element of a tag (its name). */\nexport function tagName(tag: Tag): string {\n return tag[0] ?? \"\";\n}\n\n/** Second element of a tag (its primary value), or undefined for a bare name tag. */\nexport function tagValue(tag: Tag): string | undefined {\n return tag[1];\n}\n\n/** Construct common tags. */\nexport const Tag = {\n e(id: string, relay?: string, marker?: string, pubkey?: string): Tag {\n const t: string[] = [\"e\", id.toLowerCase()];\n if (relay !== undefined) {\n t.push(relay);\n }\n if (marker !== undefined) {\n t.push(marker);\n }\n if (pubkey !== undefined) {\n t.push(pubkey.toLowerCase());\n }\n return t;\n },\n p(pubkey: string, relay?: string, petname?: string): Tag {\n const t: string[] = [\"p\", pubkey.toLowerCase()];\n if (relay !== undefined) {\n t.push(relay);\n }\n if (petname !== undefined) {\n t.push(petname);\n }\n return t;\n },\n a(coordinate: string, relay?: string): Tag {\n const t: string[] = [\"a\", coordinate];\n if (relay !== undefined) {\n t.push(relay);\n }\n return t;\n },\n d(identifier: string): Tag {\n return [\"d\", identifier];\n },\n t(hashtag: string): Tag {\n return [\"t\", hashtag];\n },\n r(url: string, marker?: string): Tag {\n const t: string[] = [\"r\", url];\n if (marker !== undefined) {\n t.push(marker);\n }\n return t;\n },\n k(kind: number | string): Tag {\n return [\"k\", String(kind)];\n },\n} as const;\n\n/** First `d` tag value on an event, if any. */\nexport function getDTag(tags: ReadonlyArray<Tag>): string | undefined {\n for (const tag of tags) {\n if (tag[0] === \"d\" && tag[1] !== undefined) {\n return tag[1];\n }\n }\n return undefined;\n}\n\n/** NIP-01 `a` tag: `kind:pubkey:identifier` (identifier may be empty and may contain extra colons). */\nexport type EventAddress = {\n kind: number;\n pubkey: string;\n identifier: string;\n};\n\n/** Parse a NIP-01 addressable/replaceable coordinate. */\nexport function parseEventAddress(value: string): EventAddress | undefined {\n const first = value.indexOf(\":\");\n if (first <= 0) {\n return undefined;\n }\n const second = value.indexOf(\":\", first + 1);\n if (second === -1) {\n return undefined;\n }\n const kind = Number(value.slice(0, first));\n if (!Number.isInteger(kind) || kind < 0 || kind > 65535) {\n return undefined;\n }\n const pubkey = value.slice(first + 1, second).toLowerCase();\n if (!isHex32(pubkey)) {\n return undefined;\n }\n return { kind, pubkey, identifier: value.slice(second + 1) };\n}\n\n/** Format an address as a `kind:pubkey:dTag` coordinate string. */\nexport function formatEventAddress(kind: number, pubkey: string, identifier = \"\"): string {\n return `${kind}:${pubkey.toLowerCase()}:${identifier}`;\n}\n\n/**\n * Coordinate for replaceable (`kind:pubkey:`) and addressable (`kind:pubkey:d`) events. Undefined\n * for regular/ephemeral kinds.\n */\nexport function eventAddress(event: {\n kind: number;\n pubkey: string;\n tags: ReadonlyArray<Tag>;\n}): string | undefined {\n if (isAddressableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, getDTag(event.tags) ?? \"\");\n }\n if (isReplaceableKind(event.kind)) {\n return formatEventAddress(event.kind, event.pubkey, \"\");\n }\n return undefined;\n}\n","import { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport { EventValidationError } from \"./error.ts\";\nimport type { Tag } from \"./tag.ts\";\nimport { isTag } from \"./tag.ts\";\nimport { isHex32, isHex64, isRecord, utf8Encoder, bytesToHex } from \"./util.ts\";\n\n/** Wire event template before pubkey/id/sig. */\nexport type EventTemplate = {\n readonly kind: number;\n readonly tags: ReadonlyArray<Tag>;\n readonly content: string;\n readonly created_at: number;\n};\n\n/** Event with pubkey, ready to hash and sign. */\nexport type UnsignedEvent = EventTemplate & {\n readonly pubkey: string;\n};\n\n/**\n * Fully formed NIP-01 event (JSON-friendly plain object). Fields are readonly at the type level;\n * wire interop remains plain JSON.\n */\nexport type Event = UnsignedEvent & {\n readonly id: string;\n readonly sig: string;\n};\n\n/** Cache of events whose signatures have been verified successfully. */\nconst verifiedEvents = new WeakSet<Event>();\n\n/** Cache of events known to fail verification. */\nconst failedEvents = new WeakSet<Event>();\n\nexport function markVerified(event: Event): void {\n verifiedEvents.add(event);\n failedEvents.delete(event);\n}\n\nexport function markUnverified(event: Event): void {\n failedEvents.add(event);\n verifiedEvents.delete(event);\n}\n\nexport function isMarkedVerified(event: Event): boolean {\n return verifiedEvents.has(event);\n}\n\nexport function isMarkedFailed(event: Event): boolean {\n return failedEvents.has(event);\n}\n\n/** Structural validation of an unsigned event (no crypto). */\nexport function validateEvent(event: unknown): event is UnsignedEvent {\n if (!isRecord(event)) {\n return false;\n }\n if (\n typeof event[\"kind\"] !== \"number\" ||\n !Number.isInteger(event[\"kind\"]) ||\n event[\"kind\"] < 0 ||\n event[\"kind\"] > 65535\n ) {\n return false;\n }\n if (typeof event[\"content\"] !== \"string\") {\n return false;\n }\n if (typeof event[\"created_at\"] !== \"number\" || !Number.isInteger(event[\"created_at\"])) {\n return false;\n }\n if (typeof event[\"pubkey\"] !== \"string\" || !isHex32(event[\"pubkey\"])) {\n return false;\n }\n if (!Array.isArray(event[\"tags\"])) {\n return false;\n }\n for (const tag of event[\"tags\"]) {\n if (!isTag(tag)) {\n return false;\n }\n }\n return true;\n}\n\n/** Structural validation of a full event including id and sig hex shape. */\nexport function validateSignedEvent(event: unknown): event is Event {\n if (!validateEvent(event)) {\n return false;\n }\n const e = event as Record<string, unknown>;\n if (typeof e[\"id\"] !== \"string\" || !isHex32(e[\"id\"])) {\n return false;\n }\n if (typeof e[\"sig\"] !== \"string\" || !isHex64(e[\"sig\"])) {\n return false;\n }\n return true;\n}\n\n/**\n * Canonical NIP-01 serialization used for hashing: JSON array `[0, pubkey, created_at, kind, tags,\n * content]` with no extra whitespace.\n */\nexport function serializeEvent(event: UnsignedEvent): string {\n if (!validateEvent(event)) {\n throw new EventValidationError(\"cannot serialize event with invalid shape\");\n }\n return JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]);\n}\n\n/** SHA-256 of the canonical serialization, lowercase hex. */\nexport function getEventHash(event: UnsignedEvent): string {\n const serialized = serializeEvent(event);\n return bytesToHex(sha256(utf8Encoder.encode(serialized)));\n}\n\n/**\n * NIP-01 newest-first order: `created_at` descending, then `id` in plain code-unit order (ids are\n * canonical lowercase hex, so `<` is lexical order).\n */\nexport function compareEventsDesc(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at;\n }\n if (a.id < b.id) {\n return -1;\n }\n if (a.id > b.id) {\n return 1;\n }\n return 0;\n}\n\n/**\n * Sort events reverse-chronologically by `created_at`, then by `id` lexicographically. Mutates and\n * returns the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort(compareEventsDesc);\n}\n\n/** Non-mutating sort; returns a new array. */\nexport function sortedEvents(events: ReadonlyArray<Event>): Event[] {\n return [...events].sort(compareEventsDesc);\n}\n\n/**\n * Negentropy item order: `created_at` ascending, then `id` lexicographic (`<`/`>`). Time axis is\n * opposite {@link sortEvents}; id tie-break is the same direction.\n */\nexport function itemCompare(\n a: { id: string; created_at: number },\n b: { id: string; created_at: number },\n): number {\n if (a.created_at !== b.created_at) {\n return a.created_at - b.created_at;\n }\n if (a.id < b.id) {\n return -1;\n }\n if (a.id > b.id) {\n return 1;\n }\n return 0;\n}\n\n/**\n * NIP-01 replaceable/addressable winner: higher `created_at`, or equal timestamp and\n * lexicographically lower `id`.\n */\nexport function isReplaceableWinner(\n candidate: { created_at: number; id: string },\n incumbent: { created_at: number; id: string },\n): boolean {\n if (candidate.created_at !== incumbent.created_at) {\n return candidate.created_at > incumbent.created_at;\n }\n return candidate.id < incumbent.id;\n}\n\n/** True when a signed event is the signed form of `unsigned` (kind/tags/content/time/pubkey). */\nexport function signedMatchesUnsigned(signed: Event, unsigned: UnsignedEvent): boolean {\n if (signed.kind !== unsigned.kind) {\n return false;\n }\n if (signed.content !== unsigned.content) {\n return false;\n }\n if (signed.created_at !== unsigned.created_at) {\n return false;\n }\n if (JSON.stringify(signed.tags) !== JSON.stringify(unsigned.tags)) {\n return false;\n }\n if (unsigned.pubkey && signed.pubkey.toLowerCase() !== unsigned.pubkey.toLowerCase()) {\n return false;\n }\n return true;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"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":"instance-DQE8BoWt.mjs","names":[],"sources":["../src/wasm/abi.ts","../src/wasm/adapter.ts","../src/wasm/instance.ts"],"sourcesContent":["/** Wasm-bindgen 0.2.122 bundler-produced exports used by interned TS glue. */\nexport type CryptoWasmExports = {\n memory: WebAssembly.Memory;\n verify: (\n idPtr: number,\n idLen: number,\n pkPtr: number,\n pkLen: number,\n sigPtr: number,\n sigLen: number,\n ) => number;\n verify_serialized: (\n serPtr: number,\n serLen: number,\n idPtr: number,\n idLen: number,\n pkPtr: number,\n pkLen: number,\n sigPtr: number,\n sigLen: number,\n ) => number;\n sign: (\n retptr: number,\n idPtr: number,\n idLen: number,\n skPtr: number,\n skLen: number,\n auxPtr: number,\n auxLen: number,\n ) => void;\n public_key: (retptr: number, skPtr: number, skLen: number) => void;\n /** 0.2.122 name for `__wbindgen_malloc(size, align)`. */\n __wbindgen_export: (size: number, align: number) => number;\n /** 0.2.122 name for `__wbindgen_free(ptr, size, align)` after malloc claimed `__wbindgen_export`. */\n __wbindgen_export2: (ptr: number, size: number, align: number) => void;\n __wbindgen_add_to_stack_pointer: (delta: number) => number;\n __wbindgen_start?: () => void;\n};\n\n/** Imports 0.2.122 may request. Instantiation fails if anything else appears. */\nexport const ALLOWED_WASM_IMPORTS: ReadonlyArray<{ module: string; name: string }> = [\n { module: \"wbg\", name: \"__wbindgen_throw\" },\n { module: \"./nostr_crypto_wasm_bg.js\", name: \"__wbindgen_throw\" },\n];\n\nconst utf8 = new TextDecoder();\n\nfunction decodeUtf8(memory: WebAssembly.Memory, ptr: number, len: number): string {\n return utf8.decode(new Uint8Array(memory.buffer, ptr, len));\n}\n\nfunction makeWbgImports(holder: { exports?: CryptoWasmExports }): WebAssembly.Imports {\n const throwRuntime = (ptr: number, len: number): never => {\n const mem = holder.exports?.memory;\n const msg = mem ? decodeUtf8(mem, ptr, len) : \"wasm panic\";\n throw new WebAssembly.RuntimeError(msg);\n };\n const wbg = {\n __wbindgen_throw: throwRuntime,\n };\n return {\n wbg,\n \"./nostr_crypto_wasm_bg.js\": wbg,\n };\n}\n\n/** Fail if the module imports anything outside {@link ALLOWED_WASM_IMPORTS}. */\nexport function assertAllowedWasmImports(mod: WebAssembly.Module): void {\n const allowed = new Set(ALLOWED_WASM_IMPORTS.map((item) => `${item.module}\\0${item.name}`));\n for (const imp of WebAssembly.Module.imports(mod)) {\n if (imp.kind !== \"function\") {\n throw new TypeError(`unexpected wasm import kind ${imp.kind}: ${imp.module}.${imp.name}`);\n }\n if (!allowed.has(`${imp.module}\\0${imp.name}`)) {\n throw new TypeError(`unexpected wasm import ${imp.module}.${imp.name}`);\n }\n }\n}\n\nfunction requireMemory(value: WebAssembly.ExportValue | undefined): WebAssembly.Memory {\n if (!(value instanceof WebAssembly.Memory)) {\n throw new TypeError(\"wasm missing memory export\");\n }\n return value;\n}\n\n// oxlint-disable-next-line no-unnecessary-type-parameters -- T binds the declared ABI signature at each call site\nfunction requireFn<T extends (...args: number[]) => unknown>(\n value: WebAssembly.ExportValue | undefined,\n name: string,\n): T {\n if (typeof value !== \"function\") {\n throw new TypeError(`wasm missing ${name} export`);\n }\n // oxlint-disable-next-line no-unsafe-type-assertion -- wasm exports are opaque; each name is bound to the signature the ABI contract requires\n return value as T;\n}\n\nfunction asExports(raw: WebAssembly.Exports): CryptoWasmExports {\n const exports: CryptoWasmExports = {\n memory: requireMemory(raw[\"memory\"]),\n verify: requireFn(raw[\"verify\"], \"verify\"),\n verify_serialized: requireFn(raw[\"verify_serialized\"], \"verify_serialized\"),\n sign: requireFn(raw[\"sign\"], \"sign\"),\n public_key: requireFn(raw[\"public_key\"], \"public_key\"),\n __wbindgen_export: requireFn(raw[\"__wbindgen_export\"], \"__wbindgen_export\"),\n __wbindgen_export2: requireFn(raw[\"__wbindgen_export2\"], \"__wbindgen_export2\"),\n __wbindgen_add_to_stack_pointer: requireFn(\n raw[\"__wbindgen_add_to_stack_pointer\"],\n \"__wbindgen_add_to_stack_pointer\",\n ),\n };\n const start = raw[\"__wbindgen_start\"];\n if (typeof start === \"function\") {\n exports.__wbindgen_start = requireFn<() => void>(start, \"__wbindgen_start\");\n }\n return exports;\n}\n\nfunction passBytes(exports: CryptoWasmExports, bytes: Uint8Array): { ptr: number; len: number } {\n const len = bytes.length;\n const ptr = exports.__wbindgen_export(len, 1) >>> 0;\n new Uint8Array(exports.memory.buffer, ptr, len).set(bytes);\n return { ptr, len };\n}\n\nexport function wasmVerify(\n exports: CryptoWasmExports,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n const idP = passBytes(exports, id);\n const pkP = passBytes(exports, pubkey);\n const sigP = passBytes(exports, sig);\n return exports.verify(idP.ptr, idP.len, pkP.ptr, pkP.len, sigP.ptr, sigP.len) !== 0;\n}\n\nexport function wasmVerifySerialized(\n exports: CryptoWasmExports,\n serialized: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n): boolean {\n const serP = passBytes(exports, serialized);\n const idP = passBytes(exports, id);\n const pkP = passBytes(exports, pubkey);\n const sigP = passBytes(exports, sig);\n return (\n exports.verify_serialized(\n serP.ptr,\n serP.len,\n idP.ptr,\n idP.len,\n pkP.ptr,\n pkP.len,\n sigP.ptr,\n sigP.len,\n ) !== 0\n );\n}\n\nfunction takeBytes(exports: CryptoWasmExports, ptr: number, len: number): Uint8Array {\n const out = new Uint8Array(len);\n if (len > 0) {\n out.set(new Uint8Array(exports.memory.buffer, ptr, len));\n exports.__wbindgen_export2(ptr, len, 1);\n }\n return out;\n}\n\nfunction callReturningBytes(\n exports: CryptoWasmExports,\n invoke: (retptr: number) => void,\n): Uint8Array {\n const retptr = exports.__wbindgen_add_to_stack_pointer(-16);\n try {\n invoke(retptr);\n const view = new DataView(exports.memory.buffer);\n const ptr = view.getUint32(retptr, true);\n const len = view.getUint32(retptr + 4, true);\n return takeBytes(exports, ptr, len);\n } finally {\n exports.__wbindgen_add_to_stack_pointer(16);\n }\n}\n\nexport function wasmSign(\n exports: CryptoWasmExports,\n id: Uint8Array,\n seckey: Uint8Array,\n aux: Uint8Array,\n): Uint8Array {\n const idP = passBytes(exports, id);\n const skP = passBytes(exports, seckey);\n const auxP = passBytes(exports, aux);\n return callReturningBytes(exports, (retptr) => {\n exports.sign(retptr, idP.ptr, idP.len, skP.ptr, skP.len, auxP.ptr, auxP.len);\n });\n}\n\nexport function wasmPublicKey(exports: CryptoWasmExports, seckey: Uint8Array): Uint8Array {\n const skP = passBytes(exports, seckey);\n return callReturningBytes(exports, (retptr) => {\n exports.public_key(retptr, skP.ptr, skP.len);\n });\n}\n\nexport async function instantiateCryptoWasm(\n bytes: ArrayBuffer | ArrayBufferView,\n): Promise<CryptoWasmExports> {\n const holder: { exports?: CryptoWasmExports } = {};\n const { module, instance } = await WebAssembly.instantiate(bytes, makeWbgImports(holder));\n assertAllowedWasmImports(module);\n holder.exports = asExports(instance.exports);\n holder.exports.__wbindgen_start?.();\n return holder.exports;\n}\n","import { WasmVerifyPoisonedError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { createEventVerifier } from \"../core/verifier.ts\";\nimport type { SerializedEventVerifier } from \"../core/verifier.ts\";\n\nexport { WasmVerifyPoisonedError } from \"../core/error.ts\";\n\n/**\n * Build the WASM event verifier on {@link createEventVerifier}. A `WebAssembly.RuntimeError` from\n * the module means the instance aborted — it poisons every later call with the same\n * `WasmVerifyPoisonedError`; any other backend error verifies `false` (the event is marked\n * failed).\n */\nexport function createWasmEventVerifier(\n verifySerialized: SerializedEventVerifier,\n poison: { error?: Error },\n): (event: Event) => boolean {\n const backend: SerializedEventVerifier = (serializedUtf8, id, pubkey, sig) => {\n if (poison.error !== undefined) {\n throw poison.error;\n }\n try {\n return verifySerialized(serializedUtf8, id, pubkey, sig);\n } catch (error) {\n if (error instanceof WebAssembly.RuntimeError) {\n poison.error = new WasmVerifyPoisonedError(\"wasm verify aborted the instance\", {\n cause: error,\n });\n throw poison.error;\n }\n return false;\n }\n };\n return createEventVerifier(backend);\n}\n","import { CryptoError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { SigningBackend } from \"../core/key.ts\";\nimport {\n instantiateCryptoWasm,\n wasmPublicKey,\n wasmSign,\n wasmVerify,\n wasmVerifySerialized,\n} from \"./abi.ts\";\nimport type { CryptoWasmExports } from \"./abi.ts\";\nimport { createWasmEventVerifier, WasmVerifyPoisonedError } from \"./adapter.ts\";\n\nexport type LoadNostrWasmOptions = {\n /** Bytes, or a URL whose bytes will be read by the platform loader. */\n module?: ArrayBuffer | ArrayBufferView | URL;\n};\n\nexport type NostrWasm = SigningBackend & {\n verify: (id: Uint8Array, pubkey: Uint8Array, sig: Uint8Array) => boolean;\n verifySerialized: (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n ) => boolean;\n verifyEvent: (event: Event) => boolean;\n};\n\nexport function isWasmBytes(value: unknown): value is ArrayBuffer | ArrayBufferView {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport async function fetchWasmUrl(url: URL): Promise<Uint8Array> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new CryptoError(`failed to fetch wasm: ${res.status} ${url.href}`);\n }\n return new Uint8Array(await res.arrayBuffer());\n}\n\nfunction requireByteLength(bytes: Uint8Array, expected: number, label: string): void {\n if (bytes.length !== expected) {\n throw new CryptoError(`invalid ${label} length: expected ${expected}, got ${bytes.length}`);\n }\n}\n\nfunction wrapPoison<T>(poison: { error?: Error }, fn: () => T): T {\n if (poison.error) {\n throw poison.error;\n }\n try {\n return fn();\n } catch (error) {\n if (error instanceof WebAssembly.RuntimeError) {\n poison.error = new WasmVerifyPoisonedError(\"wasm verify aborted the instance\", {\n cause: error,\n });\n throw poison.error;\n }\n throw error;\n }\n}\n\nfunction bindExports(exports: CryptoWasmExports): NostrWasm {\n const poison: { error?: Error } = {};\n const rawSerialized = (\n serializedUtf8: Uint8Array,\n id: Uint8Array,\n pubkey: Uint8Array,\n sig: Uint8Array,\n ): boolean => wasmVerifySerialized(exports, serializedUtf8, id, pubkey, sig);\n return {\n verify: (id, pubkey, sig) => wrapPoison(poison, () => wasmVerify(exports, id, pubkey, sig)),\n verifySerialized: (serializedUtf8, id, pubkey, sig) =>\n wrapPoison(poison, () => rawSerialized(serializedUtf8, id, pubkey, sig)),\n verifyEvent: createWasmEventVerifier(rawSerialized, poison),\n sign: (id, seckey, aux) =>\n wrapPoison(poison, () => {\n requireByteLength(id, 32, \"id\");\n requireByteLength(seckey, 32, \"secret key\");\n requireByteLength(aux, 32, \"aux\");\n const sig = wasmSign(exports, id, seckey, aux);\n if (sig.length !== 64) {\n throw new CryptoError(\"wasm sign failed\");\n }\n return sig;\n }),\n publicKey: (seckey) =>\n wrapPoison(poison, () => {\n requireByteLength(seckey, 32, \"secret key\");\n const pk = wasmPublicKey(exports, seckey);\n if (pk.length !== 32) {\n throw new CryptoError(\"wasm publicKey failed\");\n }\n return pk;\n }),\n };\n}\n\n/** Builds a loader that interns a single instance per module slot. */\nexport function createNostrWasmLoader(\n wasmBytes: (opts?: LoadNostrWasmOptions) => Promise<ArrayBuffer | ArrayBufferView>,\n): {\n loadNostrWasm: (opts?: LoadNostrWasmOptions) => Promise<NostrWasm>;\n resetNostrWasm: () => void;\n} {\n let interned: Promise<NostrWasm> | undefined;\n\n async function instantiate(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n const bytes = await wasmBytes(opts);\n const exports = await instantiateCryptoWasm(bytes);\n return bindExports(exports);\n }\n\n /** Instantiate once. Repeats reuse the same module. Failure throws; no noble fallback. */\n async function loadNostrWasm(opts?: LoadNostrWasmOptions): Promise<NostrWasm> {\n if (interned) {\n return interned;\n }\n const pending = instantiate(opts);\n interned = pending;\n try {\n return await pending;\n } catch (error) {\n if (interned === pending) {\n interned = undefined;\n }\n throw error;\n }\n }\n\n function resetNostrWasm(): void {\n interned = undefined;\n }\n\n return { loadNostrWasm, resetNostrWasm };\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":";;;;;;AASA,IAAa,YAAb,MAAa,UAAU;CACrB;CAEA,YAAoB,OAAmB;EACrC,qBAAqB,KAAK;EAC1B,KAAK,SAAS,IAAI,WAAW,KAAK;CACpC;CAEA,OAAO,WAAsB;EAC3B,OAAO,IAAI,UAAU,QAAQ,MAAM,gBAAgB,CAAC;CACtD;CAEA,OAAO,UAAU,OAA8B;EAC7C,OAAO,IAAI,UAAU,KAAK;CAC5B;CAEA,OAAO,QAAQ,KAAwB;EACrC,OAAO,IAAI,UAAUA,aAAW,YAAY,KAAK,YAAY,CAAC,CAAC;CACjE;CAEA,IAAI,QAAoB;EACtB,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,YAAY,8BAA8B;EAEtD,OAAO,IAAI,WAAW,KAAK,MAAM;CACnC;CAEA,QAAgB;EACd,OAAOC,aAAW,KAAK,KAAK;CAC9B;;CAGA,UAAgB;EACd,IAAI,KAAK,QAAQ;GACf,KAAK,OAAO,KAAK,CAAC;GAClB,KAAK,SAAS,KAAA;EAChB;CACF;AACF;;AAMA,SAAgB,iBAAiB,KAAwB;CACvD,OAAO,YAAY,KAAK,YAAY;AACtC;AAYA,MAAM,eAA+B;CACnC,YAAY,cAAc,QAAQ,aAAa,SAAS;CACxD,OAAO,IAAI,WAAW,YAAY,QAAQ,KAAK,IAAI,WAAW,OAAO;AACvE;;AAGA,SAAgB,aAAa,WAAuD;CAClF,MAAM,QACJ,qBAAqB,YACjB,UAAU,QACV,OAAO,cAAc,WACnBD,aAAW,YAAY,WAAW,YAAY,CAAC,KAC9C,qBAAqB,SAAS,GAAG;CAC1C,OAAOC,aAAW,QAAQ,aAAa,KAAK,CAAC;AAC/C;;AAGA,IAAa,OAAb,MAAa,KAAK;CAChB;CACA;;;;;;CAMA;CAEA,YAAoB,WAAsB,SAAyB;EACjE,KAAK,YAAY;EACjB,KAAK,UAAU;EACf,MAAM,SAAS,QAAQ,UAAU,UAAU,KAAK;EAChD,IAAI,OAAO,WAAW,IACpB,MAAM,IAAI,YAAY,gDAAgD;EAExE,KAAK,YAAYA,aAAW,MAAM;CACpC;CAEA,OAAO,SAAS,UAA0B,cAAoB;EAC5D,OAAO,IAAI,KAAK,UAAU,SAAS,GAAG,OAAO;CAC/C;CAEA,OAAO,cACL,WACA,UAA0B,cACpB;EACN,MAAM,KACJ,qBAAqB,YACjB,YACA,OAAO,cAAc,WACnB,UAAU,QAAQ,SAAS,IAC3B,UAAU,UAAU,SAAS;EACrC,OAAO,IAAI,KAAK,IAAI,OAAO;CAC7B;AACF;AAEA,SAAS,YAAY,WAAyD;CAC5E,OAAO,qBAAqB,OAAO,YAAY,KAAK,cAAc,SAAS;AAC7E;;AAGA,SAAgB,cACd,UACA,WACO;CACP,MAAM,OAAO,YAAY,SAAS;CAQlC,OAAO,UAAU;EANf,MAAM,SAAS;EACf,MAAM,SAAS;EACf,SAAS,SAAS;EAClB,YAAY,SAAS;EACrB,QAAQ,KAAK;CAES,GAAG,IAAI;AACjC;;;;;AAMA,SAAgB,UACd,UACA,WACO;CACP,MAAM,OAAO,YAAY,SAAS;CAClC,IAAI,CAAC,cAAc,QAAQ,GACzB,MAAM,IAAI,YAAY,oCAAoC;CAG5D,IAAI,SAAS,WAAW,KAAK,WAC3B,MAAM,IAAI,YAAY,iDAAiD;CAGzE,MAAM,aAA4B;EAChC,MAAM,SAAS;EACf,MAAM,SAAS;EACf,SAAS,SAAS;EAClB,YAAY,SAAS;EACrB,QAAQ,KAAK;CACf;CAEA,MAAM,KAAK,aAAa,UAAU;CAClC,MAAM,MAAM,KAAK,QAAQ,KAAKD,aAAW,EAAE,GAAG,KAAK,UAAU,OAAO,YAAY,EAAE,CAAC;CACnF,IAAI,IAAI,WAAW,IACjB,MAAM,IAAI,YAAY,+CAA+C;CAEvE,MAAM,QAAe;EAAE,GAAG;EAAY;EAAI,KAAKC,aAAW,GAAG;CAAE;CAC/D,aAAa,KAAK;CAClB,OAAO;AACT"}
@@ -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"}