@openclaw/nostr 2026.7.2-beta.7 → 2026.7.33

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 (159) hide show
  1. package/dist/{channel-O6-jXoT5.js → .setup/channel-B4SX7Iq6.mjs} +554 -877
  2. package/dist/{config-schema-CelPzg1N.js → .setup/config-schema-DIiXiBKr.mjs} +1 -1
  3. package/dist/.setup/inbound-direct-dm-runtime-EdLLVtkh.mjs +2 -0
  4. package/dist/{setup-adapter-BjKmSwdW.js → .setup/setup-adapter-DEU3o8MF.mjs} +1 -31
  5. package/dist/.setup/setup-plugin-api-DzlCumkz.mjs +2 -0
  6. package/dist/.setup/setup-plugin-api-csfJrRxD.mjs +111 -0
  7. package/dist/{setup-surface-BJCFpe02.js → .setup/setup-surface-DWa6-9ku.mjs} +70 -41
  8. package/dist/api.js +72 -61
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/doctor-contract-api.js +3 -2
  11. package/dist/setup-api.js +1 -1
  12. package/dist/setup-entry.js +2 -1
  13. package/dist/setup-plugin-api.js +1 -112
  14. package/node_modules/nostr-tools/lib/cjs/abstract-pool.js +28 -59
  15. package/node_modules/nostr-tools/lib/cjs/abstract-pool.js.map +3 -3
  16. package/node_modules/nostr-tools/lib/cjs/abstract-relay.js +7 -44
  17. package/node_modules/nostr-tools/lib/cjs/abstract-relay.js.map +2 -2
  18. package/node_modules/nostr-tools/lib/cjs/core.js +1 -17
  19. package/node_modules/nostr-tools/lib/cjs/core.js.map +3 -3
  20. package/node_modules/nostr-tools/lib/cjs/filter.js.map +1 -1
  21. package/node_modules/nostr-tools/lib/cjs/index.js +130 -172
  22. package/node_modules/nostr-tools/lib/cjs/index.js.map +4 -4
  23. package/node_modules/nostr-tools/lib/cjs/kinds.js +1 -17
  24. package/node_modules/nostr-tools/lib/cjs/kinds.js.map +3 -3
  25. package/node_modules/nostr-tools/lib/cjs/nip04.js.map +1 -1
  26. package/node_modules/nostr-tools/lib/cjs/nip10.js +5 -20
  27. package/node_modules/nostr-tools/lib/cjs/nip10.js.map +3 -3
  28. package/node_modules/nostr-tools/lib/cjs/nip13.js.map +1 -1
  29. package/node_modules/nostr-tools/lib/cjs/nip17.js +23 -30
  30. package/node_modules/nostr-tools/lib/cjs/nip17.js.map +4 -4
  31. package/node_modules/nostr-tools/lib/cjs/nip18.js +14 -21
  32. package/node_modules/nostr-tools/lib/cjs/nip18.js.map +4 -4
  33. package/node_modules/nostr-tools/lib/cjs/nip19.js.map +1 -1
  34. package/node_modules/nostr-tools/lib/cjs/nip21.js.map +1 -1
  35. package/node_modules/nostr-tools/lib/cjs/nip22.js +7 -22
  36. package/node_modules/nostr-tools/lib/cjs/nip22.js.map +3 -3
  37. package/node_modules/nostr-tools/lib/cjs/nip25.js +14 -21
  38. package/node_modules/nostr-tools/lib/cjs/nip25.js.map +4 -4
  39. package/node_modules/nostr-tools/lib/cjs/nip27.js.map +1 -1
  40. package/node_modules/nostr-tools/lib/cjs/nip28.js +14 -21
  41. package/node_modules/nostr-tools/lib/cjs/nip28.js.map +4 -4
  42. package/node_modules/nostr-tools/lib/cjs/nip29.js.map +1 -1
  43. package/node_modules/nostr-tools/lib/cjs/nip30.js.map +2 -2
  44. package/node_modules/nostr-tools/lib/cjs/nip42.js.map +2 -2
  45. package/node_modules/nostr-tools/lib/cjs/nip44.js.map +1 -1
  46. package/node_modules/nostr-tools/lib/cjs/nip45.js +9 -22
  47. package/node_modules/nostr-tools/lib/cjs/nip45.js.map +4 -4
  48. package/node_modules/nostr-tools/lib/cjs/nip46.js +70 -111
  49. package/node_modules/nostr-tools/lib/cjs/nip46.js.map +4 -4
  50. package/node_modules/nostr-tools/lib/cjs/nip47.js +18 -25
  51. package/node_modules/nostr-tools/lib/cjs/nip47.js.map +4 -4
  52. package/node_modules/nostr-tools/lib/cjs/nip57.js +16 -23
  53. package/node_modules/nostr-tools/lib/cjs/nip57.js.map +4 -4
  54. package/node_modules/nostr-tools/lib/cjs/nip58.js.map +2 -2
  55. package/node_modules/nostr-tools/lib/cjs/nip59.js +7 -16
  56. package/node_modules/nostr-tools/lib/cjs/nip59.js.map +3 -3
  57. package/node_modules/nostr-tools/lib/cjs/nip75.js.map +1 -1
  58. package/node_modules/nostr-tools/lib/cjs/nip94.js.map +1 -1
  59. package/node_modules/nostr-tools/lib/cjs/nip98.js +16 -23
  60. package/node_modules/nostr-tools/lib/cjs/nip98.js.map +4 -4
  61. package/node_modules/nostr-tools/lib/cjs/nip99.js.map +2 -2
  62. package/node_modules/nostr-tools/lib/cjs/nipb0.js +17 -38
  63. package/node_modules/nostr-tools/lib/cjs/nipb0.js.map +4 -4
  64. package/node_modules/nostr-tools/lib/cjs/nipb7.js +7 -13
  65. package/node_modules/nostr-tools/lib/cjs/nipb7.js.map +2 -2
  66. package/node_modules/nostr-tools/lib/cjs/pool.js +61 -102
  67. package/node_modules/nostr-tools/lib/cjs/pool.js.map +4 -4
  68. package/node_modules/nostr-tools/lib/cjs/pure.js +14 -21
  69. package/node_modules/nostr-tools/lib/cjs/pure.js.map +4 -4
  70. package/node_modules/nostr-tools/lib/cjs/references.js.map +1 -1
  71. package/node_modules/nostr-tools/lib/cjs/relay.js +42 -86
  72. package/node_modules/nostr-tools/lib/cjs/relay.js.map +4 -4
  73. package/node_modules/nostr-tools/lib/cjs/signer.js +14 -21
  74. package/node_modules/nostr-tools/lib/cjs/signer.js.map +4 -4
  75. package/node_modules/nostr-tools/lib/cjs/utils.js +0 -10
  76. package/node_modules/nostr-tools/lib/cjs/utils.js.map +2 -2
  77. package/node_modules/nostr-tools/lib/cjs/wasm.js +2 -16
  78. package/node_modules/nostr-tools/lib/cjs/wasm.js.map +4 -4
  79. package/node_modules/nostr-tools/lib/esm/abstract-pool.js +28 -59
  80. package/node_modules/nostr-tools/lib/esm/abstract-pool.js.map +3 -3
  81. package/node_modules/nostr-tools/lib/esm/abstract-relay.js +7 -44
  82. package/node_modules/nostr-tools/lib/esm/abstract-relay.js.map +2 -2
  83. package/node_modules/nostr-tools/lib/esm/core.js +1 -15
  84. package/node_modules/nostr-tools/lib/esm/core.js.map +3 -3
  85. package/node_modules/nostr-tools/lib/esm/filter.js.map +1 -1
  86. package/node_modules/nostr-tools/lib/esm/index.js +80 -122
  87. package/node_modules/nostr-tools/lib/esm/index.js.map +4 -4
  88. package/node_modules/nostr-tools/lib/esm/kinds.js +1 -17
  89. package/node_modules/nostr-tools/lib/esm/kinds.js.map +3 -3
  90. package/node_modules/nostr-tools/lib/esm/nip04.js.map +1 -1
  91. package/node_modules/nostr-tools/lib/esm/nip10.js +5 -18
  92. package/node_modules/nostr-tools/lib/esm/nip10.js.map +3 -3
  93. package/node_modules/nostr-tools/lib/esm/nip13.js.map +1 -1
  94. package/node_modules/nostr-tools/lib/esm/nip17.js +8 -15
  95. package/node_modules/nostr-tools/lib/esm/nip17.js.map +4 -4
  96. package/node_modules/nostr-tools/lib/esm/nip18.js +8 -15
  97. package/node_modules/nostr-tools/lib/esm/nip18.js.map +4 -4
  98. package/node_modules/nostr-tools/lib/esm/nip19.js.map +1 -1
  99. package/node_modules/nostr-tools/lib/esm/nip21.js.map +1 -1
  100. package/node_modules/nostr-tools/lib/esm/nip22.js +7 -20
  101. package/node_modules/nostr-tools/lib/esm/nip22.js.map +3 -3
  102. package/node_modules/nostr-tools/lib/esm/nip25.js +8 -15
  103. package/node_modules/nostr-tools/lib/esm/nip25.js.map +4 -4
  104. package/node_modules/nostr-tools/lib/esm/nip27.js.map +1 -1
  105. package/node_modules/nostr-tools/lib/esm/nip28.js +8 -15
  106. package/node_modules/nostr-tools/lib/esm/nip28.js.map +4 -4
  107. package/node_modules/nostr-tools/lib/esm/nip29.js.map +1 -1
  108. package/node_modules/nostr-tools/lib/esm/nip30.js.map +2 -2
  109. package/node_modules/nostr-tools/lib/esm/nip42.js.map +2 -2
  110. package/node_modules/nostr-tools/lib/esm/nip44.js.map +1 -1
  111. package/node_modules/nostr-tools/lib/esm/nip45.js +10 -23
  112. package/node_modules/nostr-tools/lib/esm/nip45.js.map +4 -4
  113. package/node_modules/nostr-tools/lib/esm/nip46.js +54 -95
  114. package/node_modules/nostr-tools/lib/esm/nip46.js.map +4 -4
  115. package/node_modules/nostr-tools/lib/esm/nip47.js +8 -15
  116. package/node_modules/nostr-tools/lib/esm/nip47.js.map +4 -4
  117. package/node_modules/nostr-tools/lib/esm/nip57.js +10 -17
  118. package/node_modules/nostr-tools/lib/esm/nip57.js.map +4 -4
  119. package/node_modules/nostr-tools/lib/esm/nip58.js.map +2 -2
  120. package/node_modules/nostr-tools/lib/esm/nip59.js +1 -10
  121. package/node_modules/nostr-tools/lib/esm/nip59.js.map +3 -3
  122. package/node_modules/nostr-tools/lib/esm/nip75.js.map +1 -1
  123. package/node_modules/nostr-tools/lib/esm/nip94.js.map +1 -1
  124. package/node_modules/nostr-tools/lib/esm/nip98.js +8 -15
  125. package/node_modules/nostr-tools/lib/esm/nip98.js.map +4 -4
  126. package/node_modules/nostr-tools/lib/esm/nip99.js.map +2 -2
  127. package/node_modules/nostr-tools/lib/esm/nipb0.js +18 -39
  128. package/node_modules/nostr-tools/lib/esm/nipb0.js.map +4 -4
  129. package/node_modules/nostr-tools/lib/esm/nipb7.js +7 -13
  130. package/node_modules/nostr-tools/lib/esm/nipb7.js.map +2 -2
  131. package/node_modules/nostr-tools/lib/esm/pool.js +54 -95
  132. package/node_modules/nostr-tools/lib/esm/pool.js.map +4 -4
  133. package/node_modules/nostr-tools/lib/esm/pure.js +8 -15
  134. package/node_modules/nostr-tools/lib/esm/pure.js.map +4 -4
  135. package/node_modules/nostr-tools/lib/esm/references.js.map +1 -1
  136. package/node_modules/nostr-tools/lib/esm/relay.js +36 -80
  137. package/node_modules/nostr-tools/lib/esm/relay.js.map +4 -4
  138. package/node_modules/nostr-tools/lib/esm/signer.js +8 -15
  139. package/node_modules/nostr-tools/lib/esm/signer.js.map +4 -4
  140. package/node_modules/nostr-tools/lib/esm/utils.js +0 -10
  141. package/node_modules/nostr-tools/lib/esm/utils.js.map +2 -2
  142. package/node_modules/nostr-tools/lib/esm/wasm.js +3 -17
  143. package/node_modules/nostr-tools/lib/esm/wasm.js.map +4 -4
  144. package/node_modules/nostr-tools/lib/nostr.bundle.js +77 -121
  145. package/node_modules/nostr-tools/lib/nostr.bundle.js.map +4 -4
  146. package/node_modules/nostr-tools/lib/types/abstract-pool.d.ts +4 -7
  147. package/node_modules/nostr-tools/lib/types/abstract-relay.d.ts +2 -7
  148. package/node_modules/nostr-tools/lib/types/helpers.d.ts +2 -0
  149. package/node_modules/nostr-tools/lib/types/kinds.d.ts +0 -2
  150. package/node_modules/nostr-tools/lib/types/nipb0.d.ts +1 -0
  151. package/node_modules/nostr-tools/lib/types/nipb7.d.ts +1 -0
  152. package/node_modules/nostr-tools/lib/types/relay.d.ts +2 -2
  153. package/node_modules/nostr-tools/lib/types/utils.d.ts +0 -4
  154. package/node_modules/nostr-tools/package.json +1 -1
  155. package/openclaw.plugin.json +5 -0
  156. package/package.json +15 -41
  157. package/dist/inbound-direct-dm-runtime-DHn9NoYN.js +0 -2
  158. package/node_modules/nostr-tools/lib/types/nipb0.test.d.ts +0 -1
  159. /package/dist/{state-account-id-CvBZ9s6P.js → .setup/state-account-id-CvBZ9s6P.mjs} +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../nip44.ts", "../../utils.ts"],
4
- "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffffffff // 4294967295 (2^32-1)\nconst extendedPrefixThreshold = 0x10000 // 65536: lengths below use 2-byte u16 prefix, at or above use 6-byte prefix\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n return {\n chacha_key: keys.subarray(0, 32),\n chacha_nonce: keys.subarray(32, 44),\n hmac_key: keys.subarray(44, 76),\n }\n}\n\nfunction calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n if (len <= 32) return 32\n const nextPower = 2 ** (Math.floor(Math.log2(len - 1)) + 1)\n const chunk = nextPower <= 256 ? 32 : nextPower / 8\n return chunk * (Math.floor((len - 1) / chunk) + 1)\n}\n\nfunction writeU16BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > 0xffff)\n throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n const arr = new Uint8Array(2)\n new DataView(arr.buffer).setUint16(0, num, false)\n return arr\n}\n\nfunction writeU32BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < extendedPrefixThreshold || num > maxPlaintextSize)\n throw new Error('invalid plaintext size: must be between 65536 and 4294967295 bytes')\n const arr = new Uint8Array(4)\n new DataView(arr.buffer).setUint32(0, num, false)\n return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n const unpadded = utf8Encoder.encode(plaintext)\n const unpaddedLen = unpadded.length\n if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize)\n throw new Error('invalid plaintext size: must be between 1 and 4294967295 bytes')\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen)) // 6 bytes\n : writeU16BE(unpaddedLen) // 2 bytes\n const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen)\n return concatBytes(prefix, unpadded, suffix)\n}\n\nfunction unpad(padded: Uint8Array): string {\n const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength)\n const firstTwo = dv.getUint16(0)\n let unpaddedLen: number\n let prefixLen: number\n if (firstTwo === 0) {\n // Extended format: 2 zero bytes + 4-byte u32 length\n unpaddedLen = dv.getUint32(2)\n if (unpaddedLen < extendedPrefixThreshold) throw new Error('invalid padding')\n prefixLen = 6\n } else {\n unpaddedLen = firstTwo\n prefixLen = 2\n }\n const unpadded = padded.subarray(prefixLen, prefixLen + unpaddedLen)\n if (\n unpaddedLen < minPlaintextSize ||\n unpaddedLen > maxPlaintextSize ||\n unpadded.length !== unpaddedLen ||\n padded.length !== prefixLen + calcPaddedLen(unpaddedLen)\n )\n throw new Error('invalid padding')\n return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n const combined = concatBytes(aad, message)\n return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, mac: 32b)\n// plaintext: 1b to 0xffffffff\n// padded plaintext (small, <65536): 32b to 0x10000, with 2b prefix -> 34b to 0x10000+2\n// padded plaintext (large, >=65536): 0x10000 to 0x100000000, with 6b prefix -> 0x10006 to 0x100000000+6\n// ciphertext: same as padded plaintext (chacha20 doesn't change length)\n// raw payload (small): 99 (65+34) to 65603 (65+0x10000+2)\n// raw payload (large): 65607 (65+0x10006) to 4294967367 (65+0x100000000+6)\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n const plen = payload.length\n if (plen < 132) throw new Error('invalid payload length: ' + plen)\n if (payload[0] === '#') throw new Error('unknown encryption version')\n let data: Uint8Array\n try {\n data = base64.decode(payload)\n } catch (error) {\n throw new Error('invalid base64: ' + (error as any).message)\n }\n const dlen = data.length\n if (dlen < 99) throw new Error('invalid data length: ' + dlen)\n const vers = data[0]\n if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n return {\n nonce: data.subarray(1, 33),\n ciphertext: data.subarray(33, -32),\n mac: data.subarray(-32),\n }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n const padded = pad(plaintext)\n const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n const mac = hmacAad(hmac_key, ciphertext, nonce)\n return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\n/** Callers should validate payload size before calling to prevent DoS from oversized inputs. */\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n const { nonce, ciphertext, mac } = decodePayload(payload)\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n return unpad(padded)\n}\n\nexport const v2 = {\n utils: {\n getConversationKey,\n calcPaddedLen,\n pad,\n unpad,\n },\n encrypt,\n decrypt,\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\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"],
4
+ "sourcesContent": ["import { chacha20 } from '@noble/ciphers/chacha.js'\nimport { equalBytes } from '@noble/ciphers/utils.js'\nimport { secp256k1 } from '@noble/curves/secp256k1.js'\nimport { extract as hkdf_extract, expand as hkdf_expand } from '@noble/hashes/hkdf.js'\nimport { hmac } from '@noble/hashes/hmac.js'\nimport { sha256 } from '@noble/hashes/sha2.js'\nimport { concatBytes, hexToBytes, randomBytes } from '@noble/hashes/utils.js'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nconst minPlaintextSize = 0x0001 // 1b msg => padded to 32b\nconst maxPlaintextSize = 0xffffffff // 4294967295 (2^32-1)\nconst extendedPrefixThreshold = 0x10000 // 65536: lengths below use 2-byte u16 prefix, at or above use 6-byte prefix\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes('02' + pubkeyB)).subarray(1, 33)\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode('nip44-v2'))\n}\n\nfunction getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n const keys = hkdf_expand(sha256, conversationKey, nonce, 76)\n return {\n chacha_key: keys.subarray(0, 32),\n chacha_nonce: keys.subarray(32, 44),\n hmac_key: keys.subarray(44, 76),\n }\n}\n\nfunction calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) throw new Error('expected positive integer')\n if (len <= 32) return 32\n const nextPower = 2 ** (Math.floor(Math.log2(len - 1)) + 1)\n const chunk = nextPower <= 256 ? 32 : nextPower / 8\n return chunk * (Math.floor((len - 1) / chunk) + 1)\n}\n\nfunction writeU16BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > 0xffff)\n throw new Error('invalid plaintext size: must be between 1 and 65535 bytes')\n const arr = new Uint8Array(2)\n new DataView(arr.buffer).setUint16(0, num, false)\n return arr\n}\n\nfunction writeU32BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < extendedPrefixThreshold || num > maxPlaintextSize)\n throw new Error('invalid plaintext size: must be between 65536 and 4294967295 bytes')\n const arr = new Uint8Array(4)\n new DataView(arr.buffer).setUint32(0, num, false)\n return arr\n}\n\nfunction pad(plaintext: string): Uint8Array {\n const unpadded = utf8Encoder.encode(plaintext)\n const unpaddedLen = unpadded.length\n if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize)\n throw new Error('invalid plaintext size: must be between 1 and 4294967295 bytes')\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen)) // 6 bytes\n : writeU16BE(unpaddedLen) // 2 bytes\n const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen)\n return concatBytes(prefix, unpadded, suffix)\n}\n\nfunction unpad(padded: Uint8Array): string {\n const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength)\n const firstTwo = dv.getUint16(0)\n let unpaddedLen: number\n let prefixLen: number\n if (firstTwo === 0) {\n // Extended format: 2 zero bytes + 4-byte u32 length\n unpaddedLen = dv.getUint32(2)\n if (unpaddedLen < extendedPrefixThreshold) throw new Error('invalid padding')\n prefixLen = 6\n } else {\n unpaddedLen = firstTwo\n prefixLen = 2\n }\n const unpadded = padded.subarray(prefixLen, prefixLen + unpaddedLen)\n if (\n unpaddedLen < minPlaintextSize ||\n unpaddedLen > maxPlaintextSize ||\n unpadded.length !== unpaddedLen ||\n padded.length !== prefixLen + calcPaddedLen(unpaddedLen)\n )\n throw new Error('invalid padding')\n return utf8Decoder.decode(unpadded)\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) throw new Error('AAD associated data must be 32 bytes')\n const combined = concatBytes(aad, message)\n return hmac(sha256, key, combined)\n}\n\n// metadata: always 65b (version: 1b, nonce: 32b, mac: 32b)\n// plaintext: 1b to 0xffffffff\n// padded plaintext (small, <65536): 32b to 0x10000, with 2b prefix -> 34b to 0x10000+2\n// padded plaintext (large, >=65536): 0x10000 to 0x100000000, with 6b prefix -> 0x10006 to 0x100000000+6\n// ciphertext: same as padded plaintext (chacha20 doesn't change length)\n// raw payload (small): 99 (65+34) to 65603 (65+0x10000+2)\n// raw payload (large): 65607 (65+0x10006) to 4294967367 (65+0x100000000+6)\nfunction decodePayload(payload: string): { nonce: Uint8Array; ciphertext: Uint8Array; mac: Uint8Array } {\n if (typeof payload !== 'string') throw new Error('payload must be a valid string')\n const plen = payload.length\n if (plen < 132) throw new Error('invalid payload length: ' + plen)\n if (payload[0] === '#') throw new Error('unknown encryption version')\n let data: Uint8Array\n try {\n data = base64.decode(payload)\n } catch (error) {\n throw new Error('invalid base64: ' + (error as any).message)\n }\n const dlen = data.length\n if (dlen < 99) throw new Error('invalid data length: ' + dlen)\n const vers = data[0]\n if (vers !== 2) throw new Error('unknown encryption version ' + vers)\n return {\n nonce: data.subarray(1, 33),\n ciphertext: data.subarray(33, -32),\n mac: data.subarray(-32),\n }\n}\n\nexport function encrypt(plaintext: string, conversationKey: Uint8Array, nonce: Uint8Array = randomBytes(32)): string {\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n const padded = pad(plaintext)\n const ciphertext = chacha20(chacha_key, chacha_nonce, padded)\n const mac = hmacAad(hmac_key, ciphertext, nonce)\n return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac))\n}\n\n/** Callers should validate payload size before calling to prevent DoS from oversized inputs. */\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n const { nonce, ciphertext, mac } = decodePayload(payload)\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce)\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce)\n if (!equalBytes(calculatedMac, mac)) throw new Error('invalid MAC')\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext)\n return unpad(padded)\n}\n\nexport const v2 = {\n utils: {\n getConversationKey,\n calcPaddedLen,\n pad,\n unpad,\n },\n encrypt,\n decrypt,\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"],
5
5
  "mappings": "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",
6
6
  "names": ["hexToBytes", "hexToBytes"]
7
7
  }
@@ -1,25 +1,9 @@
1
1
  // nip45.ts
2
2
  import { sha256 } from "@noble/hashes/sha2.js";
3
- import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
4
-
5
- // utils.ts
6
- import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
7
- var utf8Decoder = new TextDecoder("utf-8");
8
- var utf8Encoder = new TextEncoder();
9
- function isHex32(input) {
10
- for (let i = 0; i < 64; i++) {
11
- let cc = input.charCodeAt(i);
12
- if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
13
- return false;
14
- }
15
- }
16
- return true;
17
- }
18
-
19
- // nip45.ts
3
+ import { bytesToHex } from "@noble/hashes/utils.js";
20
4
  var M = 256;
21
5
  var HLL_HEX_LENGTH = M * 2;
22
- var utf8Encoder2 = new TextEncoder();
6
+ var utf8Encoder = new TextEncoder();
23
7
  function getCountManyFilter(target, directive) {
24
8
  switch (directive) {
25
9
  case "reactions":
@@ -40,7 +24,7 @@ function newHll() {
40
24
  return new Uint8Array(M);
41
25
  }
42
26
  function hllDecode(hex) {
43
- if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-f]+$/.test(hex))
27
+ if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex))
44
28
  return void 0;
45
29
  const registers = new Uint8Array(M);
46
30
  for (let i = 0; i < M; i++) {
@@ -59,12 +43,12 @@ function hllEncode(registers) {
59
43
  }
60
44
  function computeOffset(filterFirstTagValue) {
61
45
  let hex = filterFirstTagValue;
62
- if (!isHex32(hex)) {
46
+ if (!isHex64(hex)) {
63
47
  const parts = hex.split(":");
64
- if (parts.length === 3 && isHex32(parts[1])) {
48
+ if (parts.length === 3 && isHex64(parts[1])) {
65
49
  hex = parts[1];
66
50
  } else {
67
- hex = bytesToHex2(sha256(utf8Encoder2.encode(filterFirstTagValue)));
51
+ hex = bytesToHex(sha256(utf8Encoder.encode(filterFirstTagValue)));
68
52
  }
69
53
  }
70
54
  return parseInt(hex[32], 16) + 8;
@@ -82,7 +66,7 @@ function getFilterFirstTagValue(filter) {
82
66
  function feedPubkey(hll, pubkey, offset) {
83
67
  if (offset < 0 || offset > 24)
84
68
  throw new Error(`invalid offset ${offset}`);
85
- if (!isHex32(pubkey))
69
+ if (!isHex64(pubkey))
86
70
  throw new Error("pubkey must be 32-byte hex");
87
71
  if (hll.length === 0)
88
72
  hll = newHll();
@@ -126,6 +110,9 @@ function estimateCount(hll) {
126
110
  return Math.floor(linearCounting(M, v));
127
111
  return Math.floor(estimate);
128
112
  }
113
+ function isHex64(value) {
114
+ return /^[0-9a-fA-F]{64}$/.test(value);
115
+ }
129
116
  function countLeadingZeroBitsAfterOffset(pubkey, offset) {
130
117
  let zeroBits = 0;
131
118
  for (let i = offset + 1; i < offset + 8; i++) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../nip45.ts", "../../utils.ts"],
4
- "sourcesContent": ["import { sha256 } from '@noble/hashes/sha2.js'\nimport { bytesToHex } from '@noble/hashes/utils.js'\n\nimport type { Event } from './core.ts'\nimport type { Filter } from './filter.ts'\nimport { isHex32 } from './utils.ts'\n\nconst M = 256\nconst HLL_HEX_LENGTH = M * 2\nconst utf8Encoder = new TextEncoder()\n\nexport type CountManyDirective = 'reactions' | 'reposts' | 'quotes' | 'replies' | 'comments' | 'followers'\n\nexport function getCountManyFilter(target: string, directive: CountManyDirective): Filter {\n switch (directive) {\n case 'reactions':\n return { '#e': [target], kinds: [7] }\n case 'reposts':\n return { '#e': [target], kinds: [6] }\n case 'quotes':\n return { '#q': [target], kinds: [1, 1111] }\n case 'replies':\n return { '#e': [target], kinds: [1] }\n case 'comments':\n return { '#E': [target], kinds: [1111] }\n case 'followers':\n return { '#p': [target], kinds: [3] }\n }\n}\n\nexport function newHll(): Uint8Array {\n return new Uint8Array(M)\n}\n\nexport function hllDecode(hex: string): Uint8Array | undefined {\n if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-f]+$/.test(hex)) return undefined\n\n const registers = new Uint8Array(M)\n for (let i = 0; i < M; i++) {\n registers[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16)\n }\n return registers\n}\n\nexport function hllEncode(registers: Uint8Array): string {\n if (registers.length !== M) throw new Error(`invalid number of registers ${registers.length}`)\n\n let hex = ''\n for (let i = 0; i < M; i++) {\n hex += registers[i].toString(16).padStart(2, '0')\n }\n return hex\n}\n\nexport function computeOffset(filterFirstTagValue: string): number {\n let hex = filterFirstTagValue\n\n if (!isHex32(hex)) {\n const parts = hex.split(':')\n if (parts.length === 3 && isHex32(parts[1])) {\n hex = parts[1]\n } else {\n hex = bytesToHex(sha256(utf8Encoder.encode(filterFirstTagValue)))\n }\n }\n\n return parseInt(hex[32], 16) + 8\n}\n\nexport function getFilterFirstTagValue(filter: Filter): string | undefined {\n for (const key in filter) {\n if (key[0] !== '#') continue\n\n const values = filter[key as `#${string}`]\n if (Array.isArray(values) && typeof values[0] === 'string') return values[0]\n }\n\n return undefined\n}\n\nexport function feedPubkey(hll: Uint8Array, pubkey: string, offset: number): Uint8Array {\n if (offset < 0 || offset > 24) throw new Error(`invalid offset ${offset}`)\n if (!isHex32(pubkey)) throw new Error('pubkey must be 32-byte hex')\n\n if (hll.length === 0) hll = newHll()\n if (hll.length !== M) throw new Error(`invalid number of registers ${hll.length}`)\n\n const ri = parseInt(pubkey.slice(offset * 2, offset * 2 + 2), 16)\n const value = countLeadingZeroBitsAfterOffset(pubkey, offset) + 1\n if (value > hll[ri]) hll[ri] = value\n\n return hll\n}\n\nexport function feedEvent(hll: Uint8Array, event: Event, offset: number): Uint8Array {\n return feedPubkey(hll, event.pubkey, offset)\n}\n\nexport function mergeHll(target: Uint8Array, source: Uint8Array): Uint8Array {\n if (target.length === 0) target = newHll()\n if (target.length !== M) throw new Error(`invalid number of registers ${target.length}`)\n if (source.length !== M) throw new Error(`invalid number of registers ${source.length}`)\n\n for (let i = 0; i < M; i++) {\n if (source[i] > target[i]) target[i] = source[i]\n }\n\n return target\n}\n\nexport function estimateCount(hll: Uint8Array): number {\n if (hll.length === 0) return 0\n if (hll.length !== M) throw new Error(`invalid number of registers ${hll.length}`)\n\n const v = countZeros(hll)\n if (v !== 0) {\n const lc = linearCounting(M, v)\n if (lc <= 220) return Math.floor(lc)\n }\n\n const estimate = calculateEstimate(hll)\n if (estimate <= M * 3 && v !== 0) return Math.floor(linearCounting(M, v))\n\n return Math.floor(estimate)\n}\n\nfunction countLeadingZeroBitsAfterOffset(pubkey: string, offset: number): number {\n let zeroBits = 0\n for (let i = offset + 1; i < offset + 8; i++) {\n const byte = parseInt(pubkey.slice(i * 2, i * 2 + 2), 16)\n if (byte === 0) {\n zeroBits += 8\n continue\n }\n\n let mask = 0x80\n while ((byte & mask) === 0) {\n zeroBits++\n mask >>= 1\n }\n break\n }\n return zeroBits\n}\n\nfunction countZeros(registers: Uint8Array): number {\n let count = 0\n for (let i = 0; i < M; i++) {\n if (registers[i] === 0) count++\n }\n return count\n}\n\nfunction linearCounting(m: number, v: number): number {\n return m * Math.log(m / v)\n}\n\nfunction calculateEstimate(registers: Uint8Array): number {\n let sum = 0\n for (let i = 0; i < M; i++) {\n sum += 1 / 2 ** registers[i]\n }\n return (0.7182725932495458 * M * M) / sum\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\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
- "names": ["bytesToHex", "utf8Encoder", "bytesToHex"]
3
+ "sources": ["../../nip45.ts"],
4
+ "sourcesContent": ["import { sha256 } from '@noble/hashes/sha2.js'\nimport { bytesToHex } from '@noble/hashes/utils.js'\n\nimport type { Event } from './core.ts'\nimport type { Filter } from './filter.ts'\n\nconst M = 256\nconst HLL_HEX_LENGTH = M * 2\nconst utf8Encoder = new TextEncoder()\n\nexport type CountManyDirective = 'reactions' | 'reposts' | 'quotes' | 'replies' | 'comments' | 'followers'\n\nexport function getCountManyFilter(target: string, directive: CountManyDirective): Filter {\n switch (directive) {\n case 'reactions':\n return { '#e': [target], kinds: [7] }\n case 'reposts':\n return { '#e': [target], kinds: [6] }\n case 'quotes':\n return { '#q': [target], kinds: [1, 1111] }\n case 'replies':\n return { '#e': [target], kinds: [1] }\n case 'comments':\n return { '#E': [target], kinds: [1111] }\n case 'followers':\n return { '#p': [target], kinds: [3] }\n }\n}\n\nexport function newHll(): Uint8Array {\n return new Uint8Array(M)\n}\n\nexport function hllDecode(hex: string): Uint8Array | undefined {\n if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex)) return undefined\n\n const registers = new Uint8Array(M)\n for (let i = 0; i < M; i++) {\n registers[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16)\n }\n return registers\n}\n\nexport function hllEncode(registers: Uint8Array): string {\n if (registers.length !== M) throw new Error(`invalid number of registers ${registers.length}`)\n\n let hex = ''\n for (let i = 0; i < M; i++) {\n hex += registers[i].toString(16).padStart(2, '0')\n }\n return hex\n}\n\nexport function computeOffset(filterFirstTagValue: string): number {\n let hex = filterFirstTagValue\n\n if (!isHex64(hex)) {\n const parts = hex.split(':')\n if (parts.length === 3 && isHex64(parts[1])) {\n hex = parts[1]\n } else {\n hex = bytesToHex(sha256(utf8Encoder.encode(filterFirstTagValue)))\n }\n }\n\n return parseInt(hex[32], 16) + 8\n}\n\nexport function getFilterFirstTagValue(filter: Filter): string | undefined {\n for (const key in filter) {\n if (key[0] !== '#') continue\n\n const values = filter[key as `#${string}`]\n if (Array.isArray(values) && typeof values[0] === 'string') return values[0]\n }\n\n return undefined\n}\n\nexport function feedPubkey(hll: Uint8Array, pubkey: string, offset: number): Uint8Array {\n if (offset < 0 || offset > 24) throw new Error(`invalid offset ${offset}`)\n if (!isHex64(pubkey)) throw new Error('pubkey must be 32-byte hex')\n\n if (hll.length === 0) hll = newHll()\n if (hll.length !== M) throw new Error(`invalid number of registers ${hll.length}`)\n\n const ri = parseInt(pubkey.slice(offset * 2, offset * 2 + 2), 16)\n const value = countLeadingZeroBitsAfterOffset(pubkey, offset) + 1\n if (value > hll[ri]) hll[ri] = value\n\n return hll\n}\n\nexport function feedEvent(hll: Uint8Array, event: Event, offset: number): Uint8Array {\n return feedPubkey(hll, event.pubkey, offset)\n}\n\nexport function mergeHll(target: Uint8Array, source: Uint8Array): Uint8Array {\n if (target.length === 0) target = newHll()\n if (target.length !== M) throw new Error(`invalid number of registers ${target.length}`)\n if (source.length !== M) throw new Error(`invalid number of registers ${source.length}`)\n\n for (let i = 0; i < M; i++) {\n if (source[i] > target[i]) target[i] = source[i]\n }\n\n return target\n}\n\nexport function estimateCount(hll: Uint8Array): number {\n if (hll.length === 0) return 0\n if (hll.length !== M) throw new Error(`invalid number of registers ${hll.length}`)\n\n const v = countZeros(hll)\n if (v !== 0) {\n const lc = linearCounting(M, v)\n if (lc <= 220) return Math.floor(lc)\n }\n\n const estimate = calculateEstimate(hll)\n if (estimate <= M * 3 && v !== 0) return Math.floor(linearCounting(M, v))\n\n return Math.floor(estimate)\n}\n\nfunction isHex64(value: string): boolean {\n return /^[0-9a-fA-F]{64}$/.test(value)\n}\n\nfunction countLeadingZeroBitsAfterOffset(pubkey: string, offset: number): number {\n let zeroBits = 0\n for (let i = offset + 1; i < offset + 8; i++) {\n const byte = parseInt(pubkey.slice(i * 2, i * 2 + 2), 16)\n if (byte === 0) {\n zeroBits += 8\n continue\n }\n\n let mask = 0x80\n while ((byte & mask) === 0) {\n zeroBits++\n mask >>= 1\n }\n break\n }\n return zeroBits\n}\n\nfunction countZeros(registers: Uint8Array): number {\n let count = 0\n for (let i = 0; i < M; i++) {\n if (registers[i] === 0) count++\n }\n return count\n}\n\nfunction linearCounting(m: number, v: number): number {\n return m * Math.log(m / v)\n}\n\nfunction calculateEstimate(registers: Uint8Array): number {\n let sum = 0\n for (let i = 0; i < M; i++) {\n sum += 1 / 2 ** registers[i]\n }\n return (0.7182725932495458 * M * M) / sum\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
7
  }
@@ -2,41 +2,6 @@
2
2
  import { schnorr } from "@noble/curves/secp256k1.js";
3
3
  import { bytesToHex as bytesToHex2, hexToBytes as hexToBytes2 } from "@noble/hashes/utils.js";
4
4
 
5
- // utils.ts
6
- import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
7
- var utf8Decoder = new TextDecoder("utf-8");
8
- var utf8Encoder = new TextEncoder();
9
- function normalizeURL(url) {
10
- try {
11
- if (url.indexOf("://") === -1)
12
- url = "wss://" + url;
13
- let p = new URL(url);
14
- if (p.protocol === "http:")
15
- p.protocol = "ws:";
16
- else if (p.protocol === "https:")
17
- p.protocol = "wss:";
18
- p.pathname = p.pathname.replace(/\/+/g, "/");
19
- if (p.pathname.endsWith("/"))
20
- p.pathname = p.pathname.slice(0, -1);
21
- if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
22
- p.port = "";
23
- p.searchParams.sort();
24
- p.hash = "";
25
- return p.toString();
26
- } catch (e) {
27
- throw new Error(`Invalid URL: ${url}`);
28
- }
29
- }
30
- function isHex32(input) {
31
- for (let i2 = 0; i2 < 64; i2++) {
32
- let cc = input.charCodeAt(i2);
33
- if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
34
- return false;
35
- }
36
- }
37
- return true;
38
- }
39
-
40
5
  // core.ts
41
6
  var verifiedSymbol = Symbol("verified");
42
7
  var isRecord = (obj) => obj instanceof Object;
@@ -51,7 +16,7 @@ function validateEvent(event) {
51
16
  return false;
52
17
  if (typeof event.pubkey !== "string")
53
18
  return false;
54
- if (!isHex32(event.pubkey))
19
+ if (!event.pubkey.match(/^[a-f0-9]{64}$/))
55
20
  return false;
56
21
  if (!Array.isArray(event.tags))
57
22
  return false;
@@ -69,6 +34,34 @@ function validateEvent(event) {
69
34
 
70
35
  // pure.ts
71
36
  import { sha256 } from "@noble/hashes/sha2.js";
37
+
38
+ // utils.ts
39
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
40
+ var utf8Decoder = new TextDecoder("utf-8");
41
+ var utf8Encoder = new TextEncoder();
42
+ function normalizeURL(url) {
43
+ try {
44
+ if (url.indexOf("://") === -1)
45
+ url = "wss://" + url;
46
+ let p = new URL(url);
47
+ if (p.protocol === "http:")
48
+ p.protocol = "ws:";
49
+ else if (p.protocol === "https:")
50
+ p.protocol = "wss:";
51
+ p.pathname = p.pathname.replace(/\/+/g, "/");
52
+ if (p.pathname.endsWith("/"))
53
+ p.pathname = p.pathname.slice(0, -1);
54
+ if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
55
+ p.port = "";
56
+ p.searchParams.sort();
57
+ p.hash = "";
58
+ return p.toString();
59
+ } catch (e) {
60
+ throw new Error(`Invalid URL: ${url}`);
61
+ }
62
+ }
63
+
64
+ // pure.ts
72
65
  var JS = class {
73
66
  generateSecretKey() {
74
67
  return schnorr.utils.randomSecretKey();
@@ -345,14 +338,12 @@ var AbstractRelay = class {
345
338
  openSubs = /* @__PURE__ */ new Map();
346
339
  enablePing;
347
340
  enableReconnect;
348
- idleTimeout = 0;
349
341
  idleSince = Date.now();
350
342
  ongoingOperations = 0;
351
343
  reconnectTimeoutHandle;
352
344
  pingIntervalHandle;
353
345
  reconnectAttempts = 0;
354
346
  skipReconnection = false;
355
- idleTimeoutHandle;
356
347
  connectionPromise;
357
348
  openCountRequests = /* @__PURE__ */ new Map();
358
349
  openEventPublishes = /* @__PURE__ */ new Map();
@@ -368,8 +359,6 @@ var AbstractRelay = class {
368
359
  this._WebSocket = opts.websocketImplementation || WebSocket;
369
360
  this.enablePing = opts.enablePing;
370
361
  this.enableReconnect = opts.enableReconnect || false;
371
- if (opts.idleTimeout)
372
- this.idleTimeout = opts.idleTimeout;
373
362
  }
374
363
  static async connect(url, opts) {
375
364
  const relay = new AbstractRelay(url, opts);
@@ -393,22 +382,6 @@ var AbstractRelay = class {
393
382
  get connected() {
394
383
  return this._connected;
395
384
  }
396
- clearIdleTimeout() {
397
- if (this.idleTimeoutHandle) {
398
- clearTimeout(this.idleTimeoutHandle);
399
- this.idleTimeoutHandle = void 0;
400
- }
401
- }
402
- scheduleIdleClose() {
403
- this.clearIdleTimeout();
404
- if (this.idleTimeout > 0) {
405
- this.idleTimeoutHandle = setTimeout(() => {
406
- if (this.ongoingOperations === 0 && this.idleSince) {
407
- this.close();
408
- }
409
- }, this.idleTimeout);
410
- }
411
- }
412
385
  async reconnect() {
413
386
  const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)];
414
387
  this.reconnectAttempts++;
@@ -420,11 +393,6 @@ var AbstractRelay = class {
420
393
  }, backoff);
421
394
  }
422
395
  handleHardClose(reason) {
423
- if (this.ws) {
424
- this.ws.onopen = null;
425
- this.ws.onerror = null;
426
- this.ws.onclose = null;
427
- }
428
396
  if (this.pingIntervalHandle) {
429
397
  clearInterval(this.pingIntervalHandle);
430
398
  this.pingIntervalHandle = void 0;
@@ -432,7 +400,6 @@ var AbstractRelay = class {
432
400
  this._connected = false;
433
401
  this.connectionPromise = void 0;
434
402
  this.idleSince = void 0;
435
- this.clearIdleTimeout();
436
403
  if (this.enableReconnect && !this.skipReconnection) {
437
404
  this.reconnect();
438
405
  } else {
@@ -455,6 +422,7 @@ var AbstractRelay = class {
455
422
  if (this.reconnectAttempts === 0) {
456
423
  this.skipReconnection = true;
457
424
  }
425
+ this.onclose?.();
458
426
  this.handleHardClose("relay connection timed out");
459
427
  }, opts.timeout);
460
428
  }
@@ -500,6 +468,7 @@ var AbstractRelay = class {
500
468
  if (this.reconnectAttempts === 0) {
501
469
  this.skipReconnection = true;
502
470
  }
471
+ this.onclose?.();
503
472
  this.handleHardClose("relay connection failed");
504
473
  };
505
474
  this.ws.onclose = (ev) => {
@@ -588,7 +557,6 @@ var AbstractRelay = class {
588
557
  }
589
558
  async publish(event) {
590
559
  this.idleSince = void 0;
591
- this.clearIdleTimeout();
592
560
  this.ongoingOperations++;
593
561
  const ret = new Promise((resolve, reject) => {
594
562
  const timeout = setTimeout(() => {
@@ -610,10 +578,8 @@ var AbstractRelay = class {
610
578
  }
611
579
  }
612
580
  this.ongoingOperations--;
613
- if (this.ongoingOperations === 0) {
581
+ if (this.ongoingOperations === 0)
614
582
  this.idleSince = Date.now();
615
- this.scheduleIdleClose();
616
- }
617
583
  return ret;
618
584
  }
619
585
  async count(filters, params) {
@@ -639,7 +605,6 @@ var AbstractRelay = class {
639
605
  subscribe(filters, params) {
640
606
  if (params.label !== "<forced-ping>") {
641
607
  this.idleSince = void 0;
642
- this.clearIdleTimeout();
643
608
  this.ongoingOperations++;
644
609
  }
645
610
  const sub = this.prepareSubscription(filters, params);
@@ -668,17 +633,10 @@ var AbstractRelay = class {
668
633
  }
669
634
  this.closeAllSubscriptions("relay connection closed by us");
670
635
  this._connected = false;
671
- this.connectionPromise = void 0;
672
636
  this.idleSince = void 0;
673
- this.clearIdleTimeout();
674
637
  this.onclose?.();
675
- if (this.ws) {
676
- this.ws.onopen = null;
677
- this.ws.onerror = null;
678
- this.ws.onclose = null;
679
- if (this.ws.readyState !== this._WebSocket.CLOSING && this.ws.readyState !== this._WebSocket.CLOSED) {
680
- this.ws.close();
681
- }
638
+ if (this.ws?.readyState === this._WebSocket.OPEN) {
639
+ this.ws?.close();
682
640
  }
683
641
  }
684
642
  _onmessage(ev) {
@@ -705,7 +663,7 @@ var AbstractRelay = class {
705
663
  case "EVENT": {
706
664
  const so = this.openSubs.get(data[1]);
707
665
  const event = data[2];
708
- if (matchFilters(so.filters, event) && this.verifyEvent(event, this.url)) {
666
+ if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
709
667
  so.onevent(event);
710
668
  } else {
711
669
  so.oninvalidevent?.(event);
@@ -853,14 +811,18 @@ var Subscription = class {
853
811
  }
854
812
  this.relay.openSubs.delete(this.id);
855
813
  this.relay.ongoingOperations--;
856
- if (this.relay.ongoingOperations === 0) {
814
+ if (this.relay.ongoingOperations === 0)
857
815
  this.relay.idleSince = Date.now();
858
- this.relay.scheduleIdleClose();
859
- }
860
816
  this.onclose?.(reason);
861
817
  }
862
818
  };
863
819
 
820
+ // helpers.ts
821
+ var alwaysTrue = (t) => {
822
+ t[verifiedSymbol] = true;
823
+ return true;
824
+ };
825
+
864
826
  // nip45.ts
865
827
  import { sha256 as sha2563 } from "@noble/hashes/sha2.js";
866
828
  import { bytesToHex as bytesToHex3 } from "@noble/hashes/utils.js";
@@ -887,7 +849,7 @@ function newHll() {
887
849
  return new Uint8Array(M);
888
850
  }
889
851
  function hllDecode(hex) {
890
- if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-f]+$/.test(hex))
852
+ if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-fA-F]+$/.test(hex))
891
853
  return void 0;
892
854
  const registers = new Uint8Array(M);
893
855
  for (let i2 = 0; i2 < M; i2++) {
@@ -926,8 +888,8 @@ var AbstractSimplePool = class {
926
888
  verifyEvent;
927
889
  enablePing;
928
890
  enableReconnect;
929
- idleTimeout = 2e4;
930
891
  automaticallyAuth;
892
+ trustedRelayURLs = /* @__PURE__ */ new Set();
931
893
  onRelayConnectionFailure;
932
894
  onRelayConnectionSuccess;
933
895
  allowConnectingToRelay;
@@ -938,8 +900,6 @@ var AbstractSimplePool = class {
938
900
  this._WebSocket = opts.websocketImplementation;
939
901
  this.enablePing = opts.enablePing;
940
902
  this.enableReconnect = opts.enableReconnect || false;
941
- if (opts.idleTimeout)
942
- this.idleTimeout = opts.idleTimeout;
943
903
  this.automaticallyAuth = opts.automaticallyAuth;
944
904
  this.onRelayConnectionFailure = opts.onRelayConnectionFailure;
945
905
  this.onRelayConnectionSuccess = opts.onRelayConnectionSuccess;
@@ -951,11 +911,10 @@ var AbstractSimplePool = class {
951
911
  let relay = this.relays.get(url);
952
912
  if (!relay) {
953
913
  relay = new AbstractRelay(url, {
954
- verifyEvent: this.verifyEvent,
914
+ verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
955
915
  websocketImplementation: this._WebSocket,
956
916
  enablePing: this.enablePing,
957
- enableReconnect: this.enableReconnect,
958
- idleTimeout: this.idleTimeout
917
+ enableReconnect: this.enableReconnect
959
918
  });
960
919
  relay.onclose = () => {
961
920
  this.relays.delete(url);
@@ -1035,11 +994,11 @@ var AbstractSimplePool = class {
1035
994
  }
1036
995
  };
1037
996
  const closesReceived = [];
1038
- let handleClose = (i2, url, reason) => {
997
+ let handleClose = (i2, reason) => {
1039
998
  if (closesReceived[i2])
1040
999
  return;
1041
1000
  handleEose(i2);
1042
- closesReceived[i2] = { url, reason };
1001
+ closesReceived[i2] = reason;
1043
1002
  if (closesReceived.filter((a) => a).length === groupedRequests.length) {
1044
1003
  params.onclose?.(closesReceived);
1045
1004
  handleClose = () => {
@@ -1057,7 +1016,7 @@ var AbstractSimplePool = class {
1057
1016
  const allOpened = Promise.all(
1058
1017
  groupedRequests.map(async ({ url, filters }, i2) => {
1059
1018
  if (this.allowConnectingToRelay?.(url, ["read", filters]) === false) {
1060
- handleClose(i2, url, "connection skipped by allowConnectingToRelay");
1019
+ handleClose(i2, "connection skipped by allowConnectingToRelay");
1061
1020
  return;
1062
1021
  }
1063
1022
  let relay;
@@ -1068,7 +1027,7 @@ var AbstractSimplePool = class {
1068
1027
  });
1069
1028
  } catch (err) {
1070
1029
  this.onRelayConnectionFailure?.(url);
1071
- handleClose(i2, url, err?.message || String(err));
1030
+ handleClose(i2, err?.message || String(err));
1072
1031
  return;
1073
1032
  }
1074
1033
  this.onRelayConnectionSuccess?.(url);
@@ -1082,17 +1041,17 @@ var AbstractSimplePool = class {
1082
1041
  ...params,
1083
1042
  oneose: () => handleEose(i2),
1084
1043
  onclose: (reason2) => {
1085
- handleClose(i2, url, reason2);
1044
+ handleClose(i2, reason2);
1086
1045
  },
1087
1046
  alreadyHaveEvent: localAlreadyHaveEventHandler,
1088
1047
  eoseTimeout: params.maxWait,
1089
1048
  abort: params.abort
1090
1049
  });
1091
1050
  }).catch((err) => {
1092
- handleClose(i2, url, `auth was required and attempted, but failed with: ${err}`);
1051
+ handleClose(i2, `auth was required and attempted, but failed with: ${err}`);
1093
1052
  });
1094
1053
  } else {
1095
- handleClose(i2, url, reason);
1054
+ handleClose(i2, reason);
1096
1055
  }
1097
1056
  },
1098
1057
  alreadyHaveEvent: localAlreadyHaveEventHandler,
@@ -1120,7 +1079,7 @@ var AbstractSimplePool = class {
1120
1079
  if (subcloser)
1121
1080
  subcloser.close(reason);
1122
1081
  else
1123
- params.onclose?.(relays.map((url) => ({ url, reason })));
1082
+ params.onclose?.(relays.map((_) => reason));
1124
1083
  }
1125
1084
  });
1126
1085
  return subcloser;