@qntx/nostr 0.7.0 → 0.9.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 (171) hide show
  1. package/README.md +1 -1
  2. package/dist/{builder-CdLE4J6O.mjs → builder-CVRmUeNm.mjs} +5 -5
  3. package/dist/builder-CVRmUeNm.mjs.map +1 -0
  4. package/dist/{builder-DgkqDylK.d.mts → builder-GBddxj8i.d.mts} +12 -12
  5. package/dist/{client-CILhdfsF.mjs → client-CeyDLoyO.mjs} +110 -90
  6. package/dist/client-CeyDLoyO.mjs.map +1 -0
  7. package/dist/client.d.mts +1 -1
  8. package/dist/client.mjs +1 -1
  9. package/dist/core.d.mts +8 -8
  10. package/dist/core.mjs +9 -9
  11. package/dist/emitter-DkCY6NkU.mjs +32 -0
  12. package/dist/emitter-DkCY6NkU.mjs.map +1 -0
  13. package/dist/{error-L_QdmSnV.mjs → error-BILMe3Gr.mjs} +2 -2
  14. package/dist/{error-L_QdmSnV.mjs.map → error-BILMe3Gr.mjs.map} +1 -1
  15. package/dist/{error-gtJvV6YS.mjs → error-DlhGW4mJ.mjs} +2 -2
  16. package/dist/{error-gtJvV6YS.mjs.map → error-DlhGW4mJ.mjs.map} +1 -1
  17. package/dist/{event-DinQMmJ1.mjs → event-C2eLiPpV.mjs} +16 -6
  18. package/dist/event-C2eLiPpV.mjs.map +1 -0
  19. package/dist/{event-DJeb_DIL.d.mts → event-j3BwI5dF.d.mts} +2 -4
  20. package/dist/{filter-jI_9htF7.d.mts → filter-CfEgQBXt.d.mts} +3 -5
  21. package/dist/{filter-BQl3GlHt.mjs → filter-TrF2LEoy.mjs} +3 -36
  22. package/dist/filter-TrF2LEoy.mjs.map +1 -0
  23. package/dist/{gossip-CcTp9VpU.mjs → gossip-5ezg0Quo.mjs} +4 -4
  24. package/dist/gossip-5ezg0Quo.mjs.map +1 -0
  25. package/dist/{gossip-C7HLW5kI.d.mts → gossip-BWb9HmfK.d.mts} +10 -7
  26. package/dist/gossip.d.mts +1 -1
  27. package/dist/gossip.mjs +1 -1
  28. package/dist/http-CuazNRIP.mjs.map +1 -1
  29. package/dist/{http-C4HwIu4B.d.mts → http-kwWG-V1X.d.mts} +9 -3
  30. package/dist/{index-Tx15HGAD.d.mts → index-C0mFpelb.d.mts} +9 -9
  31. package/dist/{index-DocgD9K0.d.mts → index-C1m5cHpd.d.mts} +42 -28
  32. package/dist/{index-InQZpHyQ.d.mts → index-CoX7SnO7.d.mts} +7 -4
  33. package/dist/{index-iLT8UzY3.d.mts → index-D0M3kjY2.d.mts} +23 -23
  34. package/dist/{index-DU9tRFTZ.d.mts → index-Hp26bgYG.d.mts} +11 -4
  35. package/dist/{index-DiXwNn2M.d.mts → index-JD1O7m2v.d.mts} +4 -4
  36. package/dist/index.d.mts +20 -20
  37. package/dist/index.mjs +20 -20
  38. package/dist/{instance-BcKzxHIP.mjs → instance-CI_IaZpf.mjs} +3 -3
  39. package/dist/{instance-BcKzxHIP.mjs.map → instance-CI_IaZpf.mjs.map} +1 -1
  40. package/dist/{instance-DjsL8h84.d.mts → instance-X_WkCnpR.d.mts} +3 -3
  41. package/dist/{key-D_5OgGT7.d.mts → key-CyKN1RTi.d.mts} +2 -2
  42. package/dist/{key-bshqfmZL.mjs → key-DY_HEIpY.mjs} +5 -4
  43. package/dist/key-DY_HEIpY.mjs.map +1 -0
  44. package/dist/{kind-Cxwhhkx8.mjs → kind-D8fENiiL.mjs} +11 -5
  45. package/dist/kind-D8fENiiL.mjs.map +1 -0
  46. package/dist/{loaders-DD0XAGzw.mjs → loaders-DifimjJ7.mjs} +11 -10
  47. package/dist/loaders-DifimjJ7.mjs.map +1 -0
  48. package/dist/loaders.d.mts +2 -2
  49. package/dist/loaders.mjs +1 -1
  50. package/dist/{memory-D_-5-T31.mjs → memory-DENGQ_f6.mjs} +2 -2
  51. package/dist/memory-DENGQ_f6.mjs.map +1 -0
  52. package/dist/{memory-index-C6rujtoc.mjs → memory-index-D_lvlYAy.mjs} +4 -4
  53. package/dist/{memory-index-C6rujtoc.mjs.map → memory-index-D_lvlYAy.mjs.map} +1 -1
  54. package/dist/{message-CbBZOhq3.d.mts → message-CcUz22qh.d.mts} +6 -6
  55. package/dist/{message-CWLcFTQ1.mjs → message-Rd5Z_17h.mjs} +6 -5
  56. package/dist/message-Rd5Z_17h.mjs.map +1 -0
  57. package/dist/{nip17-cTDFfFVt.d.mts → nip17-B7MI82Iq.d.mts} +5 -5
  58. package/dist/{nip19-D1OnJ192.d.mts → nip19-DpPN17bY.d.mts} +23 -23
  59. package/dist/{nip46-DrsPI8EX.d.mts → nip46-BHaxC9TZ.d.mts} +17 -17
  60. package/dist/{nip51-DoPUvQ--.d.mts → nip51-O_vhOiK2.d.mts} +11 -11
  61. package/dist/{nip59-BFIihLYY.d.mts → nip59-CdDoJptA.d.mts} +10 -3
  62. package/dist/{nip65-52HiwiyD.d.mts → nip65-BL79HI8N.d.mts} +3 -3
  63. package/dist/{nip77-C9QcZn7b.d.mts → nip77-wHA_nEV5.d.mts} +7 -7
  64. package/dist/nips/blossom.d.mts +8 -8
  65. package/dist/nips/blossom.mjs +3 -3
  66. package/dist/nips/blossom.mjs.map +1 -1
  67. package/dist/nips/nip04.mjs +1 -1
  68. package/dist/nips/nip05.d.mts +9 -9
  69. package/dist/nips/nip05.mjs +1 -1
  70. package/dist/nips/nip05.mjs.map +1 -1
  71. package/dist/nips/nip10.d.mts +8 -8
  72. package/dist/nips/nip10.mjs +35 -33
  73. package/dist/nips/nip10.mjs.map +1 -1
  74. package/dist/nips/nip11.d.mts +28 -28
  75. package/dist/nips/nip11.mjs +1 -1
  76. package/dist/nips/nip11.mjs.map +1 -1
  77. package/dist/nips/nip13.d.mts +1 -1
  78. package/dist/nips/nip13.mjs +1 -1
  79. package/dist/nips/nip17.d.mts +1 -1
  80. package/dist/nips/nip17.mjs +7 -5
  81. package/dist/nips/nip17.mjs.map +1 -1
  82. package/dist/nips/nip19.d.mts +1 -1
  83. package/dist/nips/nip19.mjs +1 -1
  84. package/dist/nips/nip19.mjs.map +1 -1
  85. package/dist/nips/nip21.d.mts +4 -4
  86. package/dist/nips/nip21.mjs +1 -1
  87. package/dist/nips/nip21.mjs.map +1 -1
  88. package/dist/nips/nip27.d.mts +20 -20
  89. package/dist/nips/nip27.mjs +2 -2
  90. package/dist/nips/nip27.mjs.map +1 -1
  91. package/dist/nips/nip42.d.mts +1 -1
  92. package/dist/nips/nip42.mjs +2 -2
  93. package/dist/nips/nip44.mjs +2 -2
  94. package/dist/nips/nip44.mjs.map +1 -1
  95. package/dist/nips/nip46.d.mts +1 -1
  96. package/dist/nips/nip46.mjs +1 -1
  97. package/dist/nips/nip46.mjs.map +1 -1
  98. package/dist/nips/nip49.d.mts +4 -4
  99. package/dist/nips/nip49.mjs +1 -1
  100. package/dist/nips/nip49.mjs.map +1 -1
  101. package/dist/nips/nip51.d.mts +1 -1
  102. package/dist/nips/nip51.mjs +4 -4
  103. package/dist/nips/nip51.mjs.map +1 -1
  104. package/dist/nips/nip57.d.mts +19 -19
  105. package/dist/nips/nip57.mjs +4 -4
  106. package/dist/nips/nip57.mjs.map +1 -1
  107. package/dist/nips/nip59.d.mts +1 -1
  108. package/dist/nips/nip59.mjs +16 -8
  109. package/dist/nips/nip59.mjs.map +1 -1
  110. package/dist/nips/nip65.d.mts +1 -1
  111. package/dist/nips/nip65.mjs +3 -3
  112. package/dist/nips/nip77.d.mts +1 -1
  113. package/dist/nips/nip77.mjs +9 -6
  114. package/dist/nips/nip77.mjs.map +1 -1
  115. package/dist/nips/nip98.d.mts +1 -1
  116. package/dist/nips/nip98.mjs +4 -4
  117. package/dist/{pool-SzJKPIa3.d.mts → pool-BDI_u03Q.d.mts} +48 -28
  118. package/dist/{pool-YHRZgcQ3.mjs → pool-jok2-iBJ.mjs} +64 -47
  119. package/dist/pool-jok2-iBJ.mjs.map +1 -0
  120. package/dist/{put-DS0yzVJx.mjs → put-JsiCKMyZ.mjs} +5 -5
  121. package/dist/put-JsiCKMyZ.mjs.map +1 -0
  122. package/dist/{reactive-BJRoVHxU.mjs → reactive-6Wn9qf_d.mjs} +51 -29
  123. package/dist/reactive-6Wn9qf_d.mjs.map +1 -0
  124. package/dist/{reactive-BJ1ggn4d.d.mts → reactive-X4p6nABy.d.mts} +18 -9
  125. package/dist/{relay-CvxPiQ_m.mjs → relay-__QtO33h.mjs} +1059 -863
  126. package/dist/relay-__QtO33h.mjs.map +1 -0
  127. package/dist/relay.d.mts +4 -4
  128. package/dist/relay.mjs +2 -2
  129. package/dist/{signer-yW3slDC2.mjs → signer-D7PtVvQn.mjs} +16 -9
  130. package/dist/signer-D7PtVvQn.mjs.map +1 -0
  131. package/dist/signer.d.mts +2 -2
  132. package/dist/signer.mjs +2 -2
  133. package/dist/{storage-CZ52ifcQ.mjs → storage-oihjE0_x.mjs} +23 -21
  134. package/dist/storage-oihjE0_x.mjs.map +1 -0
  135. package/dist/storage.d.mts +2 -2
  136. package/dist/storage.mjs +4 -4
  137. package/dist/store.d.mts +2 -2
  138. package/dist/store.mjs +1 -1
  139. package/dist/testing.d.mts +4 -4
  140. package/dist/testing.mjs +8 -8
  141. package/dist/testing.mjs.map +1 -1
  142. package/dist/{types-DNmiscfd.d.mts → types-BJ4wZ7JJ.d.mts} +2 -2
  143. package/dist/{types-Cm-XMgtH.d.mts → types-Ccox7-5v.d.mts} +7 -7
  144. package/dist/{util-yFYzlbrm.mjs → util-DMF9AIO0.mjs} +6 -2
  145. package/dist/util-DMF9AIO0.mjs.map +1 -0
  146. package/dist/{verifier-CgTFpgsC.d.mts → verifier-BjL2M0ka.d.mts} +2 -2
  147. package/dist/{verifier-BTLOpo1n.mjs → verifier-uC_jd-Nw.mjs} +4 -4
  148. package/dist/verifier-uC_jd-Nw.mjs.map +1 -0
  149. package/dist/wasm.browser.d.mts +1 -1
  150. package/dist/wasm.browser.mjs +2 -2
  151. package/dist/wasm.d.mts +1 -1
  152. package/dist/wasm.mjs +2 -2
  153. package/package.json +5 -5
  154. package/dist/builder-CdLE4J6O.mjs.map +0 -1
  155. package/dist/client-CILhdfsF.mjs.map +0 -1
  156. package/dist/event-DinQMmJ1.mjs.map +0 -1
  157. package/dist/filter-BQl3GlHt.mjs.map +0 -1
  158. package/dist/gossip-CcTp9VpU.mjs.map +0 -1
  159. package/dist/key-bshqfmZL.mjs.map +0 -1
  160. package/dist/kind-Cxwhhkx8.mjs.map +0 -1
  161. package/dist/loaders-DD0XAGzw.mjs.map +0 -1
  162. package/dist/memory-D_-5-T31.mjs.map +0 -1
  163. package/dist/message-CWLcFTQ1.mjs.map +0 -1
  164. package/dist/pool-YHRZgcQ3.mjs.map +0 -1
  165. package/dist/put-DS0yzVJx.mjs.map +0 -1
  166. package/dist/reactive-BJRoVHxU.mjs.map +0 -1
  167. package/dist/relay-CvxPiQ_m.mjs.map +0 -1
  168. package/dist/signer-yW3slDC2.mjs.map +0 -1
  169. package/dist/storage-CZ52ifcQ.mjs.map +0 -1
  170. package/dist/util-yFYzlbrm.mjs.map +0 -1
  171. package/dist/verifier-BTLOpo1n.mjs.map +0 -1
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  Layered TypeScript Nostr library: events, keys, filters, signers, relays, storage, gossip, and a `Client` facade in one ESM package.
6
6
 
7
- See [docs/](docs/).
7
+ Changes are tracked in [CHANGELOG.md](CHANGELOG.md); layering rules and invariants for contributors live in [AGENTS.md](AGENTS.md).
8
8
 
9
9
  ## License
10
10
 
@@ -1,7 +1,7 @@
1
- import { d as nowSeconds, h as EventValidationError, u as normalizeURL } from "./util-yFYzlbrm.mjs";
2
- import { o as isReplaceableKind, r as isAddressableKind, t as Kind } from "./kind-Cxwhhkx8.mjs";
3
- import { h as Tag, v as formatEventAddress, x as parseEventAddress, y as getDTag } from "./event-DinQMmJ1.mjs";
4
- import { r as finalizeEvent } from "./key-bshqfmZL.mjs";
1
+ import { d as nowSeconds, h as EventValidationError, u as normalizeURL } from "./util-DMF9AIO0.mjs";
2
+ import { o as isReplaceableKind, r as isAddressableKind, t as Kind } from "./kind-D8fENiiL.mjs";
3
+ import { S as parseEventAddress, b as getDTag, g as Tag, y as formatEventAddress } from "./event-C2eLiPpV.mjs";
4
+ import { r as finalizeEvent } from "./key-DY_HEIpY.mjs";
5
5
  //#region src/core/builder.ts
6
6
  /** NIP-18 e third entry MUST be a relay URL; empty string is not one. */
7
7
  function requireRelayUrl(relayHint) {
@@ -164,4 +164,4 @@ var EventBuilder = class EventBuilder {
164
164
  //#endregion
165
165
  export { EventBuilder as t };
166
166
 
167
- //# sourceMappingURL=builder-CdLE4J6O.mjs.map
167
+ //# sourceMappingURL=builder-CVRmUeNm.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder-CVRmUeNm.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, SecretKeyInput } from \"./key.ts\";\nimport { Kind, isAddressableKind, isReplaceableKind } from \"./kind.ts\";\nimport { Tag, formatEventAddress, getDTag, parseEventAddress } from \"./tag.ts\";\nimport { normalizeURL, nowSeconds } from \"./util.ts\";\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 readonly name?: string | undefined;\n readonly display_name?: string | undefined;\n readonly about?: string | undefined;\n readonly picture?: string | undefined;\n readonly banner?: string | undefined;\n readonly website?: string | undefined;\n readonly nip05?: string | undefined;\n readonly lud06?: string | undefined;\n readonly lud16?: string | undefined;\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 }\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: ReadonlyArray<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 = target.tags.some((tag) => tag[0] === \"-\") ? \"\" : 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 protectedTag = target.tags.some((tag) => tag[0] === \"-\");\n const content = protectedTag || 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 ?? nowSeconds(),\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 | SecretKeyInput): 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"],"mappings":"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"}
@@ -1,17 +1,17 @@
1
- import { _ as Tag, n as EventTemplate, r as UnsignedEvent, t as Event } from "./event-DJeb_DIL.mjs";
2
- import { i as SecretKeyInput, t as Keys } from "./key-D_5OgGT7.mjs";
1
+ import { h as Tag, n as EventTemplate, r as UnsignedEvent, t as Event } from "./event-j3BwI5dF.mjs";
2
+ import { i as SecretKeyInput, t as Keys } from "./key-CyKN1RTi.mjs";
3
3
  //#region src/core/builder.d.ts
4
4
  /** Profile metadata JSON (kind 0 content). NIP-05 is not verified here. */
5
5
  type ProfileMetadata = {
6
- name?: string;
7
- display_name?: string;
8
- about?: string;
9
- picture?: string;
10
- banner?: string;
11
- website?: string;
12
- nip05?: string;
13
- lud06?: string;
14
- lud16?: string;
6
+ readonly name?: string | undefined;
7
+ readonly display_name?: string | undefined;
8
+ readonly about?: string | undefined;
9
+ readonly picture?: string | undefined;
10
+ readonly banner?: string | undefined;
11
+ readonly website?: string | undefined;
12
+ readonly nip05?: string | undefined;
13
+ readonly lud06?: string | undefined;
14
+ readonly lud16?: string | undefined;
15
15
  };
16
16
  /**
17
17
  * Fluent builder for event templates. Decouples intent (kind/content/tags) from signing (Keys /
@@ -69,4 +69,4 @@ declare class EventBuilder {
69
69
  }
70
70
  //#endregion
71
71
  export { ProfileMetadata as n, EventBuilder as t };
72
- //# sourceMappingURL=builder-DgkqDylK.d.mts.map
72
+ //# sourceMappingURL=builder-GBddxj8i.d.mts.map
@@ -1,21 +1,22 @@
1
- import { u as normalizeURL, v as NostrError, x as errorMessage } from "./util-yFYzlbrm.mjs";
2
- import { t as Kind } from "./kind-Cxwhhkx8.mjs";
3
- import { f as sortedEvents, o as itemCompare } from "./event-DinQMmJ1.mjs";
4
- import { t as EventBuilder } from "./builder-CdLE4J6O.mjs";
5
- import { n as canonicalizeFilters, o as matchFilters, t as canonicalizeFilter } from "./filter-BQl3GlHt.mjs";
6
- import { t as NoSignerError } from "./error-gtJvV6YS.mjs";
7
- import { t as invokeSafely } from "./report-CHld4uWj.mjs";
8
- import { n as fanIn, r as fetchRouted, t as Pool } from "./pool-YHRZgcQ3.mjs";
1
+ import { S as toError, h as EventValidationError, u as normalizeURL, v as NostrError, x as errorMessage } from "./util-DMF9AIO0.mjs";
2
+ import { t as Kind } from "./kind-D8fENiiL.mjs";
3
+ import { o as itemCompare, p as sortedEvents } from "./event-C2eLiPpV.mjs";
4
+ import { n as verifyEvent } from "./verifier-uC_jd-Nw.mjs";
5
+ import { t as EventBuilder } from "./builder-CVRmUeNm.mjs";
6
+ import { n as canonicalizeFilters, o as matchFilters, t as canonicalizeFilter } from "./filter-TrF2LEoy.mjs";
7
+ import { t as NoSignerError } from "./error-DlhGW4mJ.mjs";
9
8
  import { i as throwIfAborted, n as onAbort } from "./abort-DBq2STPj.mjs";
9
+ import { n as fanIn, r as fetchRouted, t as Pool } from "./pool-jok2-iBJ.mjs";
10
+ import { t as Emitter } from "./emitter-DkCY6NkU.mjs";
10
11
  import { storageFromItems } from "./nips/nip77.mjs";
11
12
  import { isGiftWrapKind, requireNip59Crypto, unwrap } from "./nips/nip59.mjs";
12
13
  import { Nip17Error, buildChatMessageRumor, dmRelayListEventBuilder, normalizeRecipients, requireDmRelays, wrapDirectMessage } from "./nips/nip17.mjs";
13
- import { t as Gossip } from "./gossip-CcTp9VpU.mjs";
14
- import { r as OutboxFeed, t as createLoaders } from "./loaders-DD0XAGzw.mjs";
15
- import { n as toStorageError } from "./error-L_QdmSnV.mjs";
16
- import { t as MemoryIndex } from "./memory-index-C6rujtoc.mjs";
17
- import { t as MemoryEventStore } from "./memory-D_-5-T31.mjs";
18
- import { t as ReactiveEventStore } from "./reactive-BJRoVHxU.mjs";
14
+ import { t as Gossip } from "./gossip-5ezg0Quo.mjs";
15
+ import { r as OutboxFeed, t as createLoaders } from "./loaders-DifimjJ7.mjs";
16
+ import { n as toStorageError } from "./error-BILMe3Gr.mjs";
17
+ import { t as MemoryIndex } from "./memory-index-D_lvlYAy.mjs";
18
+ import { t as MemoryEventStore } from "./memory-DENGQ_f6.mjs";
19
+ import { t as ReactiveEventStore } from "./reactive-6Wn9qf_d.mjs";
19
20
  import { t as SerialQueue } from "./serial-Bzpw-xz3.mjs";
20
21
  //#region src/client/dm.ts
21
22
  function giftWrapRelays(gossip, event) {
@@ -48,7 +49,10 @@ async function sendPrivateMessage(deps, recipients, content, opts) {
48
49
  subject: opts?.subject,
49
50
  replyTo: opts?.replyTo
50
51
  });
51
- const wraps = await wrapDirectMessage(crypto, list, rumor);
52
+ const wraps = await wrapDirectMessage(crypto, list, rumor, {
53
+ expiration: opts?.expiration,
54
+ ephemeral: opts?.ephemeral
55
+ });
52
56
  return {
53
57
  rumor,
54
58
  wraps: await Promise.all(wraps.map(async ({ recipient, wrap }) => {
@@ -282,31 +286,35 @@ async function syncToRelay(deps, url, filter, opts) {
282
286
  timeoutMs: opts?.timeoutMs,
283
287
  signal: opts?.signal
284
288
  });
289
+ const sent = [];
290
+ const received = [];
291
+ const sendFailures = {};
292
+ const persistFailures = {};
285
293
  const summary = {
286
294
  local: have,
287
295
  remote: need,
288
- sent: [],
289
- received: [],
290
- sendFailures: {},
291
- persistFailures: {}
296
+ sent,
297
+ received,
298
+ sendFailures,
299
+ persistFailures
292
300
  };
293
301
  if (opts?.dryRun === true) return summary;
294
302
  if ((direction === SyncDirection.Up || direction === SyncDirection.Both) && have.length > 0) {
295
303
  const found = await deps.storage.query([{ ids: have }]);
296
304
  const foundById = new Map(found.map((event) => [event.id, event]));
297
- for (const id of have) if (!foundById.has(id)) summary.sendFailures[id] = "event not found in local store";
305
+ for (const id of have) if (!foundById.has(id)) sendFailures[id] = "event not found in local store";
298
306
  for (let i = 0; i < found.length; i += SYNC_UPLOAD_CONCURRENCY) {
299
307
  const chunk = found.slice(i, i + SYNC_UPLOAD_CONCURRENCY);
300
308
  await Promise.all(chunk.map(async (event) => {
301
309
  try {
302
310
  const results = await deps.pool.publish([url], event, { timeoutMs: opts?.timeoutMs });
303
- if (results.some((r) => r.status === "ok")) summary.sent.push(event.id);
311
+ if (results.some((r) => r.status === "ok")) sent.push(event.id);
304
312
  else {
305
313
  const [first] = results;
306
- summary.sendFailures[event.id] = first === void 0 ? "publish failed" : first.status === "failed" ? first.error : first.message;
314
+ sendFailures[event.id] = first === void 0 ? "publish failed" : first.status === "failed" ? errorMessage(first.error) : first.message;
307
315
  }
308
316
  } catch (error) {
309
- summary.sendFailures[event.id] = errorMessage(error);
317
+ sendFailures[event.id] = errorMessage(error);
310
318
  }
311
319
  }));
312
320
  }
@@ -327,7 +335,7 @@ async function syncToRelay(deps, url, filter, opts) {
327
335
  } : void 0
328
336
  });
329
337
  if (!shouldObserve) {
330
- for (const event of events) summary.received.push(event.id);
338
+ for (const event of events) received.push(event.id);
331
339
  continue;
332
340
  }
333
341
  const ingestAll = (event) => {
@@ -338,7 +346,7 @@ async function syncToRelay(deps, url, filter, opts) {
338
346
  if (!deps.persistEvents) {
339
347
  for (const event of events) {
340
348
  ingestAll(event);
341
- summary.received.push(event.id);
349
+ received.push(event.id);
342
350
  }
343
351
  continue;
344
352
  }
@@ -346,14 +354,14 @@ async function syncToRelay(deps, url, filter, opts) {
346
354
  try {
347
355
  results = await deps.storage.putMany(events);
348
356
  } catch (error) {
349
- const message = errorMessage(error);
350
- for (const event of events) summary.persistFailures[event.id] = message;
357
+ const thrown = toError(error);
358
+ for (const event of events) persistFailures[event.id] = thrown;
351
359
  break;
352
360
  }
353
361
  for (const [j, event] of events.entries()) {
354
362
  if (results[j] === "rejected" || results[j] === "invalid") continue;
355
363
  ingestAll(event);
356
- summary.received.push(event.id);
364
+ received.push(event.id);
357
365
  }
358
366
  }
359
367
  }
@@ -399,7 +407,9 @@ var Client = class {
399
407
  #persistEvents;
400
408
  #persistQueue = [];
401
409
  #flushing;
402
- #listeners = { storageerror: /* @__PURE__ */ new Set() };
410
+ #events = new Emitter();
411
+ #dmDeps;
412
+ #syncDeps;
403
413
  constructor(opts = {}) {
404
414
  const { signer, relays, automaticAuth, gossip, storage, index, persistEvents, ...poolOptions } = opts;
405
415
  this.#signer = signer;
@@ -431,6 +441,29 @@ var Client = class {
431
441
  index: this.index,
432
442
  ingest: (event, relayUrl) => this.#ingest(event, relayUrl)
433
443
  });
444
+ this.#dmDeps = {
445
+ pool: this.pool,
446
+ gossip: this.gossip,
447
+ hydrateGossip: async (pubkeys) => this.hydrateGossip(pubkeys),
448
+ ingest: (event, relayUrl) => this.#ingest(event, relayUrl),
449
+ markSeen: (id, relayUrl) => {
450
+ this.index.markSeen(id, relayUrl);
451
+ },
452
+ assertAlive: () => this.#assertAlive(),
453
+ requireNip59Crypto: () => this.#requireNip59Crypto(),
454
+ publish: async (eventOrBuilder, opts) => this.publish(eventOrBuilder, opts)
455
+ };
456
+ this.#syncDeps = {
457
+ pool: this.pool,
458
+ storage: this.storage,
459
+ persistEvents: this.#persistEvents,
460
+ assertAlive: () => this.#assertAlive(),
461
+ ingest: (event, relayUrl, opts) => this.#ingest(event, relayUrl, opts),
462
+ markSeen: (id, relayUrl) => {
463
+ this.index.markSeen(id, relayUrl);
464
+ },
465
+ defaultRelays: (urls) => this.#defaultRelays(urls)
466
+ };
434
467
  }
435
468
  get signer() {
436
469
  return this.#signer;
@@ -456,7 +489,7 @@ var Client = class {
456
489
  const normalized = normalizeURL(url);
457
490
  this.#relays = this.#relays.filter((r) => r !== normalized);
458
491
  this.loaders.removeRelay(normalized);
459
- this.pool.close([normalized]);
492
+ this.pool.closeRelays([normalized]);
460
493
  }
461
494
  /** Connect all configured relays (best-effort; failures are ignored). */
462
495
  async connect(opts) {
@@ -474,14 +507,7 @@ var Client = class {
474
507
  * function.
475
508
  */
476
509
  on(type, listener) {
477
- const set = this.#listeners[type];
478
- set.add(listener);
479
- return () => {
480
- set.delete(listener);
481
- };
482
- }
483
- #emit(type, err) {
484
- for (const listener of [...this.#listeners[type]]) invokeSafely(() => listener(err));
510
+ return this.#events.on(type, listener);
485
511
  }
486
512
  #assertAlive() {
487
513
  if (this.#shutdown) throw new ClientError("client is shut down");
@@ -491,12 +517,22 @@ var Client = class {
491
517
  if (list.length === 0) throw new ClientError("no relays configured");
492
518
  return list;
493
519
  }
494
- /** Ingest one inbound event: index (with relay URL) → meta → persistence. */
520
+ /**
521
+ * Ingest one inbound event: index (with relay URL) → meta → persistence. The event must carry a
522
+ * valid signature; {@link EventValidationError} is thrown otherwise. Events delivered by relay
523
+ * subscriptions are already verified, so this is a WeakSet cache hit on those paths.
524
+ */
495
525
  observe(event, relayUrl) {
526
+ if (!verifyEvent(event)) throw new EventValidationError("observe requires a verified event");
496
527
  this.#ingest(event, relayUrl);
497
528
  }
498
- /** Ingest many events (deduped by id, order preserved) as one persist batch. */
529
+ /**
530
+ * Ingest many events (deduped by id, order preserved) as one persist batch. Every event must
531
+ * carry a valid signature — all are verified before anything is ingested, so a batch is
532
+ * all-or-nothing.
533
+ */
499
534
  observeAll(events) {
535
+ for (const event of events) if (!verifyEvent(event)) throw new EventValidationError("observe requires a verified event");
500
536
  const seen = /* @__PURE__ */ new Set();
501
537
  for (const event of events) {
502
538
  if (seen.has(event.id)) continue;
@@ -513,7 +549,15 @@ var Client = class {
513
549
  #ingest(event, relayUrl, opts) {
514
550
  this.index.add(event, relayUrl);
515
551
  if (opts?.meta !== false) this.gossip.ingest(event);
516
- if (opts?.persist === false || !this.#persistEvents) return;
552
+ if (opts?.persist !== false) this.#queuePersist(event);
553
+ }
554
+ /** Gossip + persistence for an event the index already holds — no redundant `index.add`. */
555
+ #ingestMeta(event) {
556
+ this.gossip.ingest(event);
557
+ this.#queuePersist(event);
558
+ }
559
+ #queuePersist(event) {
560
+ if (!this.#persistEvents) return;
517
561
  this.#persistQueue.push(event);
518
562
  this.#armFlush();
519
563
  }
@@ -531,7 +575,7 @@ var Client = class {
531
575
  try {
532
576
  await this.storage.putMany(batch);
533
577
  } catch (error) {
534
- this.#emit("storageerror", toStorageError(error));
578
+ this.#events.emit("storageerror", toStorageError(error));
535
579
  }
536
580
  }
537
581
  } finally {
@@ -672,13 +716,17 @@ var Client = class {
672
716
  });
673
717
  }
674
718
  } catch (error) {
675
- this.#emit("storageerror", toStorageError(error));
719
+ this.#events.emit("storageerror", toStorageError(error));
676
720
  }
721
+ const seenIds = /* @__PURE__ */ new Set();
677
722
  const onevent = (event, relayUrl) => {
678
- if (shouldObserve) this.#ingest(event, relayUrl, {
679
- persist: false,
680
- meta: false
681
- });
723
+ if (shouldObserve) {
724
+ if (seenIds.has(event.id)) this.index.markSeen(event.id, relayUrl);
725
+ else {
726
+ seenIds.add(event.id);
727
+ this.index.add(event, relayUrl);
728
+ }
729
+ }
682
730
  opts?.onevent?.(event, relayUrl);
683
731
  };
684
732
  const batch = opts?.gossip !== true || opts.relays !== void 0 ? await this.pool.fetch(this.#defaultRelays(opts?.relays), filters, {
@@ -692,7 +740,7 @@ var Client = class {
692
740
  });
693
741
  for (const e of batch) {
694
742
  candidates.push(e);
695
- if (shouldObserve) this.observe(e);
743
+ if (shouldObserve) this.#ingestMeta(e);
696
744
  }
697
745
  const tmp = new MemoryIndex();
698
746
  const ephemeral = /* @__PURE__ */ new Map();
@@ -717,16 +765,15 @@ var Client = class {
717
765
  const unique = [];
718
766
  const seen = /* @__PURE__ */ new Set();
719
767
  for (const result of results) for (const event of result.events) {
720
- this.#ingest(event, result.url, {
721
- persist: false,
722
- meta: false
723
- });
724
- if (!seen.has(event.id)) {
725
- seen.add(event.id);
726
- unique.push(event);
768
+ if (seen.has(event.id)) {
769
+ this.index.markSeen(event.id, result.url);
770
+ continue;
727
771
  }
772
+ seen.add(event.id);
773
+ this.index.add(event, result.url);
774
+ unique.push(event);
728
775
  }
729
- for (const event of unique) this.observe(event);
776
+ for (const event of unique) this.#ingestMeta(event);
730
777
  }
731
778
  return results;
732
779
  }
@@ -769,45 +816,18 @@ var Client = class {
769
816
  #requireNip59Crypto() {
770
817
  return requireNip59Crypto(this.#requireSigner());
771
818
  }
772
- #dmDeps() {
773
- return {
774
- pool: this.pool,
775
- gossip: this.gossip,
776
- hydrateGossip: async (pubkeys) => this.hydrateGossip(pubkeys),
777
- ingest: (event, relayUrl) => this.#ingest(event, relayUrl),
778
- markSeen: (id, relayUrl) => {
779
- this.index.markSeen(id, relayUrl);
780
- },
781
- assertAlive: () => this.#assertAlive(),
782
- requireNip59Crypto: () => this.#requireNip59Crypto(),
783
- publish: async (eventOrBuilder, opts) => this.publish(eventOrBuilder, opts)
784
- };
785
- }
786
- #syncDeps() {
787
- return {
788
- pool: this.pool,
789
- storage: this.storage,
790
- persistEvents: this.#persistEvents,
791
- assertAlive: () => this.#assertAlive(),
792
- ingest: (event, relayUrl, opts) => this.#ingest(event, relayUrl, opts),
793
- markSeen: (id, relayUrl) => {
794
- this.index.markSeen(id, relayUrl);
795
- },
796
- defaultRelays: (urls) => this.#defaultRelays(urls)
797
- };
798
- }
799
819
  /** Publish a kind:10050 NIP-17 DM relay list (`relay` tags). Not kind 10002. */
800
820
  async setDmRelays(relays, opts) {
801
- return setDmRelays(this.#dmDeps(), relays, opts);
821
+ return setDmRelays(this.#dmDeps, relays, opts);
802
822
  }
803
823
  async sendPrivateMessage(recipients, content, opts) {
804
- return sendPrivateMessage(this.#dmDeps(), recipients, content, opts);
824
+ return sendPrivateMessage(this.#dmDeps, recipients, content, opts);
805
825
  }
806
826
  async fetchPrivateMessages(opts) {
807
- return fetchPrivateMessages(this.#dmDeps(), opts);
827
+ return fetchPrivateMessages(this.#dmDeps, opts);
808
828
  }
809
829
  async subscribePrivateMessages(opts) {
810
- return subscribePrivateMessages(this.#dmDeps(), opts);
830
+ return subscribePrivateMessages(this.#dmDeps, opts);
811
831
  }
812
832
  /**
813
833
  * NIP-77 sync against one relay: reconcile, then optionally upload local-only events and/or
@@ -815,7 +835,7 @@ var Client = class {
815
835
  * listed. `persistEvents: false` skips putMany, still ingests when observe is on.
816
836
  */
817
837
  async syncToRelay(url, filter, opts) {
818
- return syncToRelay(this.#syncDeps(), url, filter, opts);
838
+ return syncToRelay(this.#syncDeps, url, filter, opts);
819
839
  }
820
840
  /**
821
841
  * NIP-77 sync against the given relays (or Client default relays). Independent sessions run in
@@ -823,10 +843,10 @@ var Client = class {
823
843
  * URL order.
824
844
  */
825
845
  async sync(filter, opts) {
826
- return sync(this.#syncDeps(), filter, opts);
846
+ return sync(this.#syncDeps, filter, opts);
827
847
  }
828
848
  };
829
849
  //#endregion
830
850
  export { ClientError as n, SyncDirection as r, Client as t };
831
851
 
832
- //# sourceMappingURL=client-CILhdfsF.mjs.map
852
+ //# sourceMappingURL=client-CeyDLoyO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-CeyDLoyO.mjs","names":[],"sources":["../src/client/dm.ts","../src/client/types.ts","../src/client/gossip-io.ts","../src/client/sync.ts","../src/client/client.ts"],"sourcesContent":["import { onAbort, throwIfAborted } from \"../core/abort.ts\";\nimport 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 { SerialQueue } from \"../core/serial.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 { Closer } from \"../relay/subscription.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 publish: (\n eventOrBuilder: Event | EventBuilder,\n opts?: PublishOptions,\n ) => Promise<PoolPublishResult[]>;\n};\n\nexport function giftWrapRelays(gossip: Gossip, event: Event): ReadonlyArray<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 expiration: opts?.expiration,\n ephemeral: opts?.ephemeral,\n });\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.status === \"ok\") && opts?.observe !== false) {\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 throwIfAborted(opts?.signal);\n await deps.hydrateGossip([self]);\n 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 (opts?.observe !== false) {\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<Closer> {\n deps.assertAlive();\n const crypto = deps.requireNip59Crypto();\n const self = await crypto.getPublicKey();\n throwIfAborted(opts?.signal);\n await deps.hydrateGossip([self]);\n 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 const queue = new SerialQueue();\n let closed = false;\n let disposeAbort: (() => void) | undefined;\n const markClosed = (): void => {\n closed = true;\n disposeAbort?.();\n };\n if (opts?.signal?.aborted === true) {\n markClosed();\n } else {\n disposeAbort = onAbort(opts?.signal, markClosed);\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 void queue.run(async () => {\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 (opts?.observe !== false) {\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 }\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 { PoolOptions, PoolPublishResult } from \"../relay/pool.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> | undefined;\n direction?: SyncDirectionName | undefined;\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 | undefined;\n signal?: AbortSignal | undefined;\n dryRun?: boolean | undefined;\n /** When false, skip observe/storage on downloaded events. Default true. */\n observe?: boolean | undefined;\n};\n\n/** Outcome of a {@link Client.sync} run: ids compared, sent, received, and failures. */\nexport type SyncSummary = {\n readonly local: ReadonlyArray<string>;\n readonly remote: ReadonlyArray<string>;\n readonly sent: ReadonlyArray<string>;\n readonly received: ReadonlyArray<string>;\n /** Publish failures by event id: relay messages or thrown-error text. */\n readonly sendFailures: Readonly<Record<string, string>>;\n /** PutMany throws by event id. */\n readonly persistFailures: Readonly<Record<string, Error>>;\n};\n\n/**\n * Options for the {@link Client} constructor. Pool fields are forwarded to {@link Pool}; the\n * client-only fields are documented here.\n */\nexport type ClientOptions = Omit<PoolOptions, \"automaticallyAuth\"> & {\n signer?: NostrSigner | undefined;\n relays?: ReadonlyArray<string> | undefined;\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 | undefined;\n gossip?: Gossip | undefined;\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 | undefined;\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 | undefined;\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 | undefined;\n};\n\n/** Typed event payloads for {@link Client.on}. */\nexport type ClientEventMap = {\n /**\n * Storage I/O failures: live `putMany` flush and `fetchEvents({ localFirst: true })` query. Those\n * paths do not throw.\n */\n storageerror: StorageError;\n};\n\nexport type FetchEventsOptions = {\n relays?: ReadonlyArray<string> | undefined;\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n gossip?: boolean | undefined;\n /** When true, query local storage first and merge with network results. Defaults to false. */\n localFirst?: boolean | undefined;\n /** When false, skip writing fetched events to storage/observe. Default true. */\n observe?: boolean | undefined;\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) | undefined;\n};\n\n/** Options for {@link Client.fetchEach}: one-shot per-relay fetch, no gossip routing. */\nexport type FetchEachOptions = {\n relays?: ReadonlyArray<string> | undefined;\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n /** When false, skip writing fetched events to storage/observe. Default true. */\n observe?: boolean | undefined;\n};\n\nexport type SubscribeOptions = {\n relays?: ReadonlyArray<string> | undefined;\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) | undefined;\n /** Every receipt from every relay, including duplicates skipped by dedupe. */\n receivedEvent?: ((id: string, relayUrl: string) => void) | undefined;\n oneose?: (() => void) | undefined;\n onclose?: ((reason: string) => void) | undefined;\n signal?: AbortSignal | undefined;\n id?: string | undefined;\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 | undefined;\n /** Fan out REQs via NIP-65 gossip routes when available. */\n gossip?: boolean | undefined;\n /** When false, skip writing received events to storage/observe. Default true. */\n observe?: boolean | undefined;\n};\n\nexport type PublishOptions = {\n relays?: ReadonlyArray<string> | undefined;\n timeoutMs?: number | undefined;\n gossip?: boolean | undefined;\n /** When false, skip writing the published event to storage/observe. Default true. */\n observe?: boolean | undefined;\n};\n\n/** Options for sending a NIP-17 private message. */\nexport type SendPrivateMessageOptions = {\n readonly subject?: string | undefined;\n readonly replyTo?: ReplyTo | undefined;\n readonly created_at?: number | undefined;\n readonly timeoutMs?: number | undefined;\n readonly observe?: boolean | undefined;\n /** NIP-17 disappearing-message expiry (unix seconds) on the seal and the gift wrap. */\n readonly expiration?: number | undefined;\n /** Wrap in the ephemeral kind 21059 (relays MUST NOT store). */\n readonly ephemeral?: boolean | undefined;\n};\n\n/** Result of sending a NIP-17 DM: the rumor plus per-recipient wrap publish results. */\nexport type PrivateMessageSendResult = {\n readonly rumor: Rumor;\n readonly wraps: ReadonlyArray<{\n readonly recipient: string;\n readonly wrap: Event;\n readonly results: ReadonlyArray<PoolPublishResult>;\n }>;\n};\n\n/** An unwrapped NIP-17 private message: the received gift wrap and its inner rumor. */\nexport type ReceivedPrivateMessage = {\n readonly wrap: Event;\n readonly rumor: Rumor;\n /** Normalized URL of the relay that delivered the wrap, when known. */\n readonly relayUrl?: string | undefined;\n};\n\n/** Options for fetching NIP-17 private-message history. */\nexport type FetchPrivateMessagesOptions = {\n readonly since?: number | undefined;\n readonly until?: number | undefined;\n readonly timeoutMs?: number | undefined;\n readonly signal?: AbortSignal | undefined;\n readonly observe?: boolean | undefined;\n};\n\n/** Options for a live NIP-17 private-message subscription. */\nexport type SubscribePrivateMessagesOptions = {\n readonly since?: number | undefined;\n readonly onevent?: ((msg: ReceivedPrivateMessage) => void) | undefined;\n readonly oneose?: (() => void) | undefined;\n readonly onclose?: ((reason: string) => void) | undefined;\n readonly signal?: AbortSignal | undefined;\n readonly eoseTimeoutMs?: number | undefined;\n readonly observe?: boolean | undefined;\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 type { Closer } from \"../relay/subscription.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: ReadonlyArray<Filter>,\n defaultRelays: () => ReadonlyArray<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: ReadonlyArray<Filter>,\n defaultRelays: () => ReadonlyArray<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: ReadonlyArray<Filter>,\n defaultRelays: () => ReadonlyArray<string>,\n opts: FanInOptions,\n): Closer {\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 { throwIfAborted } from \"../core/abort.ts\";\nimport { errorMessage, toError } from \"../core/error.ts\";\nimport 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 /**\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?: ReadonlyArray<string>) => ReadonlyArray<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 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 sent: string[] = [];\n const received: string[] = [];\n const sendFailures: Record<string, string> = {};\n const persistFailures: Record<string, Error> = {};\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 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.status === \"ok\");\n if (ok) {\n sent.push(event.id);\n } else {\n const [first] = results;\n sendFailures[event.id] =\n first === undefined\n ? \"publish failed\"\n : first.status === \"failed\"\n ? errorMessage(first.error)\n : first.message;\n }\n } catch (error) {\n sendFailures[event.id] = errorMessage(error);\n }\n }),\n );\n }\n }\n\n if ((direction === SyncDirection.Down || direction === SyncDirection.Both) && need.length > 0) {\n const shouldObserve = opts?.observe !== false;\n for (let i = 0; i < need.length; i += SYNC_ID_BATCH) {\n const batch = need.slice(i, i + SYNC_ID_BATCH);\n 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 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 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 thrown = toError(error);\n for (const event of events) {\n persistFailures[event.id] = thrown;\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 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);\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 { EventBuilder } from \"../core/builder.ts\";\nimport { Emitter } from \"../core/emitter.ts\";\nimport { EventValidationError } from \"../core/error.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 { normalizeURL } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\nimport { Gossip } from \"../gossip/index.ts\";\nimport { createLoaders, OutboxFeed } from \"../loaders/index.ts\";\nimport type { Loaders } 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 type { Closer } from \"../relay/subscription.ts\";\nimport { NoSignerError } from \"../signer/error.ts\";\nimport type { NostrSigner } from \"../signer/types.ts\";\nimport { toStorageError } 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 {\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 ClientEventMap,\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\nfunction isFilterList(filter: Filter | ReadonlyArray<Filter>): filter is ReadonlyArray<Filter> {\n return Array.isArray(filter);\n}\n\nfunction toFilterList(filter: Filter | ReadonlyArray<Filter>): Filter[] {\n return canonicalizeFilters(isFilterList(filter) ? filter : [filter]);\n}\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 #signer: NostrSigner | undefined;\n #relays: string[];\n #shutdown = false;\n readonly #persistEvents: boolean;\n readonly #persistQueue: Event[] = [];\n #flushing: Promise<void> | undefined;\n readonly #events = new Emitter<ClientEventMap>();\n readonly #dmDeps: DmDeps;\n readonly #syncDeps: SyncDeps;\n\n constructor(opts: ClientOptions = {}) {\n const { signer, relays, automaticAuth, gossip, storage, index, persistEvents, ...poolOptions } =\n opts;\n this.#signer = signer;\n this.#relays = [];\n for (const raw of relays ?? []) {\n const url = normalizeURL(raw);\n if (!this.#relays.includes(url)) {\n this.#relays.push(url);\n }\n }\n this.gossip = gossip ?? new Gossip();\n this.storage = storage ?? new MemoryEventStore();\n this.index = index ?? new ReactiveEventStore();\n this.#persistEvents = persistEvents ?? true;\n this.pool = new Pool({\n ...poolOptions,\n enableReconnect: poolOptions.enableReconnect ?? true,\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 (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 this.#dmDeps = {\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) => {\n this.index.markSeen(id, relayUrl);\n },\n assertAlive: () => this.#assertAlive(),\n requireNip59Crypto: () => this.#requireNip59Crypto(),\n publish: async (eventOrBuilder, opts) => this.publish(eventOrBuilder, opts),\n };\n this.#syncDeps = {\n pool: this.pool,\n storage: this.storage,\n persistEvents: this.#persistEvents,\n assertAlive: () => this.#assertAlive(),\n ingest: (event, relayUrl, opts) => this.#ingest(event, relayUrl, opts),\n markSeen: (id, relayUrl) => {\n this.index.markSeen(id, relayUrl);\n },\n defaultRelays: (urls) => this.#defaultRelays(urls),\n };\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.closeRelays([normalized]);\n }\n\n /** Connect all configured relays (best-effort; failures are ignored). */\n async connect(opts?: { signal?: AbortSignal | undefined }): 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 /**\n * Listen for a client event. Multiple listeners per type are allowed and fire in registration\n * order; a throwing listener is reported and does not break the dispatch. Returns an unsubscribe\n * function.\n */\n on<K extends keyof ClientEventMap>(\n type: K,\n listener: (err: ClientEventMap[K]) => void,\n ): () => void {\n return this.#events.on(type, listener);\n }\n\n #assertAlive(): void {\n if (this.#shutdown) {\n throw new ClientError(\"client is shut down\");\n }\n }\n\n #defaultRelays(urls?: ReadonlyArray<string>): ReadonlyArray<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 /**\n * Ingest one inbound event: index (with relay URL) → meta → persistence. The event must carry a\n * valid signature; {@link EventValidationError} is thrown otherwise. Events delivered by relay\n * subscriptions are already verified, so this is a WeakSet cache hit on those paths.\n */\n observe(event: Event, relayUrl?: string): void {\n if (!verifyEvent(event)) {\n throw new EventValidationError(\"observe requires a verified event\");\n }\n this.#ingest(event, relayUrl);\n }\n\n /**\n * Ingest many events (deduped by id, order preserved) as one persist batch. Every event must\n * carry a valid signature — all are verified before anything is ingested, so a batch is\n * all-or-nothing.\n */\n observeAll(events: ReadonlyArray<Event>): void {\n for (const event of events) {\n if (!verifyEvent(event)) {\n throw new EventValidationError(\"observe requires a verified event\");\n }\n }\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.gossip.ingest(event);\n }\n if (opts?.persist !== false) {\n this.#queuePersist(event);\n }\n }\n\n /** Gossip + persistence for an event the index already holds — no redundant `index.add`. */\n #ingestMeta(event: Event): void {\n this.gossip.ingest(event);\n this.#queuePersist(event);\n }\n\n #queuePersist(event: Event): void {\n if (!this.#persistEvents) {\n return;\n }\n this.#persistQueue.push(event);\n this.#armFlush();\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 this.#events.emit(\"storageerror\", 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 new OutboxFeed({\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 #requireSigner(): NostrSigner {\n const signer = this.#signer;\n if (!signer) {\n throw new ClientError(\"no signer configured\");\n }\n return signer;\n }\n\n async getPublicKey(): Promise<string> {\n return this.#requireSigner().getPublicKey();\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n return this.#requireSigner().signEvent(unsigned);\n }\n\n async signEventBuilder(builder: EventBuilder): Promise<Event> {\n return builder.sign(this.#requireSigner());\n }\n\n async signTemplate(template: EventTemplate): Promise<Event> {\n const signer = this.#requireSigner();\n const pubkey = await signer.getPublicKey();\n return 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.status === \"ok\");\n if (anyOk && opts?.observe !== false) {\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(\n filter: Filter | ReadonlyArray<Filter>,\n opts?: FetchEventsOptions,\n ): Promise<Event[]> {\n this.#assertAlive();\n const filters = toFilterList(filter);\n const shouldObserve = opts?.observe !== false;\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 this.#events.emit(\"storageerror\", toStorageError(error));\n }\n }\n\n // Every inbound event lands in the index (with its relay URL) before the caller's\n // onevent runs. `onevent` fires once per relay delivery, so only the first sighting of\n // an id does a full index put — later relays are recorded via markSeen. The batch loop\n // below adds gossip meta and persistence without re-putting.\n const seenIds = new Set<string>();\n const onevent = (event: Event, relayUrl: string) => {\n if (shouldObserve) {\n if (seenIds.has(event.id)) {\n this.index.markSeen(event.id, relayUrl);\n } else {\n seenIds.add(event.id);\n this.index.add(event, relayUrl);\n }\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.#ingestMeta(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(\n filter: Filter | ReadonlyArray<Filter>,\n opts?: FetchEachOptions,\n ): Promise<PoolFetchResult[]> {\n this.#assertAlive();\n const filters = toFilterList(filter);\n const results = await this.pool.fetchEach(this.#defaultRelays(opts?.relays), filters, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n });\n if (opts?.observe !== false) {\n const unique: Event[] = [];\n const seen = new Set<string>();\n for (const result of results) {\n for (const event of result.events) {\n if (seen.has(event.id)) {\n this.index.markSeen(event.id, result.url);\n continue;\n }\n seen.add(event.id);\n this.index.add(event, result.url);\n unique.push(event);\n }\n }\n for (const event of unique) {\n this.#ingestMeta(event);\n }\n }\n return results;\n }\n\n /** Query local storage only (no network). */\n async queryLocal(filter: Filter | ReadonlyArray<Filter>): Promise<Event[]> {\n this.#assertAlive();\n const filters = toFilterList(filter);\n return this.storage.query(filters);\n }\n\n subscribe(filter: Filter | ReadonlyArray<Filter>, opts?: SubscribeOptions): Closer {\n this.#assertAlive();\n const filters = toFilterList(filter);\n const shouldObserve = opts?.observe !== false;\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.index.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 return requireNip59Crypto(this.#requireSigner());\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(opts?: SubscribePrivateMessagesOptions): Promise<Closer> {\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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"}
package/dist/client.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as SyncSummary, a as FetchEachOptions, c as PrivateMessageSendResult, d as SendPrivateMessageOptions, f as SubscribeOptions, g as SyncOptions, h as SyncDirectionName, i as ClientOptions, l as PublishOptions, m as SyncDirection, n as ClientError, o as FetchEventsOptions, p as SubscribePrivateMessagesOptions, r as ClientEventMap, s as FetchPrivateMessagesOptions, t as Client, u as ReceivedPrivateMessage } from "./index-DocgD9K0.mjs";
1
+ import { _ as SyncSummary, a as FetchEachOptions, c as PrivateMessageSendResult, d as SendPrivateMessageOptions, f as SubscribeOptions, g as SyncOptions, h as SyncDirectionName, i as ClientOptions, l as PublishOptions, m as SyncDirection, n as ClientError, o as FetchEventsOptions, p as SubscribePrivateMessagesOptions, r as ClientEventMap, s as FetchPrivateMessagesOptions, t as Client, u as ReceivedPrivateMessage } from "./index-C1m5cHpd.mjs";
2
2
  export { Client, ClientError, type ClientEventMap, type ClientOptions, type FetchEachOptions, type FetchEventsOptions, type FetchPrivateMessagesOptions, type PrivateMessageSendResult, type PublishOptions, type ReceivedPrivateMessage, type SendPrivateMessageOptions, type SubscribeOptions, type SubscribePrivateMessagesOptions, SyncDirection, type SyncDirectionName, type SyncOptions, type SyncSummary };
package/dist/client.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as ClientError, r as SyncDirection, t as Client } from "./client-CILhdfsF.mjs";
1
+ import { n as ClientError, r as SyncDirection, t as Client } from "./client-CeyDLoyO.mjs";
2
2
  export { Client, ClientError, SyncDirection };