nostr-tools 2.23.11 → 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 +38 -17
  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 +39 -18
  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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts", "../../signer.ts"],
4
- "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n try {\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n", "import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n getPublicKey(): Promise<string>\n signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n private secretKey: Uint8Array\n\n constructor(secretKey: Uint8Array) {\n this.secretKey = secretKey\n }\n\n async getPublicKey(): Promise<string> {\n return getPublicKey(this.secretKey)\n }\n\n async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n return finalizeEvent(event, this.secretKey)\n }\n}\n"],
5
- "mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,cAAAA,aAAY,cAAAC,mBAAkB;;;ACOhC,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,SAAS,cAAc;;;AEEvB,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,QAAQ,MAAM,gBAAgB;AAAA,EACvC;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAOC,YAAW,QAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,SAASA,YAAW,QAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,MAAMA,YAAW,QAAQ,KAAKC,YAAW,aAAa,KAAK,CAAC,GAAG,SAAS,CAAC;AAC/E,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,QAAI;AACF,YAAM,OAAO,aAAa,KAAK;AAC/B,UAAI,SAAS,MAAM,IAAI;AACrB,cAAM,kBAAkB;AACxB,eAAO;AAAA,MACT;AAEA,YAAM,QAAQ,QAAQ,OAAOA,YAAW,MAAM,GAAG,GAAGA,YAAW,IAAI,GAAGA,YAAW,MAAM,MAAM,CAAC;AAC9F,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,YAAY,OAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,SAAOD,YAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;AGjDtB,IAAM,iBAAN,MAAuC;AAAA,EACpC;AAAA,EAER,YAAY,WAAuB;AACjC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,eAAgC;AACpC,WAAO,aAAa,KAAK,SAAS;AAAA,EACpC;AAAA,EAEA,MAAM,UAAU,OAA8C;AAC5D,WAAO,cAAc,OAAO,KAAK,SAAS;AAAA,EAC5C;AACF;",
6
- "names": ["bytesToHex", "hexToBytes", "i", "bytesToHex", "hexToBytes"]
3
+ "sources": ["../../pure.ts", "../../utils.ts", "../../core.ts", "../../signer.ts"],
4
+ "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n try {\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n\n/**\n * Checks if a string is a 64-char lowercase hex string as most Nostr ids and pubkeys.\n */\nexport function isHex32(input: string): boolean {\n for (let i = 0; i < 64; i++) {\n let cc = input.charCodeAt(i)\n if (isNaN(cc) || cc < 48 || cc > 102 || (cc > 57 && cc < 97)) {\n return false\n }\n }\n return true\n}\n", "import { isHex32 } from './utils.ts'\n\nexport interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!isHex32(event.pubkey)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n getPublicKey(): Promise<string>\n signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n private secretKey: Uint8Array\n\n constructor(secretKey: Uint8Array) {\n this.secretKey = secretKey\n }\n\n async getPublicKey(): Promise<string> {\n return getPublicKey(this.secretKey)\n }\n\n async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n return finalizeEvent(event, this.secretKey)\n }\n}\n"],
5
+ "mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,cAAAA,aAAY,cAAAC,mBAAkB;;;ACIvC,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;AA8HjD,SAAS,QAAQ,OAAwB;AAC9C,WAASC,KAAI,GAAGA,KAAI,IAAIA,MAAK;AAC3B,QAAI,KAAK,MAAM,WAAWA,EAAC;AAC3B,QAAI,MAAM,EAAE,KAAK,KAAK,MAAM,KAAK,OAAQ,KAAK,MAAM,KAAK,IAAK;AAC5D,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC/HO,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,QAAQ,MAAM,MAAM;AAAG,WAAO;AAEnC,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AFjDA,SAAS,cAAc;AAIvB,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,QAAQ,MAAM,gBAAgB;AAAA,EACvC;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAOC,YAAW,QAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,SAASA,YAAW,QAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,MAAMA,YAAW,QAAQ,KAAKC,YAAW,aAAa,KAAK,CAAC,GAAG,SAAS,CAAC;AAC/E,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,QAAI;AACF,YAAM,OAAO,aAAa,KAAK;AAC/B,UAAI,SAAS,MAAM,IAAI;AACrB,cAAM,kBAAkB;AACxB,eAAO;AAAA,MACT;AAEA,YAAM,QAAQ,QAAQ,OAAOA,YAAW,MAAM,GAAG,GAAGA,YAAW,IAAI,GAAGA,YAAW,MAAM,MAAM,CAAC;AAC9F,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,YAAY,OAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,SAAOD,YAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;AGjDtB,IAAM,iBAAN,MAAuC;AAAA,EACpC;AAAA,EAER,YAAY,WAAuB;AACjC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,eAAgC;AACpC,WAAO,aAAa,KAAK,SAAS;AAAA,EACpC;AAAA,EAEA,MAAM,UAAU,OAA8C;AAC5D,WAAO,cAAc,OAAO,KAAK,SAAS;AAAA,EAC5C;AACF;",
6
+ "names": ["bytesToHex", "hexToBytes", "i", "i", "bytesToHex", "hexToBytes"]
7
7
  }
package/lib/esm/utils.js CHANGED
@@ -116,12 +116,22 @@ function mergeReverseSortedLists(list1, list2) {
116
116
  }
117
117
  return result;
118
118
  }
119
+ function isHex32(input) {
120
+ for (let i = 0; i < 64; i++) {
121
+ let cc = input.charCodeAt(i);
122
+ if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
123
+ return false;
124
+ }
125
+ }
126
+ return true;
127
+ }
119
128
  export {
120
129
  binarySearch,
121
130
  bytesToHex,
122
131
  hexToBytes,
123
132
  insertEventIntoAscendingList,
124
133
  insertEventIntoDescendingList,
134
+ isHex32,
125
135
  mergeReverseSortedLists,
126
136
  normalizeURL,
127
137
  utf8Decoder,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../utils.ts"],
4
- "sourcesContent": ["import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n\n/**\n * Checks if a string is a 64-char lowercase hex string as most Nostr ids and pubkeys.\n */\nexport function isHex32(input: string): boolean {\n for (let i = 0; i < 64; i++) {\n let cc = input.charCodeAt(i)\n if (isNaN(cc) || cc < 48 || cc > 102 || (cc > 57 && cc < 97)) {\n return false\n }\n }\n return true\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/lib/esm/wasm.js CHANGED
@@ -1,5 +1,19 @@
1
1
  // wasm.ts
2
- import { bytesToHex } from "@noble/hashes/utils.js";
2
+ import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
3
+
4
+ // utils.ts
5
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
6
+ var utf8Decoder = new TextDecoder("utf-8");
7
+ var utf8Encoder = new TextEncoder();
8
+ function isHex32(input) {
9
+ for (let i2 = 0; i2 < 64; i2++) {
10
+ let cc = input.charCodeAt(i2);
11
+ if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
12
+ return false;
13
+ }
14
+ }
15
+ return true;
16
+ }
3
17
 
4
18
  // core.ts
5
19
  var verifiedSymbol = Symbol("verified");
@@ -15,7 +29,7 @@ function validateEvent(event) {
15
29
  return false;
16
30
  if (typeof event.pubkey !== "string")
17
31
  return false;
18
- if (!event.pubkey.match(/^[a-f0-9]{64}$/))
32
+ if (!isHex32(event.pubkey))
19
33
  return false;
20
34
  if (!Array.isArray(event.tags))
21
35
  return false;
@@ -49,7 +63,7 @@ var Wasm = class {
49
63
  return nw.generateSecretKey();
50
64
  }
51
65
  getPublicKey(secretKey) {
52
- return bytesToHex(nw.getPublicKey(secretKey));
66
+ return bytesToHex2(nw.getPublicKey(secretKey));
53
67
  }
54
68
  finalizeEvent(t, secretKey) {
55
69
  nw.finalizeEvent(t, secretKey);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../wasm.ts", "../../core.ts"],
4
- "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { Nostr as NostrWasm } from 'nostr-wasm'\nimport { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'\n\nlet nw: NostrWasm\n\nexport function setNostrWasm(x: NostrWasm) {\n nw = x\n}\n\nclass Wasm implements Nostr {\n generateSecretKey(): Uint8Array {\n return nw.generateSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(nw.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n nw.finalizeEvent(t as any, secretKey)\n return t as VerifiedEvent\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n try {\n nw.verifyEvent(event)\n event[verifiedSymbol] = true\n return true\n } catch (err) {\n return false\n }\n }\n}\n\nconst i: Wasm = new Wasm()\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n"],
5
- "mappings": ";AAAA,SAAS,kBAAkB;;;ACQpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASA,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,WAAW,QAA0B;AACnD,SAAO,OAAO,KAAK,CAAC,GAAe,MAA0B;AAC3D,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAChC,CAAC;AACH;;;AD5DA,IAAI;AAEG,SAAS,aAAa,GAAc;AACzC,OAAK;AACP;AAEA,IAAM,OAAN,MAA4B;AAAA,EAC1B,oBAAgC;AAC9B,WAAO,GAAG,kBAAkB;AAAA,EAC9B;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAO,WAAW,GAAG,aAAa,SAAS,CAAC;AAAA,EAC9C;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,OAAG,cAAc,GAAU,SAAS;AACpC,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI;AACF,SAAG,YAAY,KAAK;AACpB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,IAAU,IAAI,KAAK;AAClB,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;",
6
- "names": ["i"]
3
+ "sources": ["../../wasm.ts", "../../utils.ts", "../../core.ts"],
4
+ "sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils.js'\nimport { Nostr as NostrWasm } from 'nostr-wasm'\nimport { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'\n\nlet nw: NostrWasm\n\nexport function setNostrWasm(x: NostrWasm) {\n nw = x\n}\n\nclass Wasm implements Nostr {\n generateSecretKey(): Uint8Array {\n return nw.generateSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(nw.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n nw.finalizeEvent(t as any, secretKey)\n return t as VerifiedEvent\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n try {\n nw.verifyEvent(event)\n event[verifiedSymbol] = true\n return true\n } catch (err) {\n return false\n }\n }\n}\n\nconst i: Wasm = new Wasm()\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n\n/**\n * Checks if a string is a 64-char lowercase hex string as most Nostr ids and pubkeys.\n */\nexport function isHex32(input: string): boolean {\n for (let i = 0; i < 64; i++) {\n let cc = input.charCodeAt(i)\n if (isNaN(cc) || cc < 48 || cc > 102 || (cc > 57 && cc < 97)) {\n return false\n }\n }\n return true\n}\n", "import { isHex32 } from './utils.ts'\n\nexport interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!isHex32(event.pubkey)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n"],
5
+ "mappings": ";AAAA,SAAS,cAAAA,mBAAkB;;;ACK3B,SAAS,YAAY,kBAAkB;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;AA8HjD,SAAS,QAAQ,OAAwB;AAC9C,WAASC,KAAI,GAAGA,KAAI,IAAIA,MAAK;AAC3B,QAAI,KAAK,MAAM,WAAWA,EAAC;AAC3B,QAAI,MAAM,EAAE,KAAK,KAAK,MAAM,KAAK,OAAQ,KAAK,MAAM,KAAK,IAAK;AAC5D,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC/HO,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,QAAQ,MAAM,MAAM;AAAG,WAAO;AAEnC,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,WAAW,QAA0B;AACnD,SAAO,OAAO,KAAK,CAAC,GAAe,MAA0B;AAC3D,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAChC,CAAC;AACH;;;AF9DA,IAAI;AAEG,SAAS,aAAa,GAAc;AACzC,OAAK;AACP;AAEA,IAAM,OAAN,MAA4B;AAAA,EAC1B,oBAAgC;AAC9B,WAAO,GAAG,kBAAkB;AAAA,EAC9B;AAAA,EACA,aAAa,WAA+B;AAC1C,WAAOC,YAAW,GAAG,aAAa,SAAS,CAAC;AAAA,EAC9C;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,OAAG,cAAc,GAAU,SAAS;AACpC,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI;AACF,SAAG,YAAY,KAAK;AACpB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,IAAU,IAAI,KAAK;AAClB,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;",
6
+ "names": ["bytesToHex", "i", "i", "bytesToHex"]
7
7
  }
@@ -2228,44 +2228,6 @@ var NostrTools = (() => {
2228
2228
  };
2229
2229
  })();
2230
2230
 
2231
- // core.ts
2232
- var verifiedSymbol = Symbol("verified");
2233
- var isRecord = (obj) => obj instanceof Object;
2234
- function validateEvent(event) {
2235
- if (!isRecord(event))
2236
- return false;
2237
- if (typeof event.kind !== "number")
2238
- return false;
2239
- if (typeof event.content !== "string")
2240
- return false;
2241
- if (typeof event.created_at !== "number")
2242
- return false;
2243
- if (typeof event.pubkey !== "string")
2244
- return false;
2245
- if (!event.pubkey.match(/^[a-f0-9]{64}$/))
2246
- return false;
2247
- if (!Array.isArray(event.tags))
2248
- return false;
2249
- for (let i2 = 0; i2 < event.tags.length; i2++) {
2250
- let tag = event.tags[i2];
2251
- if (!Array.isArray(tag))
2252
- return false;
2253
- for (let j = 0; j < tag.length; j++) {
2254
- if (typeof tag[j] !== "string")
2255
- return false;
2256
- }
2257
- }
2258
- return true;
2259
- }
2260
- function sortEvents(events) {
2261
- return events.sort((a, b) => {
2262
- if (a.created_at !== b.created_at) {
2263
- return b.created_at - a.created_at;
2264
- }
2265
- return a.id.localeCompare(b.id);
2266
- });
2267
- }
2268
-
2269
2231
  // utils.ts
2270
2232
  var utils_exports = {};
2271
2233
  __export(utils_exports, {
@@ -2274,6 +2236,7 @@ var NostrTools = (() => {
2274
2236
  hexToBytes: () => hexToBytes,
2275
2237
  insertEventIntoAscendingList: () => insertEventIntoAscendingList,
2276
2238
  insertEventIntoDescendingList: () => insertEventIntoDescendingList,
2239
+ isHex32: () => isHex32,
2277
2240
  mergeReverseSortedLists: () => mergeReverseSortedLists,
2278
2241
  normalizeURL: () => normalizeURL,
2279
2242
  utf8Decoder: () => utf8Decoder,
@@ -2395,6 +2358,53 @@ var NostrTools = (() => {
2395
2358
  }
2396
2359
  return result;
2397
2360
  }
2361
+ function isHex32(input) {
2362
+ for (let i2 = 0; i2 < 64; i2++) {
2363
+ let cc = input.charCodeAt(i2);
2364
+ if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
2365
+ return false;
2366
+ }
2367
+ }
2368
+ return true;
2369
+ }
2370
+
2371
+ // core.ts
2372
+ var verifiedSymbol = Symbol("verified");
2373
+ var isRecord = (obj) => obj instanceof Object;
2374
+ function validateEvent(event) {
2375
+ if (!isRecord(event))
2376
+ return false;
2377
+ if (typeof event.kind !== "number")
2378
+ return false;
2379
+ if (typeof event.content !== "string")
2380
+ return false;
2381
+ if (typeof event.created_at !== "number")
2382
+ return false;
2383
+ if (typeof event.pubkey !== "string")
2384
+ return false;
2385
+ if (!isHex32(event.pubkey))
2386
+ return false;
2387
+ if (!Array.isArray(event.tags))
2388
+ return false;
2389
+ for (let i2 = 0; i2 < event.tags.length; i2++) {
2390
+ let tag = event.tags[i2];
2391
+ if (!Array.isArray(tag))
2392
+ return false;
2393
+ for (let j = 0; j < tag.length; j++) {
2394
+ if (typeof tag[j] !== "string")
2395
+ return false;
2396
+ }
2397
+ }
2398
+ return true;
2399
+ }
2400
+ function sortEvents(events) {
2401
+ return events.sort((a, b) => {
2402
+ if (a.created_at !== b.created_at) {
2403
+ return b.created_at - a.created_at;
2404
+ }
2405
+ return a.id.localeCompare(b.id);
2406
+ });
2407
+ }
2398
2408
 
2399
2409
  // pure.ts
2400
2410
  var JS = class {
@@ -2499,6 +2509,7 @@ var NostrTools = (() => {
2499
2509
  Emojisets: () => Emojisets,
2500
2510
  EncryptedDirectMessage: () => EncryptedDirectMessage,
2501
2511
  EventDeletion: () => EventDeletion,
2512
+ FavoriteFollowSets: () => FavoriteFollowSets,
2502
2513
  FavoritePodcasts: () => FavoritePodcasts,
2503
2514
  FavoriteRelays: () => FavoriteRelays,
2504
2515
  FedimintAnnouncement: () => FedimintAnnouncement,
@@ -2771,6 +2782,7 @@ var NostrTools = (() => {
2771
2782
  var InterestsList = 10015;
2772
2783
  var NutZapInfo = 10019;
2773
2784
  var MediaFollows = 10020;
2785
+ var FavoriteFollowSets = 10021;
2774
2786
  var UserEmojiList = 10030;
2775
2787
  var DecoupledKeyAnnouncement = 10044;
2776
2788
  var DirectMessageRelaysList = 10050;
@@ -3370,7 +3382,7 @@ var NostrTools = (() => {
3370
3382
  case "EVENT": {
3371
3383
  const so = this.openSubs.get(data[1]);
3372
3384
  const event = data[2];
3373
- if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
3385
+ if (matchFilters(so.filters, event) && this.verifyEvent(event, this.url)) {
3374
3386
  so.onevent(event);
3375
3387
  } else {
3376
3388
  so.oninvalidevent?.(event);
@@ -3543,12 +3555,6 @@ var NostrTools = (() => {
3543
3555
  }
3544
3556
  };
3545
3557
 
3546
- // helpers.ts
3547
- var alwaysTrue = (t) => {
3548
- t[verifiedSymbol] = true;
3549
- return true;
3550
- };
3551
-
3552
3558
  // nip45.ts
3553
3559
  var M = 256;
3554
3560
  var HLL_HEX_LENGTH = M * 2;
@@ -3573,7 +3579,7 @@ var NostrTools = (() => {
3573
3579
  return new Uint8Array(M);
3574
3580
  }
3575
3581
  function hllDecode(hex2) {
3576
- if (hex2.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex2))
3582
+ if (hex2.length !== HLL_HEX_LENGTH || !/^[0-9a-f]+$/.test(hex2))
3577
3583
  return void 0;
3578
3584
  const registers = new Uint8Array(M);
3579
3585
  for (let i2 = 0; i2 < M; i2++) {
@@ -3614,7 +3620,6 @@ var NostrTools = (() => {
3614
3620
  enableReconnect;
3615
3621
  idleTimeout = 2e4;
3616
3622
  automaticallyAuth;
3617
- trustedRelayURLs = /* @__PURE__ */ new Set();
3618
3623
  onRelayConnectionFailure;
3619
3624
  onRelayConnectionSuccess;
3620
3625
  allowConnectingToRelay;
@@ -3638,7 +3643,7 @@ var NostrTools = (() => {
3638
3643
  let relay = this.relays.get(url);
3639
3644
  if (!relay) {
3640
3645
  relay = new AbstractRelay(url, {
3641
- verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
3646
+ verifyEvent: this.verifyEvent,
3642
3647
  websocketImplementation: this._WebSocket,
3643
3648
  enablePing: this.enablePing,
3644
3649
  enableReconnect: this.enableReconnect,
@@ -3722,11 +3727,11 @@ var NostrTools = (() => {
3722
3727
  }
3723
3728
  };
3724
3729
  const closesReceived = [];
3725
- let handleClose = (i2, reason) => {
3730
+ let handleClose = (i2, url, reason) => {
3726
3731
  if (closesReceived[i2])
3727
3732
  return;
3728
3733
  handleEose(i2);
3729
- closesReceived[i2] = reason;
3734
+ closesReceived[i2] = { url, reason };
3730
3735
  if (closesReceived.filter((a) => a).length === groupedRequests.length) {
3731
3736
  params.onclose?.(closesReceived);
3732
3737
  handleClose = () => {
@@ -3744,7 +3749,7 @@ var NostrTools = (() => {
3744
3749
  const allOpened = Promise.all(
3745
3750
  groupedRequests.map(async ({ url, filters }, i2) => {
3746
3751
  if (this.allowConnectingToRelay?.(url, ["read", filters]) === false) {
3747
- handleClose(i2, "connection skipped by allowConnectingToRelay");
3752
+ handleClose(i2, url, "connection skipped by allowConnectingToRelay");
3748
3753
  return;
3749
3754
  }
3750
3755
  let relay;
@@ -3755,7 +3760,7 @@ var NostrTools = (() => {
3755
3760
  });
3756
3761
  } catch (err) {
3757
3762
  this.onRelayConnectionFailure?.(url);
3758
- handleClose(i2, err?.message || String(err));
3763
+ handleClose(i2, url, err?.message || String(err));
3759
3764
  return;
3760
3765
  }
3761
3766
  this.onRelayConnectionSuccess?.(url);
@@ -3769,17 +3774,17 @@ var NostrTools = (() => {
3769
3774
  ...params,
3770
3775
  oneose: () => handleEose(i2),
3771
3776
  onclose: (reason2) => {
3772
- handleClose(i2, reason2);
3777
+ handleClose(i2, url, reason2);
3773
3778
  },
3774
3779
  alreadyHaveEvent: localAlreadyHaveEventHandler,
3775
3780
  eoseTimeout: params.maxWait,
3776
3781
  abort: params.abort
3777
3782
  });
3778
3783
  }).catch((err) => {
3779
- handleClose(i2, `auth was required and attempted, but failed with: ${err}`);
3784
+ handleClose(i2, url, `auth was required and attempted, but failed with: ${err}`);
3780
3785
  });
3781
3786
  } else {
3782
- handleClose(i2, reason);
3787
+ handleClose(i2, url, reason);
3783
3788
  }
3784
3789
  },
3785
3790
  alreadyHaveEvent: localAlreadyHaveEventHandler,
@@ -3807,7 +3812,7 @@ var NostrTools = (() => {
3807
3812
  if (subcloser)
3808
3813
  subcloser.close(reason);
3809
3814
  else
3810
- params.onclose?.(relays.map((_) => reason));
3815
+ params.onclose?.(relays.map((url) => ({ url, reason })));
3811
3816
  }
3812
3817
  });
3813
3818
  return subcloser;
@@ -5223,7 +5228,6 @@ var NostrTools = (() => {
5223
5228
  __export(nip10_exports, {
5224
5229
  parse: () => parse
5225
5230
  });
5226
- var HEX64 = /^[0-9a-fA-F]{64}$/;
5227
5231
  function parse(event) {
5228
5232
  const result = {
5229
5233
  reply: void 0,
@@ -5236,12 +5240,12 @@ var NostrTools = (() => {
5236
5240
  let maybeRoot;
5237
5241
  for (let i2 = event.tags.length - 1; i2 >= 0; i2--) {
5238
5242
  const tag = event.tags[i2];
5239
- if (tag[0] === "e" && tag[1] && HEX64.test(tag[1])) {
5243
+ if (tag[0] === "e" && tag[1] && isHex32(tag[1])) {
5240
5244
  const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag;
5241
5245
  const eventPointer = {
5242
5246
  id: eTagEventId,
5243
5247
  relays: eTagRelayUrl ? [eTagRelayUrl] : [],
5244
- author: eTagAuthor && HEX64.test(eTagAuthor) ? eTagAuthor : void 0
5248
+ author: eTagAuthor && isHex32(eTagAuthor) ? eTagAuthor : void 0
5245
5249
  };
5246
5250
  if (eTagMarker === "root") {
5247
5251
  result.root = eventPointer;
@@ -5263,14 +5267,14 @@ var NostrTools = (() => {
5263
5267
  result.mentions.push(eventPointer);
5264
5268
  continue;
5265
5269
  }
5266
- if (tag[0] === "q" && tag[1] && HEX64.test(tag[1])) {
5270
+ if (tag[0] === "q" && tag[1] && isHex32(tag[1])) {
5267
5271
  const [_, eTagEventId, eTagRelayUrl] = tag;
5268
5272
  result.quotes.push({
5269
5273
  id: eTagEventId,
5270
5274
  relays: eTagRelayUrl ? [eTagRelayUrl] : []
5271
5275
  });
5272
5276
  }
5273
- if (tag[0] === "p" && tag[1] && HEX64.test(tag[1])) {
5277
+ if (tag[0] === "p" && tag[1] && isHex32(tag[1])) {
5274
5278
  result.profiles.push({
5275
5279
  pubkey: tag[1],
5276
5280
  relays: tag[2] ? [tag[2]] : []
@@ -6390,7 +6394,6 @@ var NostrTools = (() => {
6390
6394
  __export(nip22_exports, {
6391
6395
  parse: () => parse3
6392
6396
  });
6393
- var HEX642 = /^[0-9a-fA-F]{64}$/;
6394
6397
  function parseKind(kind) {
6395
6398
  if (!kind)
6396
6399
  return void 0;
@@ -6405,7 +6408,7 @@ var NostrTools = (() => {
6405
6408
  if (Number.isNaN(kind))
6406
6409
  return void 0;
6407
6410
  const pubkey = value.slice(idx + 1, idx2);
6408
- if (!HEX642.test(pubkey))
6411
+ if (!isHex32(pubkey))
6409
6412
  return void 0;
6410
6413
  return {
6411
6414
  kind,
@@ -6418,12 +6421,12 @@ var NostrTools = (() => {
6418
6421
  switch (tag[0]) {
6419
6422
  case "E":
6420
6423
  case "e":
6421
- if (!tag[1] || !HEX642.test(tag[1]))
6424
+ if (!tag[1] || !isHex32(tag[1]))
6422
6425
  return void 0;
6423
6426
  return {
6424
6427
  id: tag[1],
6425
6428
  relays: tag[2] ? [tag[2]] : [],
6426
- author: tag[3] && HEX642.test(tag[3]) ? tag[3] : void 0
6429
+ author: tag[3] && isHex32(tag[3]) ? tag[3] : void 0
6427
6430
  };
6428
6431
  case "A":
6429
6432
  case "a":
@@ -6446,12 +6449,12 @@ var NostrTools = (() => {
6446
6449
  if (tag[1].includes(":")) {
6447
6450
  return parseAddressPointer(tag[1], tag[2]);
6448
6451
  }
6449
- if (!HEX642.test(tag[1]))
6452
+ if (!isHex32(tag[1]))
6450
6453
  return void 0;
6451
6454
  return {
6452
6455
  id: tag[1],
6453
6456
  relays: tag[2] ? [tag[2]] : [],
6454
- author: tag[3] && HEX642.test(tag[3]) ? tag[3] : void 0
6457
+ author: tag[3] && isHex32(tag[3]) ? tag[3] : void 0
6455
6458
  };
6456
6459
  }
6457
6460
  function choosePointer(candidates) {
@@ -6511,7 +6514,7 @@ var NostrTools = (() => {
6511
6514
  result.quotes.push(pointer);
6512
6515
  continue;
6513
6516
  }
6514
- if ((tag[0] === "P" || tag[0] === "p") && tag[1] && HEX642.test(tag[1])) {
6517
+ if ((tag[0] === "P" || tag[0] === "p") && tag[1] && isHex32(tag[1])) {
6515
6518
  result.profiles.push({
6516
6519
  pubkey: tag[1],
6517
6520
  relays: tag[2] ? [tag[2]] : []
@@ -7011,10 +7014,10 @@ var NostrTools = (() => {
7011
7014
  let p = zapRequest.tags.find(([t, v]) => t === "p" && v);
7012
7015
  if (!p)
7013
7016
  return "Zap request doesn't have a 'p' tag.";
7014
- if (!p[1].match(/^[a-f0-9]{64}$/))
7017
+ if (!isHex32(p[1]))
7015
7018
  return "Zap request 'p' tag is not valid hex.";
7016
7019
  let e = zapRequest.tags.find(([t, v]) => t === "e" && v);
7017
- if (e && !e[1].match(/^[a-f0-9]{64}$/))
7020
+ if (e && !isHex32(e[1]))
7018
7021
  return "Zap request 'e' tag is not valid hex.";
7019
7022
  let relays = zapRequest.tags.find(([t, v]) => t === "relays" && v);
7020
7023
  if (!relays)