nostr-tools 2.23.12 → 2.24.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 (141) hide show
  1. package/lib/cjs/abstract-pool.js +11 -21
  2. package/lib/cjs/abstract-pool.js.map +3 -3
  3. package/lib/cjs/abstract-relay.js +1 -1
  4. package/lib/cjs/abstract-relay.js.map +2 -2
  5. package/lib/cjs/core.js +17 -1
  6. package/lib/cjs/core.js.map +3 -3
  7. package/lib/cjs/filter.js.map +1 -1
  8. package/lib/cjs/index.js +124 -123
  9. package/lib/cjs/index.js.map +4 -4
  10. package/lib/cjs/kinds.js +17 -1
  11. package/lib/cjs/kinds.js.map +3 -3
  12. package/lib/cjs/nip04.js.map +1 -1
  13. package/lib/cjs/nip10.js +20 -5
  14. package/lib/cjs/nip10.js.map +3 -3
  15. package/lib/cjs/nip13.js.map +1 -1
  16. package/lib/cjs/nip17.js +30 -23
  17. package/lib/cjs/nip17.js.map +4 -4
  18. package/lib/cjs/nip18.js +21 -14
  19. package/lib/cjs/nip18.js.map +4 -4
  20. package/lib/cjs/nip19.js.map +1 -1
  21. package/lib/cjs/nip21.js.map +1 -1
  22. package/lib/cjs/nip22.js +22 -7
  23. package/lib/cjs/nip22.js.map +3 -3
  24. package/lib/cjs/nip25.js +21 -14
  25. package/lib/cjs/nip25.js.map +4 -4
  26. package/lib/cjs/nip27.js.map +1 -1
  27. package/lib/cjs/nip28.js +21 -14
  28. package/lib/cjs/nip28.js.map +4 -4
  29. package/lib/cjs/nip29.js.map +1 -1
  30. package/lib/cjs/nip30.js.map +2 -2
  31. package/lib/cjs/nip42.js.map +2 -2
  32. package/lib/cjs/nip44.js.map +1 -1
  33. package/lib/cjs/nip45.js +22 -9
  34. package/lib/cjs/nip45.js.map +4 -4
  35. package/lib/cjs/nip46.js +63 -63
  36. package/lib/cjs/nip46.js.map +4 -4
  37. package/lib/cjs/nip47.js +25 -18
  38. package/lib/cjs/nip47.js.map +4 -4
  39. package/lib/cjs/nip57.js +23 -16
  40. package/lib/cjs/nip57.js.map +4 -4
  41. package/lib/cjs/nip58.js.map +2 -2
  42. package/lib/cjs/nip59.js +16 -7
  43. package/lib/cjs/nip59.js.map +3 -3
  44. package/lib/cjs/nip75.js.map +1 -1
  45. package/lib/cjs/nip94.js.map +1 -1
  46. package/lib/cjs/nip98.js +23 -16
  47. package/lib/cjs/nip98.js.map +4 -4
  48. package/lib/cjs/nip99.js.map +2 -2
  49. package/lib/cjs/nipb0.js +34 -16
  50. package/lib/cjs/nipb0.js.map +4 -4
  51. package/lib/cjs/nipb7.js +13 -7
  52. package/lib/cjs/nipb7.js.map +2 -2
  53. package/lib/cjs/pool.js +54 -54
  54. package/lib/cjs/pool.js.map +4 -4
  55. package/lib/cjs/pure.js +21 -14
  56. package/lib/cjs/pure.js.map +4 -4
  57. package/lib/cjs/references.js.map +1 -1
  58. package/lib/cjs/relay.js +43 -36
  59. package/lib/cjs/relay.js.map +4 -4
  60. package/lib/cjs/signer.js +21 -14
  61. package/lib/cjs/signer.js.map +4 -4
  62. package/lib/cjs/utils.js +10 -0
  63. package/lib/cjs/utils.js.map +2 -2
  64. package/lib/cjs/wasm.js +16 -2
  65. package/lib/cjs/wasm.js.map +4 -4
  66. package/lib/esm/abstract-pool.js +11 -21
  67. package/lib/esm/abstract-pool.js.map +3 -3
  68. package/lib/esm/abstract-relay.js +1 -1
  69. package/lib/esm/abstract-relay.js.map +2 -2
  70. package/lib/esm/core.js +15 -1
  71. package/lib/esm/core.js.map +3 -3
  72. package/lib/esm/filter.js.map +1 -1
  73. package/lib/esm/index.js +74 -73
  74. package/lib/esm/index.js.map +4 -4
  75. package/lib/esm/kinds.js +17 -1
  76. package/lib/esm/kinds.js.map +3 -3
  77. package/lib/esm/nip04.js.map +1 -1
  78. package/lib/esm/nip10.js +18 -5
  79. package/lib/esm/nip10.js.map +3 -3
  80. package/lib/esm/nip13.js.map +1 -1
  81. package/lib/esm/nip17.js +15 -8
  82. package/lib/esm/nip17.js.map +4 -4
  83. package/lib/esm/nip18.js +15 -8
  84. package/lib/esm/nip18.js.map +4 -4
  85. package/lib/esm/nip19.js.map +1 -1
  86. package/lib/esm/nip21.js.map +1 -1
  87. package/lib/esm/nip22.js +20 -7
  88. package/lib/esm/nip22.js.map +3 -3
  89. package/lib/esm/nip25.js +15 -8
  90. package/lib/esm/nip25.js.map +4 -4
  91. package/lib/esm/nip27.js.map +1 -1
  92. package/lib/esm/nip28.js +15 -8
  93. package/lib/esm/nip28.js.map +4 -4
  94. package/lib/esm/nip29.js.map +1 -1
  95. package/lib/esm/nip30.js.map +2 -2
  96. package/lib/esm/nip42.js.map +2 -2
  97. package/lib/esm/nip44.js.map +1 -1
  98. package/lib/esm/nip45.js +23 -10
  99. package/lib/esm/nip45.js.map +4 -4
  100. package/lib/esm/nip46.js +47 -47
  101. package/lib/esm/nip46.js.map +4 -4
  102. package/lib/esm/nip47.js +15 -8
  103. package/lib/esm/nip47.js.map +4 -4
  104. package/lib/esm/nip57.js +17 -10
  105. package/lib/esm/nip57.js.map +4 -4
  106. package/lib/esm/nip58.js.map +2 -2
  107. package/lib/esm/nip59.js +10 -1
  108. package/lib/esm/nip59.js.map +3 -3
  109. package/lib/esm/nip75.js.map +1 -1
  110. package/lib/esm/nip94.js.map +1 -1
  111. package/lib/esm/nip98.js +15 -8
  112. package/lib/esm/nip98.js.map +4 -4
  113. package/lib/esm/nip99.js.map +2 -2
  114. package/lib/esm/nipb0.js +35 -17
  115. package/lib/esm/nipb0.js.map +4 -4
  116. package/lib/esm/nipb7.js +13 -7
  117. package/lib/esm/nipb7.js.map +2 -2
  118. package/lib/esm/pool.js +47 -47
  119. package/lib/esm/pool.js.map +4 -4
  120. package/lib/esm/pure.js +15 -8
  121. package/lib/esm/pure.js.map +4 -4
  122. package/lib/esm/references.js.map +1 -1
  123. package/lib/esm/relay.js +37 -30
  124. package/lib/esm/relay.js.map +4 -4
  125. package/lib/esm/signer.js +15 -8
  126. package/lib/esm/signer.js.map +4 -4
  127. package/lib/esm/utils.js +10 -0
  128. package/lib/esm/utils.js.map +2 -2
  129. package/lib/esm/wasm.js +17 -3
  130. package/lib/esm/wasm.js.map +4 -4
  131. package/lib/nostr.bundle.js +73 -70
  132. package/lib/nostr.bundle.js.map +4 -4
  133. package/lib/types/abstract-pool.d.ts +6 -4
  134. package/lib/types/abstract-relay.d.ts +2 -2
  135. package/lib/types/kinds.d.ts +2 -0
  136. package/lib/types/nipb0.d.ts +0 -1
  137. package/lib/types/nipb0.test.d.ts +1 -0
  138. package/lib/types/nipb7.d.ts +0 -1
  139. package/lib/types/utils.d.ts +4 -0
  140. package/package.json +1 -1
  141. package/lib/types/helpers.d.ts +0 -2
package/lib/esm/index.js CHANGED
@@ -8,47 +8,6 @@ var __export = (target, all) => {
8
8
  import { schnorr } from "@noble/curves/secp256k1.js";
9
9
  import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
10
10
 
11
- // core.ts
12
- var verifiedSymbol = Symbol("verified");
13
- var isRecord = (obj) => obj instanceof Object;
14
- function validateEvent(event) {
15
- if (!isRecord(event))
16
- return false;
17
- if (typeof event.kind !== "number")
18
- return false;
19
- if (typeof event.content !== "string")
20
- return false;
21
- if (typeof event.created_at !== "number")
22
- return false;
23
- if (typeof event.pubkey !== "string")
24
- return false;
25
- if (!event.pubkey.match(/^[a-f0-9]{64}$/))
26
- return false;
27
- if (!Array.isArray(event.tags))
28
- return false;
29
- for (let i2 = 0; i2 < event.tags.length; i2++) {
30
- let tag = event.tags[i2];
31
- if (!Array.isArray(tag))
32
- return false;
33
- for (let j = 0; j < tag.length; j++) {
34
- if (typeof tag[j] !== "string")
35
- return false;
36
- }
37
- }
38
- return true;
39
- }
40
- function sortEvents(events) {
41
- return events.sort((a, b) => {
42
- if (a.created_at !== b.created_at) {
43
- return b.created_at - a.created_at;
44
- }
45
- return a.id.localeCompare(b.id);
46
- });
47
- }
48
-
49
- // pure.ts
50
- import { sha256 } from "@noble/hashes/sha2.js";
51
-
52
11
  // utils.ts
53
12
  var utils_exports = {};
54
13
  __export(utils_exports, {
@@ -57,6 +16,7 @@ __export(utils_exports, {
57
16
  hexToBytes: () => hexToBytes,
58
17
  insertEventIntoAscendingList: () => insertEventIntoAscendingList,
59
18
  insertEventIntoDescendingList: () => insertEventIntoDescendingList,
19
+ isHex32: () => isHex32,
60
20
  mergeReverseSortedLists: () => mergeReverseSortedLists,
61
21
  normalizeURL: () => normalizeURL,
62
22
  utf8Decoder: () => utf8Decoder,
@@ -179,8 +139,56 @@ function mergeReverseSortedLists(list1, list2) {
179
139
  }
180
140
  return result;
181
141
  }
142
+ function isHex32(input) {
143
+ for (let i2 = 0; i2 < 64; i2++) {
144
+ let cc = input.charCodeAt(i2);
145
+ if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
146
+ return false;
147
+ }
148
+ }
149
+ return true;
150
+ }
151
+
152
+ // core.ts
153
+ var verifiedSymbol = Symbol("verified");
154
+ var isRecord = (obj) => obj instanceof Object;
155
+ function validateEvent(event) {
156
+ if (!isRecord(event))
157
+ return false;
158
+ if (typeof event.kind !== "number")
159
+ return false;
160
+ if (typeof event.content !== "string")
161
+ return false;
162
+ if (typeof event.created_at !== "number")
163
+ return false;
164
+ if (typeof event.pubkey !== "string")
165
+ return false;
166
+ if (!isHex32(event.pubkey))
167
+ return false;
168
+ if (!Array.isArray(event.tags))
169
+ return false;
170
+ for (let i2 = 0; i2 < event.tags.length; i2++) {
171
+ let tag = event.tags[i2];
172
+ if (!Array.isArray(tag))
173
+ return false;
174
+ for (let j = 0; j < tag.length; j++) {
175
+ if (typeof tag[j] !== "string")
176
+ return false;
177
+ }
178
+ }
179
+ return true;
180
+ }
181
+ function sortEvents(events) {
182
+ return events.sort((a, b) => {
183
+ if (a.created_at !== b.created_at) {
184
+ return b.created_at - a.created_at;
185
+ }
186
+ return a.id.localeCompare(b.id);
187
+ });
188
+ }
182
189
 
183
190
  // pure.ts
191
+ import { sha256 } from "@noble/hashes/sha2.js";
184
192
  var JS = class {
185
193
  generateSecretKey() {
186
194
  return schnorr.utils.randomSecretKey();
@@ -283,6 +291,7 @@ __export(kinds_exports, {
283
291
  Emojisets: () => Emojisets,
284
292
  EncryptedDirectMessage: () => EncryptedDirectMessage,
285
293
  EventDeletion: () => EventDeletion,
294
+ FavoriteFollowSets: () => FavoriteFollowSets,
286
295
  FavoritePodcasts: () => FavoritePodcasts,
287
296
  FavoriteRelays: () => FavoriteRelays,
288
297
  FedimintAnnouncement: () => FedimintAnnouncement,
@@ -555,6 +564,7 @@ var PrivateEventRelayList = 10013;
555
564
  var InterestsList = 10015;
556
565
  var NutZapInfo = 10019;
557
566
  var MediaFollows = 10020;
567
+ var FavoriteFollowSets = 10021;
558
568
  var UserEmojiList = 10030;
559
569
  var DecoupledKeyAnnouncement = 10044;
560
570
  var DirectMessageRelaysList = 10050;
@@ -1154,7 +1164,7 @@ var AbstractRelay = class {
1154
1164
  case "EVENT": {
1155
1165
  const so = this.openSubs.get(data[1]);
1156
1166
  const event = data[2];
1157
- if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
1167
+ if (matchFilters(so.filters, event) && this.verifyEvent(event, this.url)) {
1158
1168
  so.onevent(event);
1159
1169
  } else {
1160
1170
  so.oninvalidevent?.(event);
@@ -1327,12 +1337,6 @@ var Relay = class extends AbstractRelay {
1327
1337
  }
1328
1338
  };
1329
1339
 
1330
- // helpers.ts
1331
- var alwaysTrue = (t) => {
1332
- t[verifiedSymbol] = true;
1333
- return true;
1334
- };
1335
-
1336
1340
  // nip45.ts
1337
1341
  import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
1338
1342
  import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils.js";
@@ -1359,7 +1363,7 @@ function newHll() {
1359
1363
  return new Uint8Array(M);
1360
1364
  }
1361
1365
  function hllDecode(hex) {
1362
- if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex))
1366
+ if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-f]+$/.test(hex))
1363
1367
  return void 0;
1364
1368
  const registers = new Uint8Array(M);
1365
1369
  for (let i2 = 0; i2 < M; i2++) {
@@ -1400,7 +1404,6 @@ var AbstractSimplePool = class {
1400
1404
  enableReconnect;
1401
1405
  idleTimeout = 2e4;
1402
1406
  automaticallyAuth;
1403
- trustedRelayURLs = /* @__PURE__ */ new Set();
1404
1407
  onRelayConnectionFailure;
1405
1408
  onRelayConnectionSuccess;
1406
1409
  allowConnectingToRelay;
@@ -1424,7 +1427,7 @@ var AbstractSimplePool = class {
1424
1427
  let relay = this.relays.get(url);
1425
1428
  if (!relay) {
1426
1429
  relay = new AbstractRelay(url, {
1427
- verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
1430
+ verifyEvent: this.verifyEvent,
1428
1431
  websocketImplementation: this._WebSocket,
1429
1432
  enablePing: this.enablePing,
1430
1433
  enableReconnect: this.enableReconnect,
@@ -1508,11 +1511,11 @@ var AbstractSimplePool = class {
1508
1511
  }
1509
1512
  };
1510
1513
  const closesReceived = [];
1511
- let handleClose = (i2, reason) => {
1514
+ let handleClose = (i2, url, reason) => {
1512
1515
  if (closesReceived[i2])
1513
1516
  return;
1514
1517
  handleEose(i2);
1515
- closesReceived[i2] = reason;
1518
+ closesReceived[i2] = { url, reason };
1516
1519
  if (closesReceived.filter((a) => a).length === groupedRequests.length) {
1517
1520
  params.onclose?.(closesReceived);
1518
1521
  handleClose = () => {
@@ -1530,7 +1533,7 @@ var AbstractSimplePool = class {
1530
1533
  const allOpened = Promise.all(
1531
1534
  groupedRequests.map(async ({ url, filters }, i2) => {
1532
1535
  if (this.allowConnectingToRelay?.(url, ["read", filters]) === false) {
1533
- handleClose(i2, "connection skipped by allowConnectingToRelay");
1536
+ handleClose(i2, url, "connection skipped by allowConnectingToRelay");
1534
1537
  return;
1535
1538
  }
1536
1539
  let relay;
@@ -1541,7 +1544,7 @@ var AbstractSimplePool = class {
1541
1544
  });
1542
1545
  } catch (err) {
1543
1546
  this.onRelayConnectionFailure?.(url);
1544
- handleClose(i2, err?.message || String(err));
1547
+ handleClose(i2, url, err?.message || String(err));
1545
1548
  return;
1546
1549
  }
1547
1550
  this.onRelayConnectionSuccess?.(url);
@@ -1555,17 +1558,17 @@ var AbstractSimplePool = class {
1555
1558
  ...params,
1556
1559
  oneose: () => handleEose(i2),
1557
1560
  onclose: (reason2) => {
1558
- handleClose(i2, reason2);
1561
+ handleClose(i2, url, reason2);
1559
1562
  },
1560
1563
  alreadyHaveEvent: localAlreadyHaveEventHandler,
1561
1564
  eoseTimeout: params.maxWait,
1562
1565
  abort: params.abort
1563
1566
  });
1564
1567
  }).catch((err) => {
1565
- handleClose(i2, `auth was required and attempted, but failed with: ${err}`);
1568
+ handleClose(i2, url, `auth was required and attempted, but failed with: ${err}`);
1566
1569
  });
1567
1570
  } else {
1568
- handleClose(i2, reason);
1571
+ handleClose(i2, url, reason);
1569
1572
  }
1570
1573
  },
1571
1574
  alreadyHaveEvent: localAlreadyHaveEventHandler,
@@ -1593,7 +1596,7 @@ var AbstractSimplePool = class {
1593
1596
  if (subcloser)
1594
1597
  subcloser.close(reason);
1595
1598
  else
1596
- params.onclose?.(relays.map((_) => reason));
1599
+ params.onclose?.(relays.map((url) => ({ url, reason })));
1597
1600
  }
1598
1601
  });
1599
1602
  return subcloser;
@@ -2113,7 +2116,6 @@ var nip10_exports = {};
2113
2116
  __export(nip10_exports, {
2114
2117
  parse: () => parse
2115
2118
  });
2116
- var HEX64 = /^[0-9a-fA-F]{64}$/;
2117
2119
  function parse(event) {
2118
2120
  const result = {
2119
2121
  reply: void 0,
@@ -2126,12 +2128,12 @@ function parse(event) {
2126
2128
  let maybeRoot;
2127
2129
  for (let i2 = event.tags.length - 1; i2 >= 0; i2--) {
2128
2130
  const tag = event.tags[i2];
2129
- if (tag[0] === "e" && tag[1] && HEX64.test(tag[1])) {
2131
+ if (tag[0] === "e" && tag[1] && isHex32(tag[1])) {
2130
2132
  const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag;
2131
2133
  const eventPointer = {
2132
2134
  id: eTagEventId,
2133
2135
  relays: eTagRelayUrl ? [eTagRelayUrl] : [],
2134
- author: eTagAuthor && HEX64.test(eTagAuthor) ? eTagAuthor : void 0
2136
+ author: eTagAuthor && isHex32(eTagAuthor) ? eTagAuthor : void 0
2135
2137
  };
2136
2138
  if (eTagMarker === "root") {
2137
2139
  result.root = eventPointer;
@@ -2153,14 +2155,14 @@ function parse(event) {
2153
2155
  result.mentions.push(eventPointer);
2154
2156
  continue;
2155
2157
  }
2156
- if (tag[0] === "q" && tag[1] && HEX64.test(tag[1])) {
2158
+ if (tag[0] === "q" && tag[1] && isHex32(tag[1])) {
2157
2159
  const [_, eTagEventId, eTagRelayUrl] = tag;
2158
2160
  result.quotes.push({
2159
2161
  id: eTagEventId,
2160
2162
  relays: eTagRelayUrl ? [eTagRelayUrl] : []
2161
2163
  });
2162
2164
  }
2163
- if (tag[0] === "p" && tag[1] && HEX64.test(tag[1])) {
2165
+ if (tag[0] === "p" && tag[1] && isHex32(tag[1])) {
2164
2166
  result.profiles.push({
2165
2167
  pubkey: tag[1],
2166
2168
  relays: tag[2] ? [tag[2]] : []
@@ -2657,7 +2659,6 @@ var nip22_exports = {};
2657
2659
  __export(nip22_exports, {
2658
2660
  parse: () => parse3
2659
2661
  });
2660
- var HEX642 = /^[0-9a-fA-F]{64}$/;
2661
2662
  function parseKind(kind) {
2662
2663
  if (!kind)
2663
2664
  return void 0;
@@ -2672,7 +2673,7 @@ function parseAddressPointer(value, relayUrl) {
2672
2673
  if (Number.isNaN(kind))
2673
2674
  return void 0;
2674
2675
  const pubkey = value.slice(idx + 1, idx2);
2675
- if (!HEX642.test(pubkey))
2676
+ if (!isHex32(pubkey))
2676
2677
  return void 0;
2677
2678
  return {
2678
2679
  kind,
@@ -2685,12 +2686,12 @@ function parsePointer(tag) {
2685
2686
  switch (tag[0]) {
2686
2687
  case "E":
2687
2688
  case "e":
2688
- if (!tag[1] || !HEX642.test(tag[1]))
2689
+ if (!tag[1] || !isHex32(tag[1]))
2689
2690
  return void 0;
2690
2691
  return {
2691
2692
  id: tag[1],
2692
2693
  relays: tag[2] ? [tag[2]] : [],
2693
- author: tag[3] && HEX642.test(tag[3]) ? tag[3] : void 0
2694
+ author: tag[3] && isHex32(tag[3]) ? tag[3] : void 0
2694
2695
  };
2695
2696
  case "A":
2696
2697
  case "a":
@@ -2713,12 +2714,12 @@ function parseQuote(tag) {
2713
2714
  if (tag[1].includes(":")) {
2714
2715
  return parseAddressPointer(tag[1], tag[2]);
2715
2716
  }
2716
- if (!HEX642.test(tag[1]))
2717
+ if (!isHex32(tag[1]))
2717
2718
  return void 0;
2718
2719
  return {
2719
2720
  id: tag[1],
2720
2721
  relays: tag[2] ? [tag[2]] : [],
2721
- author: tag[3] && HEX642.test(tag[3]) ? tag[3] : void 0
2722
+ author: tag[3] && isHex32(tag[3]) ? tag[3] : void 0
2722
2723
  };
2723
2724
  }
2724
2725
  function choosePointer(candidates) {
@@ -2778,7 +2779,7 @@ function parse3(event) {
2778
2779
  result.quotes.push(pointer);
2779
2780
  continue;
2780
2781
  }
2781
- if ((tag[0] === "P" || tag[0] === "p") && tag[1] && HEX642.test(tag[1])) {
2782
+ if ((tag[0] === "P" || tag[0] === "p") && tag[1] && isHex32(tag[1])) {
2782
2783
  result.profiles.push({
2783
2784
  pubkey: tag[1],
2784
2785
  relays: tag[2] ? [tag[2]] : []
@@ -3279,10 +3280,10 @@ function validateZapRequest(zapRequestString) {
3279
3280
  let p = zapRequest.tags.find(([t, v]) => t === "p" && v);
3280
3281
  if (!p)
3281
3282
  return "Zap request doesn't have a 'p' tag.";
3282
- if (!p[1].match(/^[a-f0-9]{64}$/))
3283
+ if (!isHex32(p[1]))
3283
3284
  return "Zap request 'p' tag is not valid hex.";
3284
3285
  let e = zapRequest.tags.find(([t, v]) => t === "e" && v);
3285
- if (e && !e[1].match(/^[a-f0-9]{64}$/))
3286
+ if (e && !isHex32(e[1]))
3286
3287
  return "Zap request 'e' tag is not valid hex.";
3287
3288
  let relays = zapRequest.tags.find(([t, v]) => t === "relays" && v);
3288
3289
  if (!relays)