@qntx/nostr 0.1.0 → 0.2.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 (185) hide show
  1. package/dist/abort-Ba8tDrOI.mjs +38 -0
  2. package/dist/abort-Ba8tDrOI.mjs.map +1 -0
  3. package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
  4. package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
  5. package/dist/builder-CyrGKRVP.mjs.map +1 -0
  6. package/dist/{client-Bd8B1mYm.mjs → client-BWsX9Cv1.mjs} +354 -195
  7. package/dist/client-BWsX9Cv1.mjs.map +1 -0
  8. package/dist/client.d.mts +1 -1
  9. package/dist/client.mjs +1 -1
  10. package/dist/core.d.mts +8 -8
  11. package/dist/core.mjs +8 -9
  12. package/dist/error-BW7re1UN.d.mts +9 -0
  13. package/dist/error-B__OMBRY.mjs +14 -0
  14. package/dist/error-B__OMBRY.mjs.map +1 -0
  15. package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
  16. package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
  17. package/dist/error-DrEIM7FO.mjs.map +1 -0
  18. package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
  19. package/dist/event-D8Nfoh4K.mjs.map +1 -0
  20. package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
  21. package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
  22. package/dist/filter-BQl3GlHt.mjs.map +1 -0
  23. package/dist/filter-C0IMByE3.d.mts +37 -0
  24. package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
  25. package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
  26. package/dist/gossip-oloDMWEj.mjs.map +1 -0
  27. package/dist/gossip.d.mts +1 -1
  28. package/dist/gossip.mjs +1 -1
  29. package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
  30. package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
  31. package/dist/http-CuazNRIP.mjs.map +1 -0
  32. package/dist/index-B425CAcQ.d.mts +328 -0
  33. package/dist/index-BVyfUBuG.d.mts +181 -0
  34. package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
  35. package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
  36. package/dist/index-CUIvFQdE.d.mts +73 -0
  37. package/dist/index-JdaS_uce.d.mts +207 -0
  38. package/dist/index.d.mts +22 -20
  39. package/dist/index.mjs +19 -18
  40. package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
  41. package/dist/key-Bbtys6o7.mjs.map +1 -0
  42. package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
  43. package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
  44. package/dist/kind-Cxwhhkx8.mjs.map +1 -0
  45. package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
  46. package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
  47. package/dist/loaders.d.mts +3 -3
  48. package/dist/loaders.mjs +2 -2
  49. package/dist/memory-C2EVx0_O.mjs +46 -0
  50. package/dist/memory-C2EVx0_O.mjs.map +1 -0
  51. package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
  52. package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
  53. package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
  54. package/dist/message-DcY6Fi9o.mjs.map +1 -0
  55. package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
  56. package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
  57. package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
  58. package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
  59. package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
  60. package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
  61. package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
  62. package/dist/nips/blossom.d.mts +37 -37
  63. package/dist/nips/blossom.mjs +67 -52
  64. package/dist/nips/blossom.mjs.map +1 -1
  65. package/dist/nips/nip04.d.mts +2 -3
  66. package/dist/nips/nip04.mjs +6 -10
  67. package/dist/nips/nip04.mjs.map +1 -1
  68. package/dist/nips/nip05.d.mts +37 -43
  69. package/dist/nips/nip05.mjs +57 -57
  70. package/dist/nips/nip05.mjs.map +1 -1
  71. package/dist/nips/nip10.d.mts +18 -19
  72. package/dist/nips/nip10.mjs +76 -61
  73. package/dist/nips/nip10.mjs.map +1 -1
  74. package/dist/nips/nip11.d.mts +9 -8
  75. package/dist/nips/nip11.mjs +14 -11
  76. package/dist/nips/nip11.mjs.map +1 -1
  77. package/dist/nips/nip13.d.mts +9 -9
  78. package/dist/nips/nip13.mjs +15 -17
  79. package/dist/nips/nip13.mjs.map +1 -1
  80. package/dist/nips/nip17.d.mts +1 -1
  81. package/dist/nips/nip17.mjs +17 -12
  82. package/dist/nips/nip17.mjs.map +1 -1
  83. package/dist/nips/nip19.d.mts +1 -1
  84. package/dist/nips/nip19.mjs +52 -29
  85. package/dist/nips/nip19.mjs.map +1 -1
  86. package/dist/nips/nip21.d.mts +5 -6
  87. package/dist/nips/nip21.mjs +8 -7
  88. package/dist/nips/nip21.mjs.map +1 -1
  89. package/dist/nips/nip27.d.mts +7 -8
  90. package/dist/nips/nip27.mjs +26 -25
  91. package/dist/nips/nip27.mjs.map +1 -1
  92. package/dist/nips/nip42.d.mts +5 -6
  93. package/dist/nips/nip42.mjs +3 -3
  94. package/dist/nips/nip42.mjs.map +1 -1
  95. package/dist/nips/nip44.d.mts +24 -7
  96. package/dist/nips/nip44.mjs +25 -15
  97. package/dist/nips/nip44.mjs.map +1 -1
  98. package/dist/nips/nip46.d.mts +1 -1
  99. package/dist/nips/nip46.mjs +52 -49
  100. package/dist/nips/nip46.mjs.map +1 -1
  101. package/dist/nips/nip49.d.mts +8 -7
  102. package/dist/nips/nip49.mjs +16 -12
  103. package/dist/nips/nip49.mjs.map +1 -1
  104. package/dist/nips/nip51.d.mts +25 -27
  105. package/dist/nips/nip51.mjs +47 -36
  106. package/dist/nips/nip51.mjs.map +1 -1
  107. package/dist/nips/nip57.d.mts +12 -13
  108. package/dist/nips/nip57.mjs +35 -38
  109. package/dist/nips/nip57.mjs.map +1 -1
  110. package/dist/nips/nip59.d.mts +1 -1
  111. package/dist/nips/nip59.mjs +14 -18
  112. package/dist/nips/nip59.mjs.map +1 -1
  113. package/dist/nips/nip65.d.mts +1 -1
  114. package/dist/nips/nip65.mjs +11 -8
  115. package/dist/nips/nip65.mjs.map +1 -1
  116. package/dist/nips/nip77.d.mts +1 -1
  117. package/dist/nips/nip77.mjs +56 -34
  118. package/dist/nips/nip77.mjs.map +1 -1
  119. package/dist/nips/nip96.d.mts +21 -12
  120. package/dist/nips/nip96.mjs +59 -36
  121. package/dist/nips/nip96.mjs.map +1 -1
  122. package/dist/nips/nip98.d.mts +12 -12
  123. package/dist/nips/nip98.mjs +18 -19
  124. package/dist/nips/nip98.mjs.map +1 -1
  125. package/dist/{pool-C_j1OmBB.mjs → pool-XmAGaufS.mjs} +611 -487
  126. package/dist/pool-XmAGaufS.mjs.map +1 -0
  127. package/dist/pool-tEr7c5bG.d.mts +302 -0
  128. package/dist/reactive-DDlThDSZ.mjs +324 -0
  129. package/dist/reactive-DDlThDSZ.mjs.map +1 -0
  130. package/dist/reactive-DFpqtIf_.d.mts +66 -0
  131. package/dist/relay.d.mts +5 -4
  132. package/dist/relay.mjs +2 -2
  133. package/dist/report-CHld4uWj.mjs +32 -0
  134. package/dist/report-CHld4uWj.mjs.map +1 -0
  135. package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
  136. package/dist/signer-CKR9EHce.mjs.map +1 -0
  137. package/dist/signer.d.mts +3 -3
  138. package/dist/signer.mjs +3 -2
  139. package/dist/storage-xXxLGCVb.mjs +1376 -0
  140. package/dist/storage-xXxLGCVb.mjs.map +1 -0
  141. package/dist/storage.d.mts +4 -4
  142. package/dist/storage.mjs +5 -4
  143. package/dist/store.d.mts +2 -0
  144. package/dist/store.mjs +2 -0
  145. package/dist/testing.d.mts +125 -0
  146. package/dist/testing.mjs +957 -0
  147. package/dist/testing.mjs.map +1 -0
  148. package/dist/types-2pCUprRU.d.mts +47 -0
  149. package/dist/types-CRQEk0Oi.d.mts +19 -0
  150. package/dist/util-LfQirgDk.mjs +113 -0
  151. package/dist/util-LfQirgDk.mjs.map +1 -0
  152. package/dist/wasm.d.mts +4 -4
  153. package/dist/wasm.mjs +54 -55
  154. package/dist/wasm.mjs.map +1 -1
  155. package/dist/websocket-CmeqvAkp.d.mts +27 -0
  156. package/package.json +31 -9
  157. package/dist/builder-q0VsvnYE.mjs.map +0 -1
  158. package/dist/client-Bd8B1mYm.mjs.map +0 -1
  159. package/dist/error-B49rbDzT.d.mts +0 -7
  160. package/dist/error-CwBxle6N.mjs.map +0 -1
  161. package/dist/error-DZEs5OuZ.mjs +0 -24
  162. package/dist/error-DZEs5OuZ.mjs.map +0 -1
  163. package/dist/event-CzoQEFMF.mjs.map +0 -1
  164. package/dist/filter-BxwjEkZp.mjs.map +0 -1
  165. package/dist/filter-DUEKP3q1.d.mts +0 -40
  166. package/dist/gossip-3xkRRCm_.mjs.map +0 -1
  167. package/dist/http-D2rhokZo.mjs.map +0 -1
  168. package/dist/index-C0C4e-Gn.d.mts +0 -55
  169. package/dist/index-DgOugrpF.d.mts +0 -275
  170. package/dist/index-EwL6fHLo.d.mts +0 -267
  171. package/dist/index-k2YhCgsh.d.mts +0 -57
  172. package/dist/key-Dn-aX4x4.mjs.map +0 -1
  173. package/dist/kind-BLl4cLOc.mjs.map +0 -1
  174. package/dist/loaders-BNQszDAn.mjs.map +0 -1
  175. package/dist/memory-BCmhZPek.mjs.map +0 -1
  176. package/dist/message-C_Uj-Y-c.mjs.map +0 -1
  177. package/dist/pool-C_j1OmBB.mjs.map +0 -1
  178. package/dist/pool-DhRi7l-b.d.mts +0 -297
  179. package/dist/signer-CZ6K6FUc.mjs.map +0 -1
  180. package/dist/storage-DOy0iLRE.mjs +0 -870
  181. package/dist/storage-DOy0iLRE.mjs.map +0 -1
  182. package/dist/types-BzyADqnw.d.mts +0 -19
  183. package/dist/types-Dn24TPFs.d.mts +0 -43
  184. package/dist/util-6tfLQ9JF.mjs +0 -73
  185. package/dist/util-6tfLQ9JF.mjs.map +0 -1
@@ -1,17 +1,16 @@
1
- import { t as Event } from "../event-B5D2jxt_.mjs";
2
- import { t as EventBuilder } from "../builder-BphJa-Y-.mjs";
1
+ import { t as Event } from "../event-fU3Y01PR.mjs";
2
+ import { t as EventBuilder } from "../builder-BWn55Paf.mjs";
3
3
  //#region src/nips/nip51.d.ts
4
4
  /**
5
- * Structural crypto used by NIP-51 private tags.
6
- * Satisfied by NostrSigner when nip44Encrypt/nip44Decrypt are present.
7
- * This module must not import src/signer/.
5
+ * Structural crypto used by NIP-51 private tags. Satisfied by NostrSigner when
6
+ * nip44Encrypt/nip44Decrypt are present. This module must not import src/signer/.
8
7
  */
9
- type Nip51Crypto = {
10
- getPublicKey(): Promise<string>;
11
- nip44Encrypt(peer: string, plaintext: string): Promise<string>;
12
- nip44Decrypt(peer: string, payload: string): Promise<string>;
8
+ export type Nip51Crypto = {
9
+ getPublicKey: () => Promise<string>;
10
+ nip44Encrypt: (peer: string, plaintext: string) => Promise<string>;
11
+ nip44Decrypt: (peer: string, payload: string) => Promise<string>;
13
12
  };
14
- type MuteItem = {
13
+ export type MuteItem = {
15
14
  type: "pubkey";
16
15
  value: string;
17
16
  } | {
@@ -25,34 +24,34 @@ type MuteItem = {
25
24
  value: string;
26
25
  };
27
26
  /** Parse kind:10000 mute list public tags (`p` / `e` / `t` / `word`). */
28
- declare function parseMuteList(event: Pick<Event, "kind" | "tags">): MuteItem[];
27
+ export declare function parseMuteList(event: Pick<Event, "kind" | "tags">): MuteItem[];
29
28
  /** Build an unsigned kind:10000 EventBuilder from public mute items. */
30
- declare function muteListEventBuilder(items: readonly MuteItem[]): EventBuilder;
29
+ export declare function muteListEventBuilder(items: ReadonlyArray<MuteItem>): EventBuilder;
31
30
  /** NIP-44 ciphertext of tags, conversation peer = author's own pubkey. */
32
- declare function encryptPrivateTags(crypto: Nip51Crypto, tags: Event["tags"]): Promise<string>;
31
+ export declare function encryptPrivateTags(crypto: Nip51Crypto, tags: Event["tags"]): Promise<string>;
33
32
  /** Decrypt NIP-51 `.content`. Empty content is no private tags, not ciphertext. */
34
- declare function decryptPrivateTags(crypto: Nip51Crypto, event: Pick<Event, "pubkey" | "content">): Promise<Event["tags"]>;
33
+ export declare function decryptPrivateTags(crypto: Nip51Crypto, event: Pick<Event, "pubkey" | "content">): Promise<Event["tags"]>;
35
34
  /** Public mute items from tags; private mute items from NIP-44 `.content`. */
36
- declare function parseMuteListPrivate(crypto: Nip51Crypto, event: Pick<Event, "kind" | "tags" | "pubkey" | "content">): Promise<{
35
+ export declare function parseMuteListPrivate(crypto: Nip51Crypto, event: Pick<Event, "kind" | "tags" | "pubkey" | "content">): Promise<{
37
36
  public: MuteItem[];
38
37
  private: MuteItem[];
39
38
  }>;
40
39
  /** Parse kind:10001 pin list public `e` tags. */
41
- declare function parsePinList(event: Pick<Event, "kind" | "tags">): string[];
40
+ export declare function parsePinList(event: Pick<Event, "kind" | "tags">): string[];
42
41
  /** Build an unsigned kind:10001 EventBuilder from event ids. */
43
- declare function pinListEventBuilder(ids: readonly string[]): EventBuilder;
42
+ export declare function pinListEventBuilder(ids: ReadonlyArray<string>): EventBuilder;
44
43
  /** Parse kind:10003 bookmark list public `e` and `a` tags. */
45
- declare function parseBookmarkList(event: Pick<Event, "kind" | "tags">): {
44
+ export declare function parseBookmarkList(event: Pick<Event, "kind" | "tags">): {
46
45
  e: string[];
47
46
  a: string[];
48
47
  };
49
48
  /** Build an unsigned kind:10003 EventBuilder from public bookmark pointers. */
50
- declare function bookmarkListEventBuilder(items: {
51
- e?: readonly string[];
52
- a?: readonly string[];
49
+ export declare function bookmarkListEventBuilder(items: {
50
+ e?: ReadonlyArray<string>;
51
+ a?: ReadonlyArray<string>;
53
52
  }): EventBuilder;
54
53
  /** Parse kind:10030 user emoji list public `emoji` and `a` tags. */
55
- declare function parseUserEmojiList(event: Pick<Event, "kind" | "tags">): {
54
+ export declare function parseUserEmojiList(event: Pick<Event, "kind" | "tags">): {
56
55
  emoji: Array<{
57
56
  shortcode: string;
58
57
  url: string;
@@ -60,17 +59,17 @@ declare function parseUserEmojiList(event: Pick<Event, "kind" | "tags">): {
60
59
  sets: string[];
61
60
  };
62
61
  /** Parse kind:30002 relay set public `d` and `relay` tags. */
63
- declare function parseRelaySet(event: Pick<Event, "kind" | "tags">): {
62
+ export declare function parseRelaySet(event: Pick<Event, "kind" | "tags">): {
64
63
  d: string;
65
64
  relays: string[];
66
65
  };
67
66
  /** Parse kind:10012 favorite relays public `relay` tags and kind:30002 `a` tags. */
68
- declare function parseFavoriteRelays(event: Pick<Event, "kind" | "tags">): {
67
+ export declare function parseFavoriteRelays(event: Pick<Event, "kind" | "tags">): {
69
68
  relays: string[];
70
69
  sets: string[];
71
70
  };
72
71
  /** Parse kind:30030 emoji set public `d` / `title` / `emoji` tags. */
73
- declare function parseEmojiSet(event: Pick<Event, "kind" | "tags">): {
72
+ export declare function parseEmojiSet(event: Pick<Event, "kind" | "tags">): {
74
73
  d: string;
75
74
  title?: string;
76
75
  emoji: Array<{
@@ -79,10 +78,9 @@ declare function parseEmojiSet(event: Pick<Event, "kind" | "tags">): {
79
78
  }>;
80
79
  };
81
80
  /** Parse kind:39089 starter pack (follow pack) public `d` and `p` tags. */
82
- declare function parseFollowPack(event: Pick<Event, "kind" | "tags">): {
81
+ export declare function parseFollowPack(event: Pick<Event, "kind" | "tags">): {
83
82
  d: string;
84
83
  pubkeys: string[];
85
84
  };
86
85
  //#endregion
87
- export { MuteItem, Nip51Crypto, bookmarkListEventBuilder, decryptPrivateTags, encryptPrivateTags, muteListEventBuilder, parseBookmarkList, parseEmojiSet, parseFavoriteRelays, parseFollowPack, parseMuteList, parseMuteListPrivate, parsePinList, parseRelaySet, parseUserEmojiList, pinListEventBuilder };
88
86
  //# sourceMappingURL=nip51.d.mts.map
@@ -1,14 +1,12 @@
1
- import { a as isHex32, s as normalizeURL, t as assertHex32 } from "../util-6tfLQ9JF.mjs";
2
- import { n as EventValidationError } from "../error-DZEs5OuZ.mjs";
3
- import { t as Kind } from "../kind-BLl4cLOc.mjs";
4
- import { _ as getDTag, m as Tag, v as isTag } from "../event-CzoQEFMF.mjs";
5
- import { t as EventBuilder } from "../builder-q0VsvnYE.mjs";
1
+ import { l as normalizeURL, n as assertHex32, o as isHex32, p as EventValidationError } from "../util-LfQirgDk.mjs";
2
+ import { t as Kind } from "../kind-Cxwhhkx8.mjs";
3
+ import { h as Tag, v as getDTag, y as isTag } from "../event-D8Nfoh4K.mjs";
4
+ import { t as EventBuilder } from "../builder-CyrGKRVP.mjs";
6
5
  //#region src/nips/nip51.ts
7
6
  /**
8
- * NIP-51: Lists
9
- * Public tags plus NIP-44 private `.content` (author encrypts to self).
10
- * Crypto is injected; this module does not import signer. No NIP-04 sniff.
11
- * Kind 10063 Blossom servers live in blossom.ts.
7
+ * NIP-51: Lists Public tags plus NIP-44 private `.content` (author encrypts to self). Crypto is
8
+ * injected; this module does not import signer. No NIP-04 sniff. Kind 10063 Blossom servers live in
9
+ * blossom.ts.
12
10
  *
13
11
  * @see https://github.com/nostr-protocol/nips/blob/master/51.md
14
12
  */
@@ -19,10 +17,11 @@ function collectRelays(tags) {
19
17
  const out = [];
20
18
  const seen = /* @__PURE__ */ new Set();
21
19
  for (const tag of tags) {
22
- if (tag[0] !== "relay" || !tag[1]) continue;
20
+ const value = tag.at(1);
21
+ if (tag[0] !== "relay" || value === void 0 || value === "") continue;
23
22
  let url;
24
23
  try {
25
- url = normalizeURL(tag[1]);
24
+ url = normalizeURL(value);
26
25
  } catch {
27
26
  continue;
28
27
  }
@@ -36,9 +35,9 @@ function collectEmoji(tags) {
36
35
  const out = [];
37
36
  for (const tag of tags) {
38
37
  if (tag[0] !== "emoji") continue;
39
- const shortcode = tag[1];
40
- const url = tag[2];
41
- if (!shortcode || !url) continue;
38
+ const shortcode = tag.at(1);
39
+ const url = tag.at(2);
40
+ if (shortcode === void 0 || shortcode === "" || url === void 0 || url === "") continue;
42
41
  out.push({
43
42
  shortcode,
44
43
  url
@@ -47,7 +46,10 @@ function collectEmoji(tags) {
47
46
  return out;
48
47
  }
49
48
  function firstTagValue(tags, name) {
50
- for (const tag of tags) if (tag[0] === name && tag[1]) return tag[1];
49
+ for (const tag of tags) {
50
+ const value = tag.at(1);
51
+ if (tag[0] === name && value !== void 0 && value !== "") return value;
52
+ }
51
53
  }
52
54
  function identifier(tags) {
53
55
  return getDTag(tags) ?? "";
@@ -55,30 +57,30 @@ function identifier(tags) {
55
57
  /** NIP-51 a-tag: `30002:<64-hex-pubkey>:<d>` with `d` possibly containing `:`. */
56
58
  function isRelaySetAddress(value) {
57
59
  const kindSep = value.indexOf(":");
58
- if (kindSep < 0) return false;
60
+ if (kindSep === -1) return false;
59
61
  if (value.slice(0, kindSep) !== String(Kind.RelaySets)) return false;
60
62
  const pkSep = value.indexOf(":", kindSep + 1);
61
- if (pkSep < 0) return false;
63
+ if (pkSep === -1) return false;
62
64
  const pubkey = value.slice(kindSep + 1, pkSep);
63
65
  const d = value.slice(pkSep + 1);
64
- return isHex32(pubkey) && d.length > 0;
66
+ return isHex32(pubkey.toLowerCase()) && d.length > 0;
65
67
  }
66
68
  /** Parse kind:10000 mute list public tags (`p` / `e` / `t` / `word`). */
67
69
  function parseMuteList(event) {
68
70
  requireKind(event, Kind.MuteList);
69
71
  const items = [];
70
72
  for (const tag of event.tags) {
71
- const value = tag[1];
72
- if (!value) continue;
73
+ const value = tag.at(1);
74
+ if (value === void 0 || value === "") continue;
73
75
  switch (tag[0]) {
74
76
  case "p":
75
- if (isHex32(value)) items.push({
77
+ if (isHex32(value.toLowerCase())) items.push({
76
78
  type: "pubkey",
77
79
  value: value.toLowerCase()
78
80
  });
79
81
  break;
80
82
  case "e":
81
- if (isHex32(value)) items.push({
83
+ if (isHex32(value.toLowerCase())) items.push({
82
84
  type: "event",
83
85
  value: value.toLowerCase()
84
86
  });
@@ -128,8 +130,8 @@ async function decryptPrivateTags(crypto, event) {
128
130
  let parsed;
129
131
  try {
130
132
  parsed = JSON.parse(plaintext);
131
- } catch (cause) {
132
- throw new EventValidationError("invalid NIP-51 private tags", { cause });
133
+ } catch (error) {
134
+ throw new EventValidationError("invalid NIP-51 private tags", { cause: error });
133
135
  }
134
136
  if (!Array.isArray(parsed) || !parsed.every(isTag)) throw new EventValidationError("invalid NIP-51 private tags");
135
137
  return parsed;
@@ -151,8 +153,9 @@ function parsePinList(event) {
151
153
  requireKind(event, Kind.PinList);
152
154
  const ids = [];
153
155
  for (const tag of event.tags) {
154
- if (tag[0] !== "e" || !tag[1] || !isHex32(tag[1])) continue;
155
- ids.push(tag[1].toLowerCase());
156
+ const value = tag.at(1);
157
+ if (tag[0] !== "e" || value === void 0 || !isHex32(value.toLowerCase())) continue;
158
+ ids.push(value.toLowerCase());
156
159
  }
157
160
  return ids;
158
161
  }
@@ -168,10 +171,11 @@ function parseBookmarkList(event) {
168
171
  const e = [];
169
172
  const a = [];
170
173
  for (const tag of event.tags) {
171
- if (!tag[1]) continue;
174
+ const value = tag.at(1);
175
+ if (value === void 0 || value === "") continue;
172
176
  if (tag[0] === "e") {
173
- if (isHex32(tag[1])) e.push(tag[1].toLowerCase());
174
- } else if (tag[0] === "a") a.push(tag[1]);
177
+ if (isHex32(value.toLowerCase())) e.push(value.toLowerCase());
178
+ } else if (tag[0] === "a") a.push(value);
175
179
  }
176
180
  return {
177
181
  e,
@@ -191,7 +195,10 @@ function bookmarkListEventBuilder(items) {
191
195
  function parseUserEmojiList(event) {
192
196
  requireKind(event, Kind.UserEmojiList);
193
197
  const sets = [];
194
- for (const tag of event.tags) if (tag[0] === "a" && tag[1]) sets.push(tag[1]);
198
+ for (const tag of event.tags) {
199
+ const value = tag.at(1);
200
+ if (tag[0] === "a" && value !== void 0 && value !== "") sets.push(value);
201
+ }
195
202
  return {
196
203
  emoji: collectEmoji(event.tags),
197
204
  sets
@@ -210,8 +217,9 @@ function parseFavoriteRelays(event) {
210
217
  requireKind(event, Kind.FavoriteRelays);
211
218
  const sets = [];
212
219
  for (const tag of event.tags) {
213
- if (tag[0] !== "a" || !tag[1] || !isRelaySetAddress(tag[1])) continue;
214
- sets.push(tag[1]);
220
+ const value = tag.at(1);
221
+ if (tag[0] !== "a" || value === void 0 || !isRelaySetAddress(value)) continue;
222
+ sets.push(value);
215
223
  }
216
224
  return {
217
225
  relays: collectRelays(event.tags),
@@ -221,19 +229,22 @@ function parseFavoriteRelays(event) {
221
229
  /** Parse kind:30030 emoji set public `d` / `title` / `emoji` tags. */
222
230
  function parseEmojiSet(event) {
223
231
  requireKind(event, Kind.EmojiSet);
224
- return {
232
+ const result = {
225
233
  d: identifier(event.tags),
226
- title: firstTagValue(event.tags, "title"),
227
234
  emoji: collectEmoji(event.tags)
228
235
  };
236
+ const title = firstTagValue(event.tags, "title");
237
+ if (title !== void 0) result.title = title;
238
+ return result;
229
239
  }
230
240
  /** Parse kind:39089 starter pack (follow pack) public `d` and `p` tags. */
231
241
  function parseFollowPack(event) {
232
242
  requireKind(event, Kind.StarterPack);
233
243
  const pubkeys = [];
234
244
  for (const tag of event.tags) {
235
- if (tag[0] !== "p" || !tag[1] || !isHex32(tag[1])) continue;
236
- pubkeys.push(tag[1].toLowerCase());
245
+ const value = tag.at(1);
246
+ if (tag[0] !== "p" || value === void 0 || !isHex32(value.toLowerCase())) continue;
247
+ pubkeys.push(value.toLowerCase());
237
248
  }
238
249
  return {
239
250
  d: identifier(event.tags),
@@ -1 +1 @@
1
- {"version":3,"file":"nip51.mjs","names":[],"sources":["../../src/nips/nip51.ts"],"sourcesContent":["/**\n * NIP-51: Lists\n * Public tags plus NIP-44 private `.content` (author encrypts to self).\n * Crypto is injected; this module does not import signer. No NIP-04 sniff.\n * Kind 10063 Blossom servers live in blossom.ts.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/51.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { getDTag, isTag, Tag } from \"../core/tag.ts\";\nimport { assertHex32, isHex32, normalizeURL } from \"../core/util.ts\";\n\n/**\n * Structural crypto used by NIP-51 private tags.\n * Satisfied by NostrSigner when nip44Encrypt/nip44Decrypt are present.\n * This module must not import src/signer/.\n */\nexport type Nip51Crypto = {\n getPublicKey(): Promise<string>;\n nip44Encrypt(peer: string, plaintext: string): Promise<string>;\n nip44Decrypt(peer: string, payload: string): Promise<string>;\n};\n\nexport type MuteItem =\n | { type: \"pubkey\"; value: string }\n | { type: \"event\"; value: string }\n | { type: \"hashtag\"; value: string }\n | { type: \"word\"; value: string };\n\nfunction requireKind(event: Pick<Event, \"kind\">, kind: number): void {\n if (event.kind !== kind) {\n throw new EventValidationError(`expected kind ${kind}, got ${event.kind}`);\n }\n}\n\nfunction collectRelays(tags: Event[\"tags\"]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n if (tag[0] !== \"relay\" || !tag[1]) continue;\n let url: string;\n try {\n url = normalizeURL(tag[1]);\n } catch {\n continue;\n }\n if (seen.has(url)) continue;\n seen.add(url);\n out.push(url);\n }\n return out;\n}\n\nfunction collectEmoji(tags: Event[\"tags\"]): Array<{ shortcode: string; url: string }> {\n const out: Array<{ shortcode: string; url: string }> = [];\n for (const tag of tags) {\n if (tag[0] !== \"emoji\") continue;\n const shortcode = tag[1];\n const url = tag[2];\n if (!shortcode || !url) continue;\n out.push({ shortcode, url });\n }\n return out;\n}\n\nfunction firstTagValue(tags: Event[\"tags\"], name: string): string | undefined {\n for (const tag of tags) {\n if (tag[0] === name && tag[1]) return tag[1];\n }\n return undefined;\n}\n\nfunction identifier(tags: Event[\"tags\"]): string {\n return getDTag(tags) ?? \"\";\n}\n\n/** NIP-51 a-tag: `30002:<64-hex-pubkey>:<d>` with `d` possibly containing `:`. */\nfunction isRelaySetAddress(value: string): boolean {\n const kindSep = value.indexOf(\":\");\n if (kindSep < 0) return false;\n if (value.slice(0, kindSep) !== String(Kind.RelaySets)) return false;\n const pkSep = value.indexOf(\":\", kindSep + 1);\n if (pkSep < 0) return false;\n const pubkey = value.slice(kindSep + 1, pkSep);\n const d = value.slice(pkSep + 1);\n return isHex32(pubkey) && d.length > 0;\n}\n\n/** Parse kind:10000 mute list public tags (`p` / `e` / `t` / `word`). */\nexport function parseMuteList(event: Pick<Event, \"kind\" | \"tags\">): MuteItem[] {\n requireKind(event, Kind.MuteList);\n const items: MuteItem[] = [];\n for (const tag of event.tags) {\n const value = tag[1];\n if (!value) continue;\n switch (tag[0]) {\n case \"p\":\n if (isHex32(value)) items.push({ type: \"pubkey\", value: value.toLowerCase() });\n break;\n case \"e\":\n if (isHex32(value)) items.push({ type: \"event\", value: value.toLowerCase() });\n break;\n case \"t\":\n // NIP-51 does not require hashtag case-folding.\n items.push({ type: \"hashtag\", value });\n break;\n case \"word\":\n items.push({ type: \"word\", value: value.toLowerCase() });\n break;\n }\n }\n return items;\n}\n\n/** Build an unsigned kind:10000 EventBuilder from public mute items. */\nexport function muteListEventBuilder(items: readonly MuteItem[]): EventBuilder {\n const b = new EventBuilder(Kind.MuteList, \"\");\n for (const item of items) {\n switch (item.type) {\n case \"pubkey\":\n b.tag(Tag.p(assertHex32(item.value, \"public key\")));\n break;\n case \"event\":\n b.tag(Tag.e(assertHex32(item.value, \"event id\")));\n break;\n case \"hashtag\":\n if (item.value) b.tag(Tag.t(item.value));\n break;\n case \"word\":\n if (item.value) b.tag([\"word\", item.value.toLowerCase()]);\n break;\n }\n }\n return b;\n}\n\n/** NIP-44 ciphertext of tags, conversation peer = author's own pubkey. */\nexport async function encryptPrivateTags(\n crypto: Nip51Crypto,\n tags: Event[\"tags\"],\n): Promise<string> {\n const self = await crypto.getPublicKey();\n return crypto.nip44Encrypt(self, JSON.stringify(tags));\n}\n\n/** Decrypt NIP-51 `.content`. Empty content is no private tags, not ciphertext. */\nexport async function decryptPrivateTags(\n crypto: Nip51Crypto,\n event: Pick<Event, \"pubkey\" | \"content\">,\n): Promise<Event[\"tags\"]> {\n // NIP-44 rejects empty plaintext; empty `.content` means no private items.\n if (event.content === \"\") return [];\n const self = await crypto.getPublicKey();\n if (self.toLowerCase() !== event.pubkey.toLowerCase()) {\n throw new EventValidationError(\"NIP-51 private content is only for the author\");\n }\n const plaintext = await crypto.nip44Decrypt(self, event.content);\n let parsed: unknown;\n try {\n parsed = JSON.parse(plaintext);\n } catch (cause) {\n throw new EventValidationError(\"invalid NIP-51 private tags\", { cause });\n }\n if (!Array.isArray(parsed) || !parsed.every(isTag)) {\n throw new EventValidationError(\"invalid NIP-51 private tags\");\n }\n return parsed;\n}\n\n/** Public mute items from tags; private mute items from NIP-44 `.content`. */\nexport async function parseMuteListPrivate(\n crypto: Nip51Crypto,\n event: Pick<Event, \"kind\" | \"tags\" | \"pubkey\" | \"content\">,\n): Promise<{ public: MuteItem[]; private: MuteItem[] }> {\n const pub = parseMuteList(event);\n const decrypted = await decryptPrivateTags(crypto, event);\n return {\n public: pub,\n private: parseMuteList({ kind: event.kind, tags: decrypted }),\n };\n}\n\n/** Parse kind:10001 pin list public `e` tags. */\nexport function parsePinList(event: Pick<Event, \"kind\" | \"tags\">): string[] {\n requireKind(event, Kind.PinList);\n const ids: string[] = [];\n for (const tag of event.tags) {\n if (tag[0] !== \"e\" || !tag[1] || !isHex32(tag[1])) continue;\n ids.push(tag[1].toLowerCase());\n }\n return ids;\n}\n\n/** Build an unsigned kind:10001 EventBuilder from event ids. */\nexport function pinListEventBuilder(ids: readonly string[]): EventBuilder {\n const b = new EventBuilder(Kind.PinList, \"\");\n for (const id of ids) b.tag(Tag.e(assertHex32(id, \"event id\")));\n return b;\n}\n\n/** Parse kind:10003 bookmark list public `e` and `a` tags. */\nexport function parseBookmarkList(event: Pick<Event, \"kind\" | \"tags\">): {\n e: string[];\n a: string[];\n} {\n requireKind(event, Kind.BookmarkList);\n const e: string[] = [];\n const a: string[] = [];\n for (const tag of event.tags) {\n if (!tag[1]) continue;\n if (tag[0] === \"e\") {\n if (isHex32(tag[1])) e.push(tag[1].toLowerCase());\n } else if (tag[0] === \"a\") {\n a.push(tag[1]);\n }\n }\n return { e, a };\n}\n\n/** Build an unsigned kind:10003 EventBuilder from public bookmark pointers. */\nexport function bookmarkListEventBuilder(items: {\n e?: readonly string[];\n a?: readonly string[];\n}): EventBuilder {\n const b = new EventBuilder(Kind.BookmarkList, \"\");\n if (items.e) {\n for (const id of items.e) b.tag(Tag.e(assertHex32(id, \"event id\")));\n }\n if (items.a) {\n for (const coord of items.a) {\n if (coord) b.tag(Tag.a(coord));\n }\n }\n return b;\n}\n\n/** Parse kind:10030 user emoji list public `emoji` and `a` tags. */\nexport function parseUserEmojiList(event: Pick<Event, \"kind\" | \"tags\">): {\n emoji: Array<{ shortcode: string; url: string }>;\n sets: string[];\n} {\n requireKind(event, Kind.UserEmojiList);\n const sets: string[] = [];\n for (const tag of event.tags) {\n if (tag[0] === \"a\" && tag[1]) sets.push(tag[1]);\n }\n return { emoji: collectEmoji(event.tags), sets };\n}\n\n/** Parse kind:30002 relay set public `d` and `relay` tags. */\nexport function parseRelaySet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n relays: string[];\n} {\n requireKind(event, Kind.RelaySets);\n return { d: identifier(event.tags), relays: collectRelays(event.tags) };\n}\n\n/** Parse kind:10012 favorite relays public `relay` tags and kind:30002 `a` tags. */\nexport function parseFavoriteRelays(event: Pick<Event, \"kind\" | \"tags\">): {\n relays: string[];\n sets: string[];\n} {\n requireKind(event, Kind.FavoriteRelays);\n const sets: string[] = [];\n for (const tag of event.tags) {\n if (tag[0] !== \"a\" || !tag[1] || !isRelaySetAddress(tag[1])) continue;\n sets.push(tag[1]);\n }\n return { relays: collectRelays(event.tags), sets };\n}\n\n/** Parse kind:30030 emoji set public `d` / `title` / `emoji` tags. */\nexport function parseEmojiSet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n title?: string;\n emoji: Array<{ shortcode: string; url: string }>;\n} {\n requireKind(event, Kind.EmojiSet);\n return {\n d: identifier(event.tags),\n title: firstTagValue(event.tags, \"title\"),\n emoji: collectEmoji(event.tags),\n };\n}\n\n/** Parse kind:39089 starter pack (follow pack) public `d` and `p` tags. */\nexport function parseFollowPack(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n pubkeys: string[];\n} {\n requireKind(event, Kind.StarterPack);\n const pubkeys: string[] = [];\n for (const tag of event.tags) {\n if (tag[0] !== \"p\" || !tag[1] || !isHex32(tag[1])) continue;\n pubkeys.push(tag[1].toLowerCase());\n }\n return { d: identifier(event.tags), pubkeys };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip51.mjs","names":[],"sources":["../../src/nips/nip51.ts"],"sourcesContent":["/**\n * NIP-51: Lists Public tags plus NIP-44 private `.content` (author encrypts to self). Crypto is\n * injected; this module does not import signer. No NIP-04 sniff. Kind 10063 Blossom servers live in\n * blossom.ts.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/51.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { getDTag, isTag, Tag } from \"../core/tag.ts\";\nimport { assertHex32, isHex32, normalizeURL } from \"../core/util.ts\";\n\n/**\n * Structural crypto used by NIP-51 private tags. Satisfied by NostrSigner when\n * nip44Encrypt/nip44Decrypt are present. This module must not import src/signer/.\n */\nexport type Nip51Crypto = {\n getPublicKey: () => Promise<string>;\n nip44Encrypt: (peer: string, plaintext: string) => Promise<string>;\n nip44Decrypt: (peer: string, payload: string) => Promise<string>;\n};\n\nexport type MuteItem =\n | { type: \"pubkey\"; value: string }\n | { type: \"event\"; value: string }\n | { type: \"hashtag\"; value: string }\n | { type: \"word\"; value: string };\n\nfunction requireKind(event: Pick<Event, \"kind\">, kind: number): void {\n if (event.kind !== kind) {\n throw new EventValidationError(`expected kind ${kind}, got ${event.kind}`);\n }\n}\n\nfunction collectRelays(tags: Event[\"tags\"]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n const value = tag.at(1);\n if (tag[0] !== \"relay\" || value === undefined || value === \"\") {\n continue;\n }\n let url: string;\n try {\n url = normalizeURL(value);\n } catch {\n continue;\n }\n if (seen.has(url)) {\n continue;\n }\n seen.add(url);\n out.push(url);\n }\n return out;\n}\n\nfunction collectEmoji(tags: Event[\"tags\"]): Array<{ shortcode: string; url: string }> {\n const out: Array<{ shortcode: string; url: string }> = [];\n for (const tag of tags) {\n if (tag[0] !== \"emoji\") {\n continue;\n }\n const shortcode = tag.at(1);\n const url = tag.at(2);\n if (shortcode === undefined || shortcode === \"\" || url === undefined || url === \"\") {\n continue;\n }\n out.push({ shortcode, url });\n }\n return out;\n}\n\nfunction firstTagValue(tags: Event[\"tags\"], name: string): string | undefined {\n for (const tag of tags) {\n const value = tag.at(1);\n if (tag[0] === name && value !== undefined && value !== \"\") {\n return value;\n }\n }\n return undefined;\n}\n\nfunction identifier(tags: Event[\"tags\"]): string {\n return getDTag(tags) ?? \"\";\n}\n\n/** NIP-51 a-tag: `30002:<64-hex-pubkey>:<d>` with `d` possibly containing `:`. */\nfunction isRelaySetAddress(value: string): boolean {\n const kindSep = value.indexOf(\":\");\n if (kindSep === -1) {\n return false;\n }\n if (value.slice(0, kindSep) !== String(Kind.RelaySets)) {\n return false;\n }\n const pkSep = value.indexOf(\":\", kindSep + 1);\n if (pkSep === -1) {\n return false;\n }\n const pubkey = value.slice(kindSep + 1, pkSep);\n const d = value.slice(pkSep + 1);\n return isHex32(pubkey.toLowerCase()) && d.length > 0;\n}\n\n/** Parse kind:10000 mute list public tags (`p` / `e` / `t` / `word`). */\nexport function parseMuteList(event: Pick<Event, \"kind\" | \"tags\">): MuteItem[] {\n requireKind(event, Kind.MuteList);\n const items: MuteItem[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (value === undefined || value === \"\") {\n continue;\n }\n switch (tag[0]) {\n case \"p\":\n if (isHex32(value.toLowerCase())) {\n items.push({ type: \"pubkey\", value: value.toLowerCase() });\n }\n break;\n case \"e\":\n if (isHex32(value.toLowerCase())) {\n items.push({ type: \"event\", value: value.toLowerCase() });\n }\n break;\n case \"t\":\n // NIP-51 does not require hashtag case-folding.\n items.push({ type: \"hashtag\", value });\n break;\n case \"word\":\n items.push({ type: \"word\", value: value.toLowerCase() });\n break;\n default:\n break;\n }\n }\n return items;\n}\n\n/** Build an unsigned kind:10000 EventBuilder from public mute items. */\nexport function muteListEventBuilder(items: ReadonlyArray<MuteItem>): EventBuilder {\n const b = new EventBuilder(Kind.MuteList, \"\");\n for (const item of items) {\n switch (item.type) {\n case \"pubkey\":\n b.tag(Tag.p(assertHex32(item.value, \"public key\")));\n break;\n case \"event\":\n b.tag(Tag.e(assertHex32(item.value, \"event id\")));\n break;\n case \"hashtag\":\n if (item.value) {\n b.tag(Tag.t(item.value));\n }\n break;\n case \"word\":\n if (item.value) {\n b.tag([\"word\", item.value.toLowerCase()]);\n }\n break;\n }\n }\n return b;\n}\n\n/** NIP-44 ciphertext of tags, conversation peer = author's own pubkey. */\nexport async function encryptPrivateTags(\n crypto: Nip51Crypto,\n tags: Event[\"tags\"],\n): Promise<string> {\n const self = await crypto.getPublicKey();\n return crypto.nip44Encrypt(self, JSON.stringify(tags));\n}\n\n/** Decrypt NIP-51 `.content`. Empty content is no private tags, not ciphertext. */\nexport async function decryptPrivateTags(\n crypto: Nip51Crypto,\n event: Pick<Event, \"pubkey\" | \"content\">,\n): Promise<Event[\"tags\"]> {\n // NIP-44 rejects empty plaintext; empty `.content` means no private items.\n if (event.content === \"\") {\n return [];\n }\n const self = await crypto.getPublicKey();\n if (self.toLowerCase() !== event.pubkey.toLowerCase()) {\n throw new EventValidationError(\"NIP-51 private content is only for the author\");\n }\n const plaintext = await crypto.nip44Decrypt(self, event.content);\n let parsed: unknown;\n try {\n parsed = JSON.parse(plaintext);\n } catch (error) {\n throw new EventValidationError(\"invalid NIP-51 private tags\", { cause: error });\n }\n if (!Array.isArray(parsed) || !parsed.every(isTag)) {\n throw new EventValidationError(\"invalid NIP-51 private tags\");\n }\n return parsed;\n}\n\n/** Public mute items from tags; private mute items from NIP-44 `.content`. */\nexport async function parseMuteListPrivate(\n crypto: Nip51Crypto,\n event: Pick<Event, \"kind\" | \"tags\" | \"pubkey\" | \"content\">,\n): Promise<{ public: MuteItem[]; private: MuteItem[] }> {\n const pub = parseMuteList(event);\n const decrypted = await decryptPrivateTags(crypto, event);\n return {\n public: pub,\n private: parseMuteList({ kind: event.kind, tags: decrypted }),\n };\n}\n\n/** Parse kind:10001 pin list public `e` tags. */\nexport function parsePinList(event: Pick<Event, \"kind\" | \"tags\">): string[] {\n requireKind(event, Kind.PinList);\n const ids: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"e\" || value === undefined || !isHex32(value.toLowerCase())) {\n continue;\n }\n ids.push(value.toLowerCase());\n }\n return ids;\n}\n\n/** Build an unsigned kind:10001 EventBuilder from event ids. */\nexport function pinListEventBuilder(ids: ReadonlyArray<string>): EventBuilder {\n const b = new EventBuilder(Kind.PinList, \"\");\n for (const id of ids) {\n b.tag(Tag.e(assertHex32(id, \"event id\")));\n }\n return b;\n}\n\n/** Parse kind:10003 bookmark list public `e` and `a` tags. */\nexport function parseBookmarkList(event: Pick<Event, \"kind\" | \"tags\">): {\n e: string[];\n a: string[];\n} {\n requireKind(event, Kind.BookmarkList);\n const e: string[] = [];\n const a: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (value === undefined || value === \"\") {\n continue;\n }\n if (tag[0] === \"e\") {\n if (isHex32(value.toLowerCase())) {\n e.push(value.toLowerCase());\n }\n } else if (tag[0] === \"a\") {\n a.push(value);\n }\n }\n return { e, a };\n}\n\n/** Build an unsigned kind:10003 EventBuilder from public bookmark pointers. */\nexport function bookmarkListEventBuilder(items: {\n e?: ReadonlyArray<string>;\n a?: ReadonlyArray<string>;\n}): EventBuilder {\n const b = new EventBuilder(Kind.BookmarkList, \"\");\n if (items.e) {\n for (const id of items.e) {\n b.tag(Tag.e(assertHex32(id, \"event id\")));\n }\n }\n if (items.a) {\n for (const coord of items.a) {\n if (coord) {\n b.tag(Tag.a(coord));\n }\n }\n }\n return b;\n}\n\n/** Parse kind:10030 user emoji list public `emoji` and `a` tags. */\nexport function parseUserEmojiList(event: Pick<Event, \"kind\" | \"tags\">): {\n emoji: Array<{ shortcode: string; url: string }>;\n sets: string[];\n} {\n requireKind(event, Kind.UserEmojiList);\n const sets: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] === \"a\" && value !== undefined && value !== \"\") {\n sets.push(value);\n }\n }\n return { emoji: collectEmoji(event.tags), sets };\n}\n\n/** Parse kind:30002 relay set public `d` and `relay` tags. */\nexport function parseRelaySet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n relays: string[];\n} {\n requireKind(event, Kind.RelaySets);\n return { d: identifier(event.tags), relays: collectRelays(event.tags) };\n}\n\n/** Parse kind:10012 favorite relays public `relay` tags and kind:30002 `a` tags. */\nexport function parseFavoriteRelays(event: Pick<Event, \"kind\" | \"tags\">): {\n relays: string[];\n sets: string[];\n} {\n requireKind(event, Kind.FavoriteRelays);\n const sets: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"a\" || value === undefined || !isRelaySetAddress(value)) {\n continue;\n }\n sets.push(value);\n }\n return { relays: collectRelays(event.tags), sets };\n}\n\n/** Parse kind:30030 emoji set public `d` / `title` / `emoji` tags. */\nexport function parseEmojiSet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n title?: string;\n emoji: Array<{ shortcode: string; url: string }>;\n} {\n requireKind(event, Kind.EmojiSet);\n const result: {\n d: string;\n title?: string;\n emoji: Array<{ shortcode: string; url: string }>;\n } = { d: identifier(event.tags), emoji: collectEmoji(event.tags) };\n const title = firstTagValue(event.tags, \"title\");\n if (title !== undefined) {\n result.title = title;\n }\n return result;\n}\n\n/** Parse kind:39089 starter pack (follow pack) public `d` and `p` tags. */\nexport function parseFollowPack(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n pubkeys: string[];\n} {\n requireKind(event, Kind.StarterPack);\n const pubkeys: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"p\" || value === undefined || !isHex32(value.toLowerCase())) {\n continue;\n }\n pubkeys.push(value.toLowerCase());\n }\n return { d: identifier(event.tags), pubkeys };\n}\n"],"mappings":"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"}
@@ -1,43 +1,42 @@
1
- import { n as EventTemplate, t as Event } from "../event-B5D2jxt_.mjs";
1
+ import { n as EventTemplate, t as Event } from "../event-fU3Y01PR.mjs";
2
2
  //#region src/nips/nip57.d.ts
3
- type ProfileZapRequest = {
3
+ export type ProfileZapRequest = {
4
4
  pubkey: string;
5
5
  /** Amount in millisats. */
6
6
  amount: number;
7
- relays: readonly string[];
7
+ relays: ReadonlyArray<string>;
8
8
  comment?: string;
9
9
  lnurl?: string;
10
10
  };
11
- type EventZapRequest = {
11
+ export type EventZapRequest = {
12
12
  event: Event;
13
13
  /** Amount in millisats. */
14
14
  amount: number;
15
- relays: readonly string[];
15
+ relays: ReadonlyArray<string>;
16
16
  comment?: string;
17
17
  lnurl?: string;
18
18
  };
19
- declare function makeZapRequest(params: ProfileZapRequest | EventZapRequest): EventTemplate;
20
- type ZapReceiptContext = {
19
+ export declare function makeZapRequest(params: ProfileZapRequest | EventZapRequest): EventTemplate;
20
+ export type ZapReceiptContext = {
21
21
  nostrPubkey: string;
22
22
  lnurl?: string;
23
23
  };
24
- type ZapReceiptValidation = {
24
+ export type ZapReceiptValidation = {
25
25
  valid: boolean;
26
26
  reason?: string;
27
27
  request?: Event;
28
28
  amountMsats?: number;
29
29
  };
30
- type Bolt11Fields = {
30
+ export type Bolt11Fields = {
31
31
  amountMsats?: number;
32
32
  descriptionHash?: Uint8Array;
33
33
  paymentHash?: Uint8Array;
34
34
  };
35
35
  /** Decode a BOLT11 invoice. Never throws. Requires a 32-byte payment hash (type 1). */
36
- declare function parseBolt11(pr: string): Bolt11Fields | undefined;
36
+ export declare function parseBolt11(pr: string): Bolt11Fields | undefined;
37
37
  /** Parse the `description` tag of a kind 9735 receipt as a signed kind 9734. Never throws. */
38
- declare function parseZapRequestFromReceipt(receipt: Event): Event | undefined;
38
+ export declare function parseZapRequestFromReceipt(receipt: Event): Event | undefined;
39
39
  /** Validate a kind 9735 zap receipt. Never throws. */
40
- declare function validateZapReceipt(receipt: Event, ctx: ZapReceiptContext): ZapReceiptValidation;
40
+ export declare function validateZapReceipt(receipt: Event, ctx: ZapReceiptContext): ZapReceiptValidation;
41
41
  //#endregion
42
- export { Bolt11Fields, EventZapRequest, ProfileZapRequest, ZapReceiptContext, ZapReceiptValidation, makeZapRequest, parseBolt11, parseZapRequestFromReceipt, validateZapReceipt };
43
42
  //# sourceMappingURL=nip57.d.mts.map
@@ -1,14 +1,14 @@
1
- import { i as hexToBytes, l as utf8Encoder } from "../util-6tfLQ9JF.mjs";
2
- import { n as EventValidationError } from "../error-DZEs5OuZ.mjs";
3
- import { r as isAddressableKind, t as Kind } from "../kind-BLl4cLOc.mjs";
4
- import { _ as getDTag, h as eventAddress, m as Tag, p as validateSignedEvent, y as parseEventAddress } from "../event-CzoQEFMF.mjs";
5
- import { s as verifyEvent } from "../key-Dn-aX4x4.mjs";
1
+ import { a as hexToBytes, d as utf8Encoder, p as EventValidationError } from "../util-LfQirgDk.mjs";
2
+ import { r as isAddressableKind, t as Kind } from "../kind-Cxwhhkx8.mjs";
3
+ import { b as parseEventAddress, g as eventAddress, h as Tag, m as validateSignedEvent, v as getDTag } from "../event-D8Nfoh4K.mjs";
4
+ import { s as verifyEvent } from "../key-Bbtys6o7.mjs";
6
5
  import { sha256 } from "@noble/hashes/sha2.js";
7
6
  import { bech32 } from "@scure/base";
7
+ import { equalBytes } from "@noble/ciphers/utils.js";
8
8
  //#region src/nips/nip57.ts
9
9
  /**
10
- * NIP-57 Lightning Zaps — zap request template (kind 9734) and receipt validation (kind 9735).
11
- * Does not fetch LNURL. Receipt checks never throw.
10
+ * NIP-57 Lightning Zaps — zap request template (kind 9734) and receipt validation (kind 9735). Does
11
+ * not fetch LNURL. Receipt checks never throw.
12
12
  *
13
13
  * @see https://github.com/nostr-protocol/nips/blob/master/57.md
14
14
  */
@@ -26,11 +26,11 @@ function makeZapRequest(params) {
26
26
  if (isAddressableKind(event.kind)) {
27
27
  if (getDTag(event.tags) === void 0) throw new EventValidationError("d tag not found");
28
28
  const addr = eventAddress(event);
29
- if (addr) tags.push(["a", addr]);
29
+ if (addr !== void 0) tags.push(["a", addr]);
30
30
  }
31
31
  tags.push(["k", event.kind.toString()]);
32
32
  }
33
- if (params.lnurl) tags.push(["lnurl", params.lnurl]);
33
+ if (params.lnurl !== void 0 && params.lnurl !== "") tags.push(["lnurl", params.lnurl]);
34
34
  return {
35
35
  kind: Kind.ZapRequest,
36
36
  created_at: Math.floor(Date.now() / 1e3),
@@ -41,12 +41,6 @@ function makeZapRequest(params) {
41
41
  function firstTagValue(tags, name) {
42
42
  for (const tag of tags) if (tag[0] === name && tag[1] !== void 0) return tag[1];
43
43
  }
44
- function bytesEq(a, b) {
45
- if (a.length !== b.length) return false;
46
- let x = 0;
47
- for (let i = 0; i < a.length; i++) x |= a[i] ^ b[i];
48
- return x === 0;
49
- }
50
44
  function fail(reason) {
51
45
  return {
52
46
  valid: false,
@@ -59,29 +53,29 @@ const MSATS_PER_MICRO = 1e5;
59
53
  const MSATS_PER_NANO = 100;
60
54
  /** Amount is digits after `ln` + currency letters, optional m/u/n/p. Non-integer pico is omitted. */
61
55
  function amountMsatsFromHrp(hrp) {
62
- if (!hrp.startsWith("ln")) return void 0;
56
+ if (!hrp.startsWith("ln")) return;
63
57
  let i = 2;
64
58
  while (i < hrp.length) {
65
- const c = hrp.charCodeAt(i);
66
- if (c < 97 || c > 122) break;
59
+ const c = hrp.codePointAt(i);
60
+ if (c === void 0 || c < 97 || c > 122) break;
67
61
  i++;
68
62
  }
69
- if (i === hrp.length) return void 0;
63
+ if (i === hrp.length) return;
70
64
  const rest = hrp.slice(i);
71
65
  const m = /^([0-9]+)([munp])?$/.exec(rest);
72
- if (!m) return void 0;
66
+ if (!m) return;
73
67
  const n = Number(m[1]);
74
- if (!Number.isSafeInteger(n)) return void 0;
75
- const mul = m[2];
68
+ if (!Number.isSafeInteger(n)) return;
69
+ const mul = m.at(2);
76
70
  if (mul === void 0) return n * MSATS_PER_BTC;
77
71
  if (mul === "m") return n * MSATS_PER_MILLI;
78
72
  if (mul === "u") return n * MSATS_PER_MICRO;
79
73
  if (mul === "n") return n * MSATS_PER_NANO;
80
- if (n % 10 !== 0) return void 0;
74
+ if (n % 10 !== 0) return;
81
75
  return n / 10;
82
76
  }
83
77
  function parseMsatsTag(value) {
84
- if (!/^[0-9]+$/.test(value)) return void 0;
78
+ if (!/^[0-9]+$/.test(value)) return;
85
79
  const n = Number(value);
86
80
  return Number.isSafeInteger(n) ? n : void 0;
87
81
  }
@@ -113,8 +107,8 @@ const HASH_BYTES = 32;
113
107
  function parseBolt11(pr) {
114
108
  try {
115
109
  const { prefix, words } = bech32.decode(pr.toLowerCase(), false);
116
- if (!prefix.startsWith("ln")) return void 0;
117
- if (words.length < 111) return void 0;
110
+ if (!prefix.startsWith("ln")) return;
111
+ if (words.length < 111) return;
118
112
  const fields = {};
119
113
  const amountMsats = amountMsatsFromHrp(prefix);
120
114
  if (amountMsats !== void 0) fields.amountMsats = amountMsats;
@@ -122,7 +116,10 @@ function parseBolt11(pr) {
122
116
  let i = BOLT11_TIMESTAMP_WORDS;
123
117
  while (i + 3 <= tlvEnd) {
124
118
  const type = words[i];
125
- const dataLen = words[i + 1] << 5 | words[i + 2];
119
+ const lenHigh = words[i + 1];
120
+ const lenLow = words[i + 2];
121
+ if (type === void 0 || lenHigh === void 0 || lenLow === void 0) break;
122
+ const dataLen = lenHigh * 32 + lenLow;
126
123
  i += 3;
127
124
  if (i + dataLen > tlvEnd) break;
128
125
  const data = words.slice(i, i + dataLen);
@@ -133,7 +130,7 @@ function parseBolt11(pr) {
133
130
  if (type === 1) fields.paymentHash ??= bytes;
134
131
  else fields.descriptionHash ??= bytes;
135
132
  }
136
- if (!fields.paymentHash) return void 0;
133
+ if (!fields.paymentHash) return;
137
134
  return fields;
138
135
  } catch {
139
136
  return;
@@ -143,10 +140,10 @@ function parseBolt11(pr) {
143
140
  function parseZapRequestFromReceipt(receipt) {
144
141
  try {
145
142
  const raw = firstTagValue(receipt.tags, "description");
146
- if (raw === void 0) return void 0;
143
+ if (raw === void 0) return;
147
144
  const parsed = JSON.parse(raw);
148
- if (!validateSignedEvent(parsed) || parsed.kind !== Kind.ZapRequest) return void 0;
149
- if (!verifyEvent(parsed)) return void 0;
145
+ if (!validateSignedEvent(parsed) || parsed.kind !== Kind.ZapRequest) return;
146
+ if (!verifyEvent(parsed)) return;
150
147
  return parsed;
151
148
  } catch {
152
149
  return;
@@ -162,7 +159,8 @@ function validateZapReceipt(receipt, ctx) {
162
159
  if (descriptionRaw === void 0 || !request) return fail("invalid zap request");
163
160
  if (countTags(request.tags, "p") !== 1) return fail("invalid p count");
164
161
  if (countTags(request.tags, "e") > 1) return fail("too many e tags");
165
- if (!firstTagValue(request.tags, "relays")) return fail("missing relays");
162
+ const relaysValue = firstTagValue(request.tags, "relays");
163
+ if (relaysValue === void 0 || relaysValue === "") return fail("missing relays");
166
164
  for (const tag of request.tags) {
167
165
  if (tag[0] !== "a") continue;
168
166
  if (tag[1] === void 0) return fail("invalid a");
@@ -184,11 +182,10 @@ function validateZapReceipt(receipt, ctx) {
184
182
  const msats = parseMsatsTag(requestAmount);
185
183
  if (msats === void 0 || bolt11.amountMsats !== msats) return fail("amount mismatch");
186
184
  }
187
- if (!bytesEq(sha256(utf8Encoder.encode(descriptionRaw)), bolt11.descriptionHash)) return fail("description hash mismatch");
185
+ const digest = sha256(utf8Encoder.encode(descriptionRaw));
186
+ if (!equalBytes(digest, bolt11.descriptionHash)) return fail("description hash mismatch");
188
187
  const requestLnurl = firstTagValue(request.tags, "lnurl");
189
- if (requestLnurl !== void 0 && ctx.lnurl !== void 0) {
190
- if (requestLnurl.toLowerCase() !== ctx.lnurl.toLowerCase()) return fail("lnurl mismatch");
191
- }
188
+ if (requestLnurl !== void 0 && ctx.lnurl !== void 0 && requestLnurl.toLowerCase() !== ctx.lnurl.toLowerCase()) return fail("lnurl mismatch");
192
189
  const preimageHex = firstTagValue(receipt.tags, "preimage");
193
190
  if (preimageHex !== void 0 && bolt11.paymentHash) {
194
191
  let preimage;
@@ -197,7 +194,7 @@ function validateZapReceipt(receipt, ctx) {
197
194
  } catch {
198
195
  return fail("preimage mismatch");
199
196
  }
200
- if (!bytesEq(sha256(preimage), bolt11.paymentHash)) return fail("preimage mismatch");
197
+ if (!equalBytes(sha256(preimage), bolt11.paymentHash)) return fail("preimage mismatch");
201
198
  }
202
199
  const recipient = firstTagValue(request.tags, "p");
203
200
  if (recipient === void 0 || !hasHexTagValue(receipt.tags, "p", recipient)) return fail("missing p");
@@ -211,7 +208,7 @@ function validateZapReceipt(receipt, ctx) {
211
208
  if (tag[0] !== "P") continue;
212
209
  if (tag[1] === void 0 || tag[1].toLowerCase() !== request.pubkey.toLowerCase()) return fail("receipt P mismatch");
213
210
  }
214
- const amountMsats = bolt11.amountMsats ?? (requestAmount !== void 0 ? parseMsatsTag(requestAmount) : void 0);
211
+ const amountMsats = bolt11.amountMsats ?? (requestAmount === void 0 ? void 0 : parseMsatsTag(requestAmount));
215
212
  return amountMsats === void 0 ? {
216
213
  valid: true,
217
214
  request