@qntx/nostr 0.14.0 → 0.15.1

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 (154) hide show
  1. package/dist/{builder-CGgM_QHT.mjs → builder-70CC2EBq.mjs} +4 -4
  2. package/dist/{builder-CGgM_QHT.mjs.map → builder-70CC2EBq.mjs.map} +1 -1
  3. package/dist/{builder-C72nJITG.d.mts → builder-Dk00TdNf.d.mts} +2 -2
  4. package/dist/{client-COkEihj_.mjs → client-BXmvZ7vU.mjs} +45 -30
  5. package/dist/client-BXmvZ7vU.mjs.map +1 -0
  6. package/dist/client.d.mts +1 -1
  7. package/dist/client.mjs +1 -2
  8. package/dist/core.d.mts +8 -7
  9. package/dist/core.mjs +8 -8
  10. package/dist/{error-Dyb89ahb.mjs → error-CoP2myne.mjs} +2 -2
  11. package/dist/{error-Dyb89ahb.mjs.map → error-CoP2myne.mjs.map} +1 -1
  12. package/dist/{error-BPqAU5RB.d.mts → error-DE1UCZ2J.d.mts} +6 -2
  13. package/dist/{error-BjSUsx6M.d.mts → error-IHMcLy8-.d.mts} +2 -2
  14. package/dist/{error-QyECUDIp.mjs → error-VDn7Nv-6.mjs} +2 -2
  15. package/dist/{error-QyECUDIp.mjs.map → error-VDn7Nv-6.mjs.map} +1 -1
  16. package/dist/{event-DvM1f03b.mjs → event-DHtcm0jM.mjs} +17 -6
  17. package/dist/event-DHtcm0jM.mjs.map +1 -0
  18. package/dist/{filter-3jOh4uLN.mjs → filter-BbShkmc4.mjs} +34 -28
  19. package/dist/filter-BbShkmc4.mjs.map +1 -0
  20. package/dist/{filter-oYG2zjJ3.d.mts → filter-w1R-mU63.d.mts} +8 -6
  21. package/dist/{gossip-BvCItMVZ.d.mts → gossip-C-7WhJ7v.d.mts} +3 -3
  22. package/dist/{gossip-CRyhK6E6.mjs → gossip-CejMm_QM.mjs} +13 -11
  23. package/dist/gossip-CejMm_QM.mjs.map +1 -0
  24. package/dist/gossip.d.mts +1 -1
  25. package/dist/gossip.mjs +1 -2
  26. package/dist/{index-B2JlP2Jf.d.mts → index-11znTLM0.d.mts} +4 -4
  27. package/dist/{index-DzHmhHqD.d.mts → index-B7nMjbyt.d.mts} +3 -3
  28. package/dist/{index-muydfuYD.d.mts → index-BuNI8JH_.d.mts} +13 -13
  29. package/dist/{index-Dpj_MUxb.d.mts → index-C5QomO2D.d.mts} +10 -3
  30. package/dist/{index-DCobP2Jj.d.mts → index-DDZjqV48.d.mts} +10 -10
  31. package/dist/{index-CcgmG3F6.d.mts → index-DYk24u9o.d.mts} +12 -7
  32. package/dist/index.d.mts +21 -20
  33. package/dist/index.mjs +19 -19
  34. package/dist/{key-D_oEutQ6.mjs → key-Bt0ow7mI.mjs} +33 -11
  35. package/dist/key-Bt0ow7mI.mjs.map +1 -0
  36. package/dist/{key-DojW2QZl.d.mts → key-E0AJKMRB.d.mts} +19 -11
  37. package/dist/{loaders-BRh2riV9.mjs → loaders-COGGrZY4.mjs} +7 -7
  38. package/dist/loaders-COGGrZY4.mjs.map +1 -0
  39. package/dist/loaders.d.mts +2 -2
  40. package/dist/loaders.mjs +1 -2
  41. package/dist/{memory-D_GdEQYG.mjs → memory-Dq4dg-U4.mjs} +2 -2
  42. package/dist/{memory-D_GdEQYG.mjs.map → memory-Dq4dg-U4.mjs.map} +1 -1
  43. package/dist/{memory-index-3GSrPIWj.mjs → memory-index-DhlmbqWx.mjs} +19 -18
  44. package/dist/memory-index-DhlmbqWx.mjs.map +1 -0
  45. package/dist/{message-BMfreCQc.mjs → message-D6k16gtg.mjs} +20 -20
  46. package/dist/message-D6k16gtg.mjs.map +1 -0
  47. package/dist/{verifier-DhAbqVJF.d.mts → message-u_7KOq6Q.d.mts} +3 -22
  48. package/dist/{nip17-BZvFQLcs.d.mts → nip17--YwFSuph.d.mts} +4 -4
  49. package/dist/{nip19-D4RKn6GC.d.mts → nip19-qtyc5sCl.d.mts} +2 -2
  50. package/dist/{nip46-i8L8_nIm.d.mts → nip46-C_ZFQprM.d.mts} +2 -2
  51. package/dist/{nip51-C_kTYcud.d.mts → nip51-yi9w8FsM.d.mts} +2 -2
  52. package/dist/{nip59-DfJ6Moeu.d.mts → nip59-B6Ey8L9z.d.mts} +12 -3
  53. package/dist/{nip65-CPNE0SV0.d.mts → nip65-XrBjV9WM.d.mts} +2 -2
  54. package/dist/{nip77-DhpoL7d8.d.mts → nip77-BAXijIt_.d.mts} +2 -2
  55. package/dist/nips/blossom.d.mts +3 -2
  56. package/dist/nips/blossom.mjs +11 -11
  57. package/dist/nips/blossom.mjs.map +1 -1
  58. package/dist/nips/nip04.d.mts +3 -2
  59. package/dist/nips/nip04.mjs +10 -12
  60. package/dist/nips/nip04.mjs.map +1 -1
  61. package/dist/nips/nip05.d.mts +2 -2
  62. package/dist/nips/nip05.mjs +9 -8
  63. package/dist/nips/nip05.mjs.map +1 -1
  64. package/dist/nips/nip10.d.mts +3 -2
  65. package/dist/nips/nip10.mjs +19 -19
  66. package/dist/nips/nip10.mjs.map +1 -1
  67. package/dist/nips/nip11.d.mts +2 -1
  68. package/dist/nips/nip11.mjs +6 -4
  69. package/dist/nips/nip11.mjs.map +1 -1
  70. package/dist/nips/nip13.d.mts +1 -1
  71. package/dist/nips/nip13.mjs +2 -2
  72. package/dist/nips/nip13.mjs.map +1 -1
  73. package/dist/nips/nip17.d.mts +1 -1
  74. package/dist/nips/nip17.mjs +6 -6
  75. package/dist/nips/nip17.mjs.map +1 -1
  76. package/dist/nips/nip19.d.mts +1 -1
  77. package/dist/nips/nip19.mjs +1 -1
  78. package/dist/nips/nip21.d.mts +2 -2
  79. package/dist/nips/nip21.mjs +3 -3
  80. package/dist/nips/nip21.mjs.map +1 -1
  81. package/dist/nips/nip27.d.mts +3 -3
  82. package/dist/nips/nip27.mjs +308 -85
  83. package/dist/nips/nip27.mjs.map +1 -1
  84. package/dist/nips/nip42.mjs +1 -1
  85. package/dist/nips/nip44.d.mts +2 -1
  86. package/dist/nips/nip44.mjs +4 -4
  87. package/dist/nips/nip44.mjs.map +1 -1
  88. package/dist/nips/nip46.d.mts +1 -1
  89. package/dist/nips/nip46.mjs +9 -8
  90. package/dist/nips/nip46.mjs.map +1 -1
  91. package/dist/nips/nip49.d.mts +1 -1
  92. package/dist/nips/nip49.mjs +1 -1
  93. package/dist/nips/nip51.d.mts +1 -1
  94. package/dist/nips/nip51.mjs +14 -14
  95. package/dist/nips/nip51.mjs.map +1 -1
  96. package/dist/nips/nip57.mjs +10 -10
  97. package/dist/nips/nip57.mjs.map +1 -1
  98. package/dist/nips/nip59.d.mts +1 -1
  99. package/dist/nips/nip59.mjs +14 -11
  100. package/dist/nips/nip59.mjs.map +1 -1
  101. package/dist/nips/nip65.d.mts +1 -1
  102. package/dist/nips/nip65.mjs +2 -2
  103. package/dist/nips/nip77.d.mts +1 -1
  104. package/dist/nips/nip77.mjs +2 -2
  105. package/dist/nips/nip77.mjs.map +1 -1
  106. package/dist/nips/nip98.d.mts +1 -1
  107. package/dist/nips/nip98.mjs +5 -5
  108. package/dist/nips/nip98.mjs.map +1 -1
  109. package/dist/{pool-MehgIIzm.d.mts → pool-BMfTQZjM.d.mts} +5 -4
  110. package/dist/{pool-lMT_mvzn.mjs → pool-D13eVCqc.mjs} +5 -5
  111. package/dist/{pool-lMT_mvzn.mjs.map → pool-D13eVCqc.mjs.map} +1 -1
  112. package/dist/{put-BEM5j8xK.mjs → put-DuymazkT.mjs} +5 -5
  113. package/dist/put-DuymazkT.mjs.map +1 -0
  114. package/dist/{reactive-B7lPFgTS.d.mts → reactive-BkzOsSwU.d.mts} +3 -3
  115. package/dist/{reactive-DFHY-f00.mjs → reactive-C0-rTqN9.mjs} +10 -9
  116. package/dist/reactive-C0-rTqN9.mjs.map +1 -0
  117. package/dist/{relay-DIR77xQ2.mjs → relay-CbgToreY.mjs} +6 -6
  118. package/dist/{relay-DIR77xQ2.mjs.map → relay-CbgToreY.mjs.map} +1 -1
  119. package/dist/relay.d.mts +3 -3
  120. package/dist/relay.mjs +2 -2
  121. package/dist/{signer-Bha9gQy8.mjs → signer-DQzjm3pt.mjs} +28 -14
  122. package/dist/signer-DQzjm3pt.mjs.map +1 -0
  123. package/dist/signer.d.mts +2 -2
  124. package/dist/signer.mjs +2 -2
  125. package/dist/{storage-n2BE1bdY.mjs → storage-DvDAuxzC.mjs} +26 -41
  126. package/dist/storage-DvDAuxzC.mjs.map +1 -0
  127. package/dist/storage.d.mts +3 -3
  128. package/dist/storage.mjs +4 -4
  129. package/dist/store.d.mts +1 -1
  130. package/dist/store.mjs +1 -2
  131. package/dist/testing.d.mts +2 -2
  132. package/dist/testing.mjs +7 -7
  133. package/dist/{types-DiVVyf1e.d.mts → types-COupAqtN.d.mts} +2 -2
  134. package/dist/{types-BJ4wZ7JJ.d.mts → types-DF8yT6EG.d.mts} +7 -1
  135. package/dist/{util-DfsyJ9nz.mjs → util-BcRBO6o-.mjs} +18 -3
  136. package/dist/util-BcRBO6o-.mjs.map +1 -0
  137. package/dist/{verifier-DEHUy4AD.mjs → verifier-DBSPHzGg.mjs} +6 -15
  138. package/dist/verifier-DBSPHzGg.mjs.map +1 -0
  139. package/dist/verifier-Or2nDvHb.d.mts +16 -0
  140. package/package.json +3 -3
  141. package/dist/client-COkEihj_.mjs.map +0 -1
  142. package/dist/event-DvM1f03b.mjs.map +0 -1
  143. package/dist/filter-3jOh4uLN.mjs.map +0 -1
  144. package/dist/gossip-CRyhK6E6.mjs.map +0 -1
  145. package/dist/key-D_oEutQ6.mjs.map +0 -1
  146. package/dist/loaders-BRh2riV9.mjs.map +0 -1
  147. package/dist/memory-index-3GSrPIWj.mjs.map +0 -1
  148. package/dist/message-BMfreCQc.mjs.map +0 -1
  149. package/dist/put-BEM5j8xK.mjs.map +0 -1
  150. package/dist/reactive-DFHY-f00.mjs.map +0 -1
  151. package/dist/signer-Bha9gQy8.mjs.map +0 -1
  152. package/dist/storage-n2BE1bdY.mjs.map +0 -1
  153. package/dist/util-DfsyJ9nz.mjs.map +0 -1
  154. package/dist/verifier-DEHUy4AD.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"nip44.mjs","names":["hexToBytes","hkdf_extract","hkdf_expand"],"sources":["../../src/nips/nip44.ts"],"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, randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { CryptoError } from \"../core/error.ts\";\nimport {\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\nconst minPlaintextSize = 0x0001;\nconst maxPlaintextSize = 0xffffffff;\nconst extendedPrefixThreshold = 0x10000;\n\n/**\n * Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000\n * bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +\n * mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap\n * events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for\n * large local payloads.\n */\nexport const DEFAULT_MAX_PAYLOAD_CHARS: number = Math.ceil((71 + calcPaddedLen(0x100000)) / 3) * 4;\n\nfunction assert32(bytes: Uint8Array, label: string): void {\n if (bytes.length !== 32) {\n throw new CryptoError(`${label} must be 32 bytes`);\n }\n}\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n assertSecretKeyBytes(privkeyA);\n assertHex32(pubkeyB, \"public key\");\n const sharedX = secp256k1\n .getSharedSecret(privkeyA, hexToBytes(`02${pubkeyB.toLowerCase()}`))\n .subarray(1, 33);\n return getConversationKeyFromSharedSecret(sharedX);\n}\n\n/**\n * NIP-44 conversation key from the raw 32-byte ECDH x-coordinate instead of a secret key, for key\n * holders that only expose ECDH ({@link NostrKeyOperations}).\n */\nexport function getConversationKeyFromSharedSecret(sharedX: Uint8Array): Uint8Array {\n assert32(sharedX, \"shared secret\");\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode(\"nip44-v2\"));\n}\n\n/** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */\nexport function getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n assert32(conversationKey, \"conversation_key\");\n assert32(nonce, \"nonce\");\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\nexport function calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) {\n throw new CryptoError(\"expected positive integer\");\n }\n if (len <= 32) {\n return 32;\n }\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 CryptoError(\"invalid plaintext size: must be between 1 and 65535 bytes\");\n }\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 CryptoError(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");\n }\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 CryptoError(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");\n }\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen))\n : writeU16BE(unpaddedLen);\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 unpaddedLen = dv.getUint32(2);\n if (unpaddedLen < extendedPrefixThreshold) {\n throw new CryptoError(\"invalid padding\");\n }\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 CryptoError(\"invalid padding\");\n }\n return utf8Decoder.decode(unpadded);\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) {\n throw new CryptoError(\"AAD associated data must be 32 bytes\");\n }\n return hmac(sha256, key, concatBytes(aad, message));\n}\n\nfunction decodePayload(\n payload: string,\n maxPayloadChars: number,\n): {\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n mac: Uint8Array;\n} {\n if (typeof payload !== \"string\") {\n throw new CryptoError(\"payload must be a valid string\");\n }\n if (payload.startsWith(\"#\")) {\n throw new CryptoError(\"unknown encryption version\");\n }\n if (payload.length < 132 || payload.length > maxPayloadChars) {\n throw new CryptoError(`invalid payload length: ${payload.length}`);\n }\n let data: Uint8Array;\n try {\n data = base64.decode(payload);\n } catch (error) {\n throw new CryptoError(\"invalid base64\", { cause: error });\n }\n if (data.length < 99) {\n throw new CryptoError(`invalid data length: ${data.length}`);\n }\n const version = data.at(0);\n if (version === undefined) {\n throw new CryptoError(\"invalid data length\");\n }\n if (version !== 2) {\n throw new CryptoError(`unknown encryption version ${version}`);\n }\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(\n plaintext: string,\n conversationKey: Uint8Array,\n nonce: Uint8Array = randomBytes(32),\n): 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\nexport function decrypt(\n payload: string,\n conversationKey: Uint8Array,\n opts?: { maxPayloadChars?: number },\n): string {\n const { nonce, ciphertext, mac } = decodePayload(\n payload,\n opts?.maxPayloadChars ?? DEFAULT_MAX_PAYLOAD_CHARS,\n );\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);\n if (!equalBytes(calculatedMac, mac)) {\n throw new CryptoError(\"invalid MAC\");\n }\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext);\n return unpad(padded);\n}\n\n/** Convenience: derive conversation key from secret/public pair and encrypt. */\nexport function encryptToPubkey(\n plaintext: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));\n}\n\n/** Convenience: decrypt a payload from a peer pubkey. */\nexport function decryptFromPubkey(\n payload: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n opts?: { maxPayloadChars?: number },\n): string {\n return decrypt(payload, getConversationKey(secretKey, peerPubkey), opts);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip44.mjs","names":["hexToBytes","hkdf_extract","hkdf_expand"],"sources":["../../src/nips/nip44.ts"],"sourcesContent":["import { chacha20 } from \"@noble/ciphers/chacha.js\";\nimport { equalBytes } from \"@noble/ciphers/utils.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, randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { CryptoError } from \"../core/error.ts\";\nimport { nobleBackend } from \"../core/key.ts\";\nimport type { NostrBackend } from \"../core/key.ts\";\nimport {\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n lowerAscii,\n} from \"../core/util.ts\";\n\nconst minPlaintextSize = 0x0001;\nconst maxPlaintextSize = 0xffffffff;\nconst extendedPrefixThreshold = 0x10000;\n\n/**\n * Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000\n * bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +\n * mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap\n * events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for\n * large local payloads.\n */\nexport const DEFAULT_MAX_PAYLOAD_CHARS: number = Math.ceil((71 + calcPaddedLen(0x100000)) / 3) * 4;\n\nfunction assert32(bytes: Uint8Array, label: string): void {\n if (bytes.length !== 32) {\n throw new CryptoError(`${label} must be 32 bytes`);\n }\n}\n\nexport function getConversationKey(\n privkeyA: Uint8Array,\n pubkeyB: string,\n backend: NostrBackend = nobleBackend,\n): Uint8Array {\n assertSecretKeyBytes(privkeyA);\n assertHex32(pubkeyB, \"public key\");\n const sharedX = backend.ecdhX(privkeyA, hexToBytes(lowerAscii(pubkeyB)));\n return getConversationKeyFromSharedSecret(sharedX);\n}\n\n/**\n * NIP-44 conversation key from the raw 32-byte ECDH x-coordinate instead of a secret key, for key\n * holders that only expose ECDH ({@link NostrKeyOperations}).\n */\nexport function getConversationKeyFromSharedSecret(sharedX: Uint8Array): Uint8Array {\n assert32(sharedX, \"shared secret\");\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode(\"nip44-v2\"));\n}\n\n/** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */\nexport function getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n assert32(conversationKey, \"conversation_key\");\n assert32(nonce, \"nonce\");\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\nexport function calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) {\n throw new CryptoError(\"expected positive integer\");\n }\n if (len <= 32) {\n return 32;\n }\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 CryptoError(\"invalid plaintext size: must be between 1 and 65535 bytes\");\n }\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 CryptoError(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");\n }\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 CryptoError(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");\n }\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen))\n : writeU16BE(unpaddedLen);\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 unpaddedLen = dv.getUint32(2);\n if (unpaddedLen < extendedPrefixThreshold) {\n throw new CryptoError(\"invalid padding\");\n }\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 CryptoError(\"invalid padding\");\n }\n return utf8Decoder.decode(unpadded);\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) {\n throw new CryptoError(\"AAD associated data must be 32 bytes\");\n }\n return hmac(sha256, key, concatBytes(aad, message));\n}\n\nfunction decodePayload(\n payload: string,\n maxPayloadChars: number,\n): {\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n mac: Uint8Array;\n} {\n if (typeof payload !== \"string\") {\n throw new CryptoError(\"payload must be a valid string\");\n }\n if (payload.startsWith(\"#\")) {\n throw new CryptoError(\"unknown encryption version\");\n }\n if (payload.length < 132 || payload.length > maxPayloadChars) {\n throw new CryptoError(`invalid payload length: ${payload.length}`);\n }\n let data: Uint8Array;\n try {\n data = base64.decode(payload);\n } catch (error) {\n throw new CryptoError(\"invalid base64\", { cause: error });\n }\n if (data.length < 99) {\n throw new CryptoError(`invalid data length: ${data.length}`);\n }\n const version = data.at(0);\n if (version === undefined) {\n throw new CryptoError(\"invalid data length\");\n }\n if (version !== 2) {\n throw new CryptoError(`unknown encryption version ${version}`);\n }\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(\n plaintext: string,\n conversationKey: Uint8Array,\n nonce: Uint8Array = randomBytes(32),\n): 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\nexport function decrypt(\n payload: string,\n conversationKey: Uint8Array,\n opts?: { maxPayloadChars?: number },\n): string {\n const { nonce, ciphertext, mac } = decodePayload(\n payload,\n opts?.maxPayloadChars ?? DEFAULT_MAX_PAYLOAD_CHARS,\n );\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);\n if (!equalBytes(calculatedMac, mac)) {\n throw new CryptoError(\"invalid MAC\");\n }\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext);\n return unpad(padded);\n}\n\n/** Convenience: derive conversation key from secret/public pair and encrypt. */\nexport function encryptToPubkey(\n plaintext: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));\n}\n\n/** Convenience: decrypt a payload from a peer pubkey. */\nexport function decryptFromPubkey(\n payload: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n opts?: { maxPayloadChars?: number },\n): string {\n return decrypt(payload, getConversationKey(secretKey, peerPubkey), opts);\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { a as Nip46Response, c as decodeNip46Request, d as encodeNip46Response, f as parseBunkerURL, i as Nip46Request, l as decodeNip46Response, m as toBunkerURL, n as ClientMetadata, o as NostrConnectParams, p as parseNostrConnectURI, r as Nip46Error, s as createNostrConnectURI, t as BunkerPointer, u as encodeNip46Request } from "../nip46-i8L8_nIm.mjs";
1
+ import { a as Nip46Response, c as decodeNip46Request, d as encodeNip46Response, f as parseBunkerURL, i as Nip46Request, l as decodeNip46Response, m as toBunkerURL, n as ClientMetadata, o as NostrConnectParams, p as parseNostrConnectURI, r as Nip46Error, s as createNostrConnectURI, t as BunkerPointer, u as encodeNip46Request } from "../nip46-C_ZFQprM.mjs";
2
2
  export { BunkerPointer, ClientMetadata, Nip46Error, Nip46Request, Nip46Response, NostrConnectParams, createNostrConnectURI, decodeNip46Request, decodeNip46Response, encodeNip46Request, encodeNip46Response, parseBunkerURL, parseNostrConnectURI, toBunkerURL };
@@ -1,4 +1,4 @@
1
- import { a as containsLoneSurrogate, c as isHex32, u as isRecord, x as NostrError } from "../util-DfsyJ9nz.mjs";
1
+ import { a as containsLoneSurrogate, c as isHex32, d as lowerAscii, g as trimAscii, u as isRecord, w as NostrError } from "../util-BcRBO6o-.mjs";
2
2
  //#region src/nips/nip46.ts
3
3
  /**
4
4
  * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.
@@ -23,22 +23,23 @@ function toBunkerURL(pointer) {
23
23
  function parseBunkerURL(input) {
24
24
  let url;
25
25
  try {
26
- url = new URL(input.trim());
26
+ url = new URL(trimAscii(input));
27
27
  } catch {
28
28
  return;
29
29
  }
30
30
  if (url.protocol !== "bunker:") return;
31
31
  const pubkey = url.hostname || url.pathname.replace(/^\/*/, "");
32
- if (!isHex32(pubkey.toLowerCase())) return;
32
+ if (!isHex32(pubkey)) return;
33
33
  return {
34
- pubkey: pubkey.toLowerCase(),
34
+ pubkey,
35
35
  relays: url.searchParams.getAll("relay"),
36
36
  secret: url.searchParams.get("secret") ?? void 0
37
37
  };
38
38
  }
39
39
  /** Build a client-initiated `nostrconnect://` URI. */
40
40
  function createNostrConnectURI(params) {
41
- if (!isHex32(params.clientPubkey.toLowerCase())) throw new Nip46Error("invalid client pubkey");
41
+ const clientPubkey = lowerAscii(params.clientPubkey);
42
+ if (!isHex32(clientPubkey)) throw new Nip46Error("invalid client pubkey");
42
43
  if (!params.secret) throw new Nip46Error("nostrconnect secret is required");
43
44
  if (params.relays.length === 0) throw new Nip46Error("at least one relay is required");
44
45
  const query = new URLSearchParams();
@@ -48,7 +49,7 @@ function createNostrConnectURI(params) {
48
49
  if (params.name !== void 0 && params.name !== "") query.set("name", params.name);
49
50
  if (params.url !== void 0 && params.url !== "") query.set("url", params.url);
50
51
  if (params.image !== void 0 && params.image !== "") query.set("image", params.image);
51
- return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;
52
+ return `nostrconnect://${clientPubkey}?${query.toString()}`;
52
53
  }
53
54
  /** Parse a `nostrconnect://` URI. */
54
55
  function parseNostrConnectURI(uri) {
@@ -60,13 +61,13 @@ function parseNostrConnectURI(uri) {
60
61
  }
61
62
  if (url.protocol !== "nostrconnect:") throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);
62
63
  const clientPubkey = url.hostname || url.pathname.replace(/^\/*/, "");
63
- if (!isHex32(clientPubkey.toLowerCase())) throw new Nip46Error("invalid client pubkey in nostrconnect URI");
64
+ if (!isHex32(clientPubkey)) throw new Nip46Error("invalid client pubkey in nostrconnect URI");
64
65
  const secret = url.searchParams.get("secret");
65
66
  if (secret === null || secret === "") throw new Nip46Error("missing secret in nostrconnect URI");
66
67
  const relays = url.searchParams.getAll("relay");
67
68
  if (relays.length === 0) throw new Nip46Error("missing relays in nostrconnect URI");
68
69
  const params = {
69
- clientPubkey: clientPubkey.toLowerCase(),
70
+ clientPubkey,
70
71
  relays,
71
72
  secret
72
73
  };
@@ -1 +1 @@
1
- {"version":3,"file":"nip46.mjs","names":[],"sources":["../../src/nips/nip46.ts"],"sourcesContent":["/**\n * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.\n * Transport/signing live in {@link Nip46Signer}.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/46.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport { containsLoneSurrogate, isHex32, isRecord } from \"../core/util.ts\";\n\nexport type BunkerPointer = {\n /** Remote signer / bunker public key (hex). */\n readonly pubkey: string;\n readonly relays: ReadonlyArray<string>;\n readonly secret?: string | undefined;\n};\n\nexport type ClientMetadata = {\n readonly name?: string | undefined;\n readonly url?: string | undefined;\n readonly image?: string | undefined;\n};\n\nexport type NostrConnectParams = {\n readonly clientPubkey: string;\n readonly relays: ReadonlyArray<string>;\n readonly secret: string;\n readonly perms?: ReadonlyArray<string> | undefined;\n} & ClientMetadata;\n\nexport type Nip46Request = {\n readonly id: string;\n readonly method: string;\n readonly params: ReadonlyArray<string>;\n};\n\nexport type Nip46Response = {\n readonly id: string;\n readonly result?: string | undefined;\n readonly error?: string | undefined;\n};\n\nexport class Nip46Error extends NostrError {\n override name = \"Nip46Error\";\n}\n\n/** Encode a bunker pointer as `bunker://…`. */\nexport function toBunkerURL(pointer: BunkerPointer): string {\n const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);\n for (const relay of pointer.relays) {\n url.searchParams.append(\"relay\", relay);\n }\n if (pointer.secret !== undefined && pointer.secret !== \"\") {\n url.searchParams.set(\"secret\", pointer.secret);\n }\n return url.toString();\n}\n\n/**\n * Parse a `bunker://` URL into a pointer. Returns undefined when the input is not a bunker URL\n * (including NIP-05 identifiers).\n */\nexport function parseBunkerURL(input: string): BunkerPointer | undefined {\n let url: URL;\n try {\n url = new URL(input.trim());\n } catch {\n return undefined;\n }\n if (url.protocol !== \"bunker:\") {\n return undefined;\n }\n const pubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(pubkey.toLowerCase())) {\n return undefined;\n }\n return {\n pubkey: pubkey.toLowerCase(),\n relays: url.searchParams.getAll(\"relay\"),\n secret: url.searchParams.get(\"secret\") ?? undefined,\n };\n}\n\n/** Build a client-initiated `nostrconnect://` URI. */\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n if (!isHex32(params.clientPubkey.toLowerCase())) {\n throw new Nip46Error(\"invalid client pubkey\");\n }\n if (!params.secret) {\n throw new Nip46Error(\"nostrconnect secret is required\");\n }\n if (params.relays.length === 0) {\n throw new Nip46Error(\"at least one relay is required\");\n }\n\n const query = new URLSearchParams();\n for (const relay of params.relays) {\n query.append(\"relay\", relay);\n }\n query.set(\"secret\", params.secret);\n if (params.perms !== undefined && params.perms.length > 0) {\n query.set(\"perms\", params.perms.join(\",\"));\n }\n if (params.name !== undefined && params.name !== \"\") {\n query.set(\"name\", params.name);\n }\n if (params.url !== undefined && params.url !== \"\") {\n query.set(\"url\", params.url);\n }\n if (params.image !== undefined && params.image !== \"\") {\n query.set(\"image\", params.image);\n }\n\n return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;\n}\n\n/** Parse a `nostrconnect://` URI. */\nexport function parseNostrConnectURI(uri: string): NostrConnectParams {\n let url: URL;\n try {\n url = new URL(uri);\n } catch {\n // No `cause`: Node's URL TypeError carries the rejected input, which may\n // hold the handshake `secret` — the URI is never echoed (N11).\n throw new Nip46Error(\"invalid nostrconnect URI\");\n }\n if (url.protocol !== \"nostrconnect:\") {\n throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);\n }\n const clientPubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(clientPubkey.toLowerCase())) {\n throw new Nip46Error(\"invalid client pubkey in nostrconnect URI\");\n }\n const secret = url.searchParams.get(\"secret\");\n if (secret === null || secret === \"\") {\n throw new Nip46Error(\"missing secret in nostrconnect URI\");\n }\n const relays = url.searchParams.getAll(\"relay\");\n if (relays.length === 0) {\n throw new Nip46Error(\"missing relays in nostrconnect URI\");\n }\n const params: Mutable<NostrConnectParams> = {\n clientPubkey: clientPubkey.toLowerCase(),\n relays,\n secret,\n };\n const permsRaw = url.searchParams.get(\"perms\");\n if (permsRaw !== null && permsRaw !== \"\") {\n params.perms = permsRaw.split(\",\").filter((p) => p !== \"\");\n }\n const name = url.searchParams.get(\"name\");\n if (name !== null) {\n params.name = name;\n }\n const metadataUrl = url.searchParams.get(\"url\");\n if (metadataUrl !== null) {\n params.url = metadataUrl;\n }\n const image = url.searchParams.get(\"image\");\n if (image !== null) {\n params.image = image;\n }\n return params;\n}\n\nexport function encodeNip46Request(req: Nip46Request): string {\n return JSON.stringify({ id: req.id, method: req.method, params: req.params });\n}\n\nexport function decodeNip46Request(json: string): Nip46Request {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch {\n // No `cause`: some runtimes embed an input excerpt in the SyntaxError and\n // RPC payloads may carry the handshake `secret` (N11).\n throw new Nip46Error(\"invalid NIP-46 request JSON\");\n }\n if (containsLoneSurrogate(data)) {\n throw new Nip46Error(\"invalid NIP-46 request JSON\");\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\" || typeof data[\"method\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 request shape\");\n }\n if (!Array.isArray(data[\"params\"]) || !data[\"params\"].every((p) => typeof p === \"string\")) {\n throw new Nip46Error(\"invalid NIP-46 request params\");\n }\n return { id: data[\"id\"], method: data[\"method\"], params: data[\"params\"] };\n}\n\nexport function encodeNip46Response(res: Nip46Response): string {\n const body: Record<string, string> = { id: res.id };\n if (res.result !== undefined) {\n body[\"result\"] = res.result;\n }\n if (res.error !== undefined) {\n body[\"error\"] = res.error;\n }\n return JSON.stringify(body);\n}\n\nexport function decodeNip46Response(json: string): Nip46Response {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch {\n // No `cause`: same N11 reasoning as the request decoder.\n throw new Nip46Error(\"invalid NIP-46 response JSON\");\n }\n if (containsLoneSurrogate(data)) {\n throw new Nip46Error(\"invalid NIP-46 response JSON\");\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response shape\");\n }\n // Some remote signers send explicit nulls for absent fields.\n const result = data[\"result\"] === null ? undefined : data[\"result\"];\n const error = data[\"error\"] === null ? undefined : data[\"error\"];\n if (result !== undefined && typeof result !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response result\");\n }\n if (error !== undefined && typeof error !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response error\");\n }\n const response: Mutable<Nip46Response> = { id: data[\"id\"] };\n if (result !== undefined) {\n response.result = result;\n }\n if (error !== undefined) {\n response.error = error;\n }\n return response;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip46.mjs","names":[],"sources":["../../src/nips/nip46.ts"],"sourcesContent":["/**\n * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.\n * Transport/signing live in {@link Nip46Signer}.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/46.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport { containsLoneSurrogate, isHex32, isRecord, lowerAscii, trimAscii } from \"../core/util.ts\";\n\nexport type BunkerPointer = {\n /** Remote signer / bunker public key (hex). */\n readonly pubkey: string;\n readonly relays: ReadonlyArray<string>;\n readonly secret?: string | undefined;\n};\n\nexport type ClientMetadata = {\n readonly name?: string | undefined;\n readonly url?: string | undefined;\n readonly image?: string | undefined;\n};\n\nexport type NostrConnectParams = {\n readonly clientPubkey: string;\n readonly relays: ReadonlyArray<string>;\n readonly secret: string;\n readonly perms?: ReadonlyArray<string> | undefined;\n} & ClientMetadata;\n\nexport type Nip46Request = {\n readonly id: string;\n readonly method: string;\n readonly params: ReadonlyArray<string>;\n};\n\nexport type Nip46Response = {\n readonly id: string;\n readonly result?: string | undefined;\n readonly error?: string | undefined;\n};\n\nexport class Nip46Error extends NostrError {\n override name = \"Nip46Error\";\n}\n\n/** Encode a bunker pointer as `bunker://…`. */\nexport function toBunkerURL(pointer: BunkerPointer): string {\n const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);\n for (const relay of pointer.relays) {\n url.searchParams.append(\"relay\", relay);\n }\n if (pointer.secret !== undefined && pointer.secret !== \"\") {\n url.searchParams.set(\"secret\", pointer.secret);\n }\n return url.toString();\n}\n\n/**\n * Parse a `bunker://` URL into a pointer. Returns undefined when the input is not a bunker URL\n * (including NIP-05 identifiers).\n */\nexport function parseBunkerURL(input: string): BunkerPointer | undefined {\n let url: URL;\n try {\n url = new URL(trimAscii(input));\n } catch {\n return undefined;\n }\n if (url.protocol !== \"bunker:\") {\n return undefined;\n }\n const pubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(pubkey)) {\n return undefined;\n }\n return {\n pubkey,\n relays: url.searchParams.getAll(\"relay\"),\n secret: url.searchParams.get(\"secret\") ?? undefined,\n };\n}\n\n/** Build a client-initiated `nostrconnect://` URI. */\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n const clientPubkey = lowerAscii(params.clientPubkey);\n if (!isHex32(clientPubkey)) {\n throw new Nip46Error(\"invalid client pubkey\");\n }\n if (!params.secret) {\n throw new Nip46Error(\"nostrconnect secret is required\");\n }\n if (params.relays.length === 0) {\n throw new Nip46Error(\"at least one relay is required\");\n }\n\n const query = new URLSearchParams();\n for (const relay of params.relays) {\n query.append(\"relay\", relay);\n }\n query.set(\"secret\", params.secret);\n if (params.perms !== undefined && params.perms.length > 0) {\n query.set(\"perms\", params.perms.join(\",\"));\n }\n if (params.name !== undefined && params.name !== \"\") {\n query.set(\"name\", params.name);\n }\n if (params.url !== undefined && params.url !== \"\") {\n query.set(\"url\", params.url);\n }\n if (params.image !== undefined && params.image !== \"\") {\n query.set(\"image\", params.image);\n }\n\n return `nostrconnect://${clientPubkey}?${query.toString()}`;\n}\n\n/** Parse a `nostrconnect://` URI. */\nexport function parseNostrConnectURI(uri: string): NostrConnectParams {\n let url: URL;\n try {\n url = new URL(uri);\n } catch {\n // No `cause`: Node's URL TypeError carries the rejected input, which may\n // hold the handshake `secret` — the URI is never echoed (N11).\n throw new Nip46Error(\"invalid nostrconnect URI\");\n }\n if (url.protocol !== \"nostrconnect:\") {\n throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);\n }\n const clientPubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(clientPubkey)) {\n throw new Nip46Error(\"invalid client pubkey in nostrconnect URI\");\n }\n const secret = url.searchParams.get(\"secret\");\n if (secret === null || secret === \"\") {\n throw new Nip46Error(\"missing secret in nostrconnect URI\");\n }\n const relays = url.searchParams.getAll(\"relay\");\n if (relays.length === 0) {\n throw new Nip46Error(\"missing relays in nostrconnect URI\");\n }\n const params: Mutable<NostrConnectParams> = {\n clientPubkey,\n relays,\n secret,\n };\n const permsRaw = url.searchParams.get(\"perms\");\n if (permsRaw !== null && permsRaw !== \"\") {\n params.perms = permsRaw.split(\",\").filter((p) => p !== \"\");\n }\n const name = url.searchParams.get(\"name\");\n if (name !== null) {\n params.name = name;\n }\n const metadataUrl = url.searchParams.get(\"url\");\n if (metadataUrl !== null) {\n params.url = metadataUrl;\n }\n const image = url.searchParams.get(\"image\");\n if (image !== null) {\n params.image = image;\n }\n return params;\n}\n\nexport function encodeNip46Request(req: Nip46Request): string {\n return JSON.stringify({ id: req.id, method: req.method, params: req.params });\n}\n\nexport function decodeNip46Request(json: string): Nip46Request {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch {\n // No `cause`: some runtimes embed an input excerpt in the SyntaxError and\n // RPC payloads may carry the handshake `secret` (N11).\n throw new Nip46Error(\"invalid NIP-46 request JSON\");\n }\n if (containsLoneSurrogate(data)) {\n throw new Nip46Error(\"invalid NIP-46 request JSON\");\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\" || typeof data[\"method\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 request shape\");\n }\n if (!Array.isArray(data[\"params\"]) || !data[\"params\"].every((p) => typeof p === \"string\")) {\n throw new Nip46Error(\"invalid NIP-46 request params\");\n }\n return { id: data[\"id\"], method: data[\"method\"], params: data[\"params\"] };\n}\n\nexport function encodeNip46Response(res: Nip46Response): string {\n const body: Record<string, string> = { id: res.id };\n if (res.result !== undefined) {\n body[\"result\"] = res.result;\n }\n if (res.error !== undefined) {\n body[\"error\"] = res.error;\n }\n return JSON.stringify(body);\n}\n\nexport function decodeNip46Response(json: string): Nip46Response {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch {\n // No `cause`: same N11 reasoning as the request decoder.\n throw new Nip46Error(\"invalid NIP-46 response JSON\");\n }\n if (containsLoneSurrogate(data)) {\n throw new Nip46Error(\"invalid NIP-46 response JSON\");\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response shape\");\n }\n // Some remote signers send explicit nulls for absent fields.\n const result = data[\"result\"] === null ? undefined : data[\"result\"];\n const error = data[\"error\"] === null ? undefined : data[\"error\"];\n if (result !== undefined && typeof result !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response result\");\n }\n if (error !== undefined && typeof error !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response error\");\n }\n const response: Mutable<Nip46Response> = { id: data[\"id\"] };\n if (result !== undefined) {\n response.result = result;\n }\n if (error !== undefined) {\n response.error = error;\n }\n return response;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as NostrError } from "../error-BPqAU5RB.mjs";
1
+ import { o as NostrError } from "../error-DE1UCZ2J.mjs";
2
2
  //#region src/nips/nip49.d.ts
3
3
  export type Ncryptsec = `ncryptsec1${string}`;
4
4
  export type KeySecurityByte = 0x00 | 0x01 | 0x02;
@@ -1,4 +1,4 @@
1
- import { g as utf8Encoder, r as assertSecretKeyBytes, x as NostrError } from "../util-DfsyJ9nz.mjs";
1
+ import { r as assertSecretKeyBytes, v as utf8Encoder, w as NostrError } from "../util-BcRBO6o-.mjs";
2
2
  import { Bech32MaxSize, encodeBytes } from "./nip19.mjs";
3
3
  import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
4
4
  import { secp256k1 } from "@noble/curves/secp256k1.js";
@@ -1,2 +1,2 @@
1
- import { a as encryptPrivateTags, c as parseEmojiSet, d as parseMuteList, f as parseMuteListPrivate, g as pinListEventBuilder, h as parseUserEmojiList, i as decryptPrivateTags, l as parseFavoriteRelays, m as parseRelaySet, n as Nip51Crypto, o as muteListEventBuilder, p as parsePinList, r as bookmarkListEventBuilder, s as parseBookmarkList, t as MuteItem, u as parseFollowPack } from "../nip51-C_kTYcud.mjs";
1
+ import { a as encryptPrivateTags, c as parseEmojiSet, d as parseMuteList, f as parseMuteListPrivate, g as pinListEventBuilder, h as parseUserEmojiList, i as decryptPrivateTags, l as parseFavoriteRelays, m as parseRelaySet, n as Nip51Crypto, o as muteListEventBuilder, p as parsePinList, r as bookmarkListEventBuilder, s as parseBookmarkList, t as MuteItem, u as parseFollowPack } from "../nip51-yi9w8FsM.mjs";
2
2
  export { MuteItem, Nip51Crypto, bookmarkListEventBuilder, decryptPrivateTags, encryptPrivateTags, muteListEventBuilder, parseBookmarkList, parseEmojiSet, parseFavoriteRelays, parseFollowPack, parseMuteList, parseMuteListPrivate, parsePinList, parseRelaySet, parseUserEmojiList, pinListEventBuilder };
@@ -1,7 +1,7 @@
1
- import { a as containsLoneSurrogate, c as isHex32, d as normalizeRelayUrls, n as assertHex32, v as EventValidationError } from "../util-DfsyJ9nz.mjs";
1
+ import { a as containsLoneSurrogate, b as EventValidationError, c as isHex32, d as lowerAscii, f as normalizeRelayUrls, n as assertHex32 } from "../util-BcRBO6o-.mjs";
2
2
  import { t as Kind } from "../kind-I2Tvbe82.mjs";
3
- import { b as getDTag, g as Tag, v as firstTagValue, x as isTag } from "../event-DvM1f03b.mjs";
4
- import { t as EventBuilder } from "../builder-CGgM_QHT.mjs";
3
+ import { b as getDTag, g as Tag, v as firstTagValue, x as isTag } from "../event-DHtcm0jM.mjs";
4
+ import { t as EventBuilder } from "../builder-70CC2EBq.mjs";
5
5
  //#region src/nips/nip51.ts
6
6
  /**
7
7
  * NIP-51: Lists Public tags plus NIP-44 private `.content` (author encrypts to self). Crypto is
@@ -47,7 +47,7 @@ function isRelaySetAddress(value) {
47
47
  if (pkSep === -1) return false;
48
48
  const pubkey = value.slice(kindSep + 1, pkSep);
49
49
  const d = value.slice(pkSep + 1);
50
- return isHex32(pubkey.toLowerCase()) && d.length > 0;
50
+ return isHex32(pubkey) && d.length > 0;
51
51
  }
52
52
  /** Parse kind:10000 mute list public tags (`p` / `e` / `t` / `word`). */
53
53
  function parseMuteList(event) {
@@ -58,15 +58,15 @@ function parseMuteList(event) {
58
58
  if (value === void 0 || value === "") continue;
59
59
  switch (tag[0]) {
60
60
  case "p":
61
- if (isHex32(value.toLowerCase())) items.push({
61
+ if (isHex32(value)) items.push({
62
62
  type: "pubkey",
63
- value: value.toLowerCase()
63
+ value
64
64
  });
65
65
  break;
66
66
  case "e":
67
- if (isHex32(value.toLowerCase())) items.push({
67
+ if (isHex32(value)) items.push({
68
68
  type: "event",
69
- value: value.toLowerCase()
69
+ value
70
70
  });
71
71
  break;
72
72
  case "t":
@@ -109,7 +109,7 @@ async function encryptPrivateTags(crypto, tags) {
109
109
  async function decryptPrivateTags(crypto, event) {
110
110
  if (event.content === "") return [];
111
111
  const self = await crypto.getPublicKey();
112
- if (self.toLowerCase() !== event.pubkey.toLowerCase()) throw new EventValidationError("NIP-51 private content is only for the author");
112
+ if (lowerAscii(self) !== event.pubkey) throw new EventValidationError("NIP-51 private content is only for the author");
113
113
  const plaintext = await crypto.nip44Decrypt(self, event.content);
114
114
  let parsed;
115
115
  try {
@@ -138,8 +138,8 @@ function parsePinList(event) {
138
138
  const ids = [];
139
139
  for (const tag of event.tags) {
140
140
  const value = tag.at(1);
141
- if (tag[0] !== "e" || value === void 0 || !isHex32(value.toLowerCase())) continue;
142
- ids.push(value.toLowerCase());
141
+ if (tag[0] !== "e" || value === void 0 || !isHex32(value)) continue;
142
+ ids.push(value);
143
143
  }
144
144
  return ids;
145
145
  }
@@ -158,7 +158,7 @@ function parseBookmarkList(event) {
158
158
  const value = tag.at(1);
159
159
  if (value === void 0 || value === "") continue;
160
160
  if (tag[0] === "e") {
161
- if (isHex32(value.toLowerCase())) e.push(value.toLowerCase());
161
+ if (isHex32(value)) e.push(value);
162
162
  } else if (tag[0] === "a") a.push(value);
163
163
  }
164
164
  return {
@@ -227,8 +227,8 @@ function parseFollowPack(event) {
227
227
  const pubkeys = [];
228
228
  for (const tag of event.tags) {
229
229
  const value = tag.at(1);
230
- if (tag[0] !== "p" || value === void 0 || !isHex32(value.toLowerCase())) continue;
231
- pubkeys.push(value.toLowerCase());
230
+ if (tag[0] !== "p" || value === void 0 || !isHex32(value)) continue;
231
+ pubkeys.push(value);
232
232
  }
233
233
  return {
234
234
  d: identifier(event.tags),
@@ -1 +1 @@
1
- {"version":3,"file":"nip51.mjs","names":[],"sources":["../../src/nips/nip51.ts"],"sourcesContent":["/**\n * NIP-51: Lists Public tags plus NIP-44 private `.content` (author encrypts to self). Crypto is\n * injected; this module does not import signer. No NIP-04 sniff. Kind 10063 Blossom servers live in\n * blossom.ts.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/51.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { firstTagValue, getDTag, isTag, Tag } from \"../core/tag.ts\";\nimport { assertHex32, containsLoneSurrogate, isHex32, normalizeRelayUrls } from \"../core/util.ts\";\n\n/**\n * Structural crypto used by NIP-51 private tags. Satisfied by NostrSigner when\n * nip44Encrypt/nip44Decrypt are present. This module must not import src/signer/.\n */\nexport type Nip51Crypto = {\n getPublicKey: () => Promise<string>;\n nip44Encrypt: (peer: string, plaintext: string) => Promise<string>;\n nip44Decrypt: (peer: string, payload: string) => Promise<string>;\n};\n\nexport type MuteItem =\n | { readonly type: \"pubkey\"; readonly value: string }\n | { readonly type: \"event\"; readonly value: string }\n | { readonly type: \"hashtag\"; readonly value: string }\n | { readonly type: \"word\"; readonly value: string };\n\nfunction requireKind(event: Pick<Event, \"kind\">, kind: number): void {\n if (event.kind !== kind) {\n throw new EventValidationError(`expected kind ${kind}, got ${event.kind}`);\n }\n}\n\nfunction collectRelays(tags: Event[\"tags\"]): string[] {\n const urls: string[] = [];\n for (const tag of tags) {\n const value = tag.at(1);\n if (tag[0] === \"relay\" && value !== undefined) {\n urls.push(value);\n }\n }\n return normalizeRelayUrls(urls);\n}\n\nfunction collectEmoji(tags: Event[\"tags\"]): Array<{ shortcode: string; url: string }> {\n const out: Array<{ shortcode: string; url: string }> = [];\n for (const tag of tags) {\n if (tag[0] !== \"emoji\") {\n continue;\n }\n const shortcode = tag.at(1);\n const url = tag.at(2);\n if (shortcode === undefined || shortcode === \"\" || url === undefined || url === \"\") {\n continue;\n }\n out.push({ shortcode, url });\n }\n return out;\n}\n\nfunction identifier(tags: Event[\"tags\"]): string {\n return getDTag(tags) ?? \"\";\n}\n\n/** NIP-51 a-tag: `30002:<64-hex-pubkey>:<d>` with `d` possibly containing `:`. */\nfunction isRelaySetAddress(value: string): boolean {\n const kindSep = value.indexOf(\":\");\n if (kindSep === -1) {\n return false;\n }\n if (value.slice(0, kindSep) !== String(Kind.RelaySets)) {\n return false;\n }\n const pkSep = value.indexOf(\":\", kindSep + 1);\n if (pkSep === -1) {\n return false;\n }\n const pubkey = value.slice(kindSep + 1, pkSep);\n const d = value.slice(pkSep + 1);\n return isHex32(pubkey.toLowerCase()) && d.length > 0;\n}\n\n/** Parse kind:10000 mute list public tags (`p` / `e` / `t` / `word`). */\nexport function parseMuteList(event: Pick<Event, \"kind\" | \"tags\">): MuteItem[] {\n requireKind(event, Kind.MuteList);\n const items: MuteItem[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (value === undefined || value === \"\") {\n continue;\n }\n switch (tag[0]) {\n case \"p\":\n if (isHex32(value.toLowerCase())) {\n items.push({ type: \"pubkey\", value: value.toLowerCase() });\n }\n break;\n case \"e\":\n if (isHex32(value.toLowerCase())) {\n items.push({ type: \"event\", value: value.toLowerCase() });\n }\n break;\n case \"t\":\n // NIP-51 does not require hashtag case-folding.\n items.push({ type: \"hashtag\", value });\n break;\n case \"word\":\n items.push({ type: \"word\", value: value.toLowerCase() });\n break;\n default:\n break;\n }\n }\n return items;\n}\n\n/** Build an unsigned kind:10000 EventBuilder from public mute items. */\nexport function muteListEventBuilder(items: ReadonlyArray<MuteItem>): EventBuilder {\n const b = new EventBuilder(Kind.MuteList, \"\");\n for (const item of items) {\n switch (item.type) {\n case \"pubkey\":\n b.tag(Tag.p(assertHex32(item.value, \"public key\")));\n break;\n case \"event\":\n b.tag(Tag.e(assertHex32(item.value, \"event id\")));\n break;\n case \"hashtag\":\n if (item.value) {\n b.tag(Tag.t(item.value));\n }\n break;\n case \"word\":\n if (item.value) {\n b.tag([\"word\", item.value.toLowerCase()]);\n }\n break;\n }\n }\n return b;\n}\n\n/** NIP-44 ciphertext of tags, conversation peer = author's own pubkey. */\nexport async function encryptPrivateTags(\n crypto: Nip51Crypto,\n tags: Event[\"tags\"],\n): Promise<string> {\n const self = await crypto.getPublicKey();\n return crypto.nip44Encrypt(self, JSON.stringify(tags));\n}\n\n/** Decrypt NIP-51 `.content`. Empty content is no private tags, not ciphertext. */\nexport async function decryptPrivateTags(\n crypto: Nip51Crypto,\n event: Pick<Event, \"pubkey\" | \"content\">,\n): Promise<Event[\"tags\"]> {\n // NIP-44 rejects empty plaintext; empty `.content` means no private items.\n if (event.content === \"\") {\n return [];\n }\n const self = await crypto.getPublicKey();\n if (self.toLowerCase() !== event.pubkey.toLowerCase()) {\n throw new EventValidationError(\"NIP-51 private content is only for the author\");\n }\n const plaintext = await crypto.nip44Decrypt(self, event.content);\n let parsed: unknown;\n try {\n parsed = JSON.parse(plaintext);\n } catch (error) {\n throw new EventValidationError(\"invalid NIP-51 private tags\", { cause: error });\n }\n // Scan the parsed value before the shape check: `JSON.parse` accepts lone\n // surrogates via `\\ud800`-style escapes while serde_json rejects them, so\n // both sides must agree on the invalid-tags error (N10).\n if (containsLoneSurrogate(parsed) || !Array.isArray(parsed) || !parsed.every(isTag)) {\n throw new EventValidationError(\"invalid NIP-51 private tags\");\n }\n return parsed;\n}\n\n/** Public mute items from tags; private mute items from NIP-44 `.content`. */\nexport async function parseMuteListPrivate(\n crypto: Nip51Crypto,\n event: Pick<Event, \"kind\" | \"tags\" | \"pubkey\" | \"content\">,\n): Promise<{ public: MuteItem[]; private: MuteItem[] }> {\n const pub = parseMuteList(event);\n const decrypted = await decryptPrivateTags(crypto, event);\n return {\n public: pub,\n private: parseMuteList({ kind: event.kind, tags: decrypted }),\n };\n}\n\n/** Parse kind:10001 pin list public `e` tags. */\nexport function parsePinList(event: Pick<Event, \"kind\" | \"tags\">): string[] {\n requireKind(event, Kind.PinList);\n const ids: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"e\" || value === undefined || !isHex32(value.toLowerCase())) {\n continue;\n }\n ids.push(value.toLowerCase());\n }\n return ids;\n}\n\n/** Build an unsigned kind:10001 EventBuilder from event ids. */\nexport function pinListEventBuilder(ids: ReadonlyArray<string>): EventBuilder {\n const b = new EventBuilder(Kind.PinList, \"\");\n for (const id of ids) {\n b.tag(Tag.e(assertHex32(id, \"event id\")));\n }\n return b;\n}\n\n/** Parse kind:10003 bookmark list public `e` and `a` tags. */\nexport function parseBookmarkList(event: Pick<Event, \"kind\" | \"tags\">): {\n e: string[];\n a: string[];\n} {\n requireKind(event, Kind.BookmarkList);\n const e: string[] = [];\n const a: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (value === undefined || value === \"\") {\n continue;\n }\n if (tag[0] === \"e\") {\n if (isHex32(value.toLowerCase())) {\n e.push(value.toLowerCase());\n }\n } else if (tag[0] === \"a\") {\n a.push(value);\n }\n }\n return { e, a };\n}\n\n/** Build an unsigned kind:10003 EventBuilder from public bookmark pointers. */\nexport function bookmarkListEventBuilder(items: {\n e?: ReadonlyArray<string>;\n a?: ReadonlyArray<string>;\n}): EventBuilder {\n const b = new EventBuilder(Kind.BookmarkList, \"\");\n if (items.e) {\n for (const id of items.e) {\n b.tag(Tag.e(assertHex32(id, \"event id\")));\n }\n }\n if (items.a) {\n for (const coord of items.a) {\n if (coord) {\n b.tag(Tag.a(coord));\n }\n }\n }\n return b;\n}\n\n/** Parse kind:10030 user emoji list public `emoji` and `a` tags. */\nexport function parseUserEmojiList(event: Pick<Event, \"kind\" | \"tags\">): {\n emoji: Array<{ shortcode: string; url: string }>;\n sets: string[];\n} {\n requireKind(event, Kind.UserEmojiList);\n const sets: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] === \"a\" && value !== undefined && value !== \"\") {\n sets.push(value);\n }\n }\n return { emoji: collectEmoji(event.tags), sets };\n}\n\n/** Parse kind:30002 relay set public `d` and `relay` tags. */\nexport function parseRelaySet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n relays: string[];\n} {\n requireKind(event, Kind.RelaySets);\n return { d: identifier(event.tags), relays: collectRelays(event.tags) };\n}\n\n/** Parse kind:10012 favorite relays public `relay` tags and kind:30002 `a` tags. */\nexport function parseFavoriteRelays(event: Pick<Event, \"kind\" | \"tags\">): {\n relays: string[];\n sets: string[];\n} {\n requireKind(event, Kind.FavoriteRelays);\n const sets: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"a\" || value === undefined || !isRelaySetAddress(value)) {\n continue;\n }\n sets.push(value);\n }\n return { relays: collectRelays(event.tags), sets };\n}\n\n/** Parse kind:30030 emoji set public `d` / `title` / `emoji` tags. */\nexport function parseEmojiSet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n title?: string;\n emoji: Array<{ shortcode: string; url: string }>;\n} {\n requireKind(event, Kind.EmojiSet);\n const result: {\n d: string;\n title?: string;\n emoji: Array<{ shortcode: string; url: string }>;\n } = { d: identifier(event.tags), emoji: collectEmoji(event.tags) };\n const title = firstTagValue(event.tags, \"title\");\n // empty title is treated as absent\n if (title !== undefined && title !== \"\") {\n result.title = title;\n }\n return result;\n}\n\n/** Parse kind:39089 starter pack (follow pack) public `d` and `p` tags. */\nexport function parseFollowPack(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n pubkeys: string[];\n} {\n requireKind(event, Kind.StarterPack);\n const pubkeys: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"p\" || value === undefined || !isHex32(value.toLowerCase())) {\n continue;\n }\n pubkeys.push(value.toLowerCase());\n }\n return { d: identifier(event.tags), pubkeys };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip51.mjs","names":[],"sources":["../../src/nips/nip51.ts"],"sourcesContent":["/**\n * NIP-51: Lists Public tags plus NIP-44 private `.content` (author encrypts to self). Crypto is\n * injected; this module does not import signer. No NIP-04 sniff. Kind 10063 Blossom servers live in\n * blossom.ts.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/51.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { firstTagValue, getDTag, isTag, Tag } from \"../core/tag.ts\";\nimport {\n assertHex32,\n containsLoneSurrogate,\n isHex32,\n normalizeRelayUrls,\n lowerAscii,\n} from \"../core/util.ts\";\n\n/**\n * Structural crypto used by NIP-51 private tags. Satisfied by NostrSigner when\n * nip44Encrypt/nip44Decrypt are present. This module must not import src/signer/.\n */\nexport type Nip51Crypto = {\n getPublicKey: () => Promise<string>;\n nip44Encrypt: (peer: string, plaintext: string) => Promise<string>;\n nip44Decrypt: (peer: string, payload: string) => Promise<string>;\n};\n\nexport type MuteItem =\n | { readonly type: \"pubkey\"; readonly value: string }\n | { readonly type: \"event\"; readonly value: string }\n | { readonly type: \"hashtag\"; readonly value: string }\n | { readonly type: \"word\"; readonly value: string };\n\nfunction requireKind(event: Pick<Event, \"kind\">, kind: number): void {\n if (event.kind !== kind) {\n throw new EventValidationError(`expected kind ${kind}, got ${event.kind}`);\n }\n}\n\nfunction collectRelays(tags: Event[\"tags\"]): string[] {\n const urls: string[] = [];\n for (const tag of tags) {\n const value = tag.at(1);\n if (tag[0] === \"relay\" && value !== undefined) {\n urls.push(value);\n }\n }\n return normalizeRelayUrls(urls);\n}\n\nfunction collectEmoji(tags: Event[\"tags\"]): Array<{ shortcode: string; url: string }> {\n const out: Array<{ shortcode: string; url: string }> = [];\n for (const tag of tags) {\n if (tag[0] !== \"emoji\") {\n continue;\n }\n const shortcode = tag.at(1);\n const url = tag.at(2);\n if (shortcode === undefined || shortcode === \"\" || url === undefined || url === \"\") {\n continue;\n }\n out.push({ shortcode, url });\n }\n return out;\n}\n\nfunction identifier(tags: Event[\"tags\"]): string {\n return getDTag(tags) ?? \"\";\n}\n\n/** NIP-51 a-tag: `30002:<64-hex-pubkey>:<d>` with `d` possibly containing `:`. */\nfunction isRelaySetAddress(value: string): boolean {\n const kindSep = value.indexOf(\":\");\n if (kindSep === -1) {\n return false;\n }\n if (value.slice(0, kindSep) !== String(Kind.RelaySets)) {\n return false;\n }\n const pkSep = value.indexOf(\":\", kindSep + 1);\n if (pkSep === -1) {\n return false;\n }\n const pubkey = value.slice(kindSep + 1, pkSep);\n const d = value.slice(pkSep + 1);\n return isHex32(pubkey) && d.length > 0;\n}\n\n/** Parse kind:10000 mute list public tags (`p` / `e` / `t` / `word`). */\nexport function parseMuteList(event: Pick<Event, \"kind\" | \"tags\">): MuteItem[] {\n requireKind(event, Kind.MuteList);\n const items: MuteItem[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (value === undefined || value === \"\") {\n continue;\n }\n switch (tag[0]) {\n case \"p\":\n if (isHex32(value)) {\n items.push({ type: \"pubkey\", value });\n }\n break;\n case \"e\":\n if (isHex32(value)) {\n items.push({ type: \"event\", value });\n }\n break;\n case \"t\":\n // NIP-51 does not require hashtag case-folding.\n items.push({ type: \"hashtag\", value });\n break;\n case \"word\":\n // Natural-language text keeps Unicode lowercase — shared\n // SpecialCasing, not a JS accident.\n items.push({ type: \"word\", value: value.toLowerCase() });\n break;\n default:\n break;\n }\n }\n return items;\n}\n\n/** Build an unsigned kind:10000 EventBuilder from public mute items. */\nexport function muteListEventBuilder(items: ReadonlyArray<MuteItem>): EventBuilder {\n const b = new EventBuilder(Kind.MuteList, \"\");\n for (const item of items) {\n switch (item.type) {\n case \"pubkey\":\n b.tag(Tag.p(assertHex32(item.value, \"public key\")));\n break;\n case \"event\":\n b.tag(Tag.e(assertHex32(item.value, \"event id\")));\n break;\n case \"hashtag\":\n if (item.value) {\n b.tag(Tag.t(item.value));\n }\n break;\n case \"word\":\n if (item.value) {\n b.tag([\"word\", item.value.toLowerCase()]);\n }\n break;\n }\n }\n return b;\n}\n\n/** NIP-44 ciphertext of tags, conversation peer = author's own pubkey. */\nexport async function encryptPrivateTags(\n crypto: Nip51Crypto,\n tags: Event[\"tags\"],\n): Promise<string> {\n const self = await crypto.getPublicKey();\n return crypto.nip44Encrypt(self, JSON.stringify(tags));\n}\n\n/** Decrypt NIP-51 `.content`. Empty content is no private tags, not ciphertext. */\nexport async function decryptPrivateTags(\n crypto: Nip51Crypto,\n event: Pick<Event, \"pubkey\" | \"content\">,\n): Promise<Event[\"tags\"]> {\n // NIP-44 rejects empty plaintext; empty `.content` means no private items.\n if (event.content === \"\") {\n return [];\n }\n const self = await crypto.getPublicKey();\n if (lowerAscii(self) !== event.pubkey) {\n throw new EventValidationError(\"NIP-51 private content is only for the author\");\n }\n const plaintext = await crypto.nip44Decrypt(self, event.content);\n let parsed: unknown;\n try {\n parsed = JSON.parse(plaintext);\n } catch (error) {\n throw new EventValidationError(\"invalid NIP-51 private tags\", { cause: error });\n }\n // Scan the parsed value before the shape check: `JSON.parse` accepts lone\n // surrogates via `\\ud800`-style escapes while serde_json rejects them, so\n // both sides must agree on the invalid-tags error (N10).\n if (containsLoneSurrogate(parsed) || !Array.isArray(parsed) || !parsed.every(isTag)) {\n throw new EventValidationError(\"invalid NIP-51 private tags\");\n }\n return parsed;\n}\n\n/** Public mute items from tags; private mute items from NIP-44 `.content`. */\nexport async function parseMuteListPrivate(\n crypto: Nip51Crypto,\n event: Pick<Event, \"kind\" | \"tags\" | \"pubkey\" | \"content\">,\n): Promise<{ public: MuteItem[]; private: MuteItem[] }> {\n const pub = parseMuteList(event);\n const decrypted = await decryptPrivateTags(crypto, event);\n return {\n public: pub,\n private: parseMuteList({ kind: event.kind, tags: decrypted }),\n };\n}\n\n/** Parse kind:10001 pin list public `e` tags. */\nexport function parsePinList(event: Pick<Event, \"kind\" | \"tags\">): string[] {\n requireKind(event, Kind.PinList);\n const ids: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"e\" || value === undefined || !isHex32(value)) {\n continue;\n }\n ids.push(value);\n }\n return ids;\n}\n\n/** Build an unsigned kind:10001 EventBuilder from event ids. */\nexport function pinListEventBuilder(ids: ReadonlyArray<string>): EventBuilder {\n const b = new EventBuilder(Kind.PinList, \"\");\n for (const id of ids) {\n b.tag(Tag.e(assertHex32(id, \"event id\")));\n }\n return b;\n}\n\n/** Parse kind:10003 bookmark list public `e` and `a` tags. */\nexport function parseBookmarkList(event: Pick<Event, \"kind\" | \"tags\">): {\n e: string[];\n a: string[];\n} {\n requireKind(event, Kind.BookmarkList);\n const e: string[] = [];\n const a: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (value === undefined || value === \"\") {\n continue;\n }\n if (tag[0] === \"e\") {\n if (isHex32(value)) {\n e.push(value);\n }\n } else if (tag[0] === \"a\") {\n a.push(value);\n }\n }\n return { e, a };\n}\n\n/** Build an unsigned kind:10003 EventBuilder from public bookmark pointers. */\nexport function bookmarkListEventBuilder(items: {\n e?: ReadonlyArray<string>;\n a?: ReadonlyArray<string>;\n}): EventBuilder {\n const b = new EventBuilder(Kind.BookmarkList, \"\");\n if (items.e) {\n for (const id of items.e) {\n b.tag(Tag.e(assertHex32(id, \"event id\")));\n }\n }\n if (items.a) {\n for (const coord of items.a) {\n if (coord) {\n b.tag(Tag.a(coord));\n }\n }\n }\n return b;\n}\n\n/** Parse kind:10030 user emoji list public `emoji` and `a` tags. */\nexport function parseUserEmojiList(event: Pick<Event, \"kind\" | \"tags\">): {\n emoji: Array<{ shortcode: string; url: string }>;\n sets: string[];\n} {\n requireKind(event, Kind.UserEmojiList);\n const sets: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] === \"a\" && value !== undefined && value !== \"\") {\n sets.push(value);\n }\n }\n return { emoji: collectEmoji(event.tags), sets };\n}\n\n/** Parse kind:30002 relay set public `d` and `relay` tags. */\nexport function parseRelaySet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n relays: string[];\n} {\n requireKind(event, Kind.RelaySets);\n return { d: identifier(event.tags), relays: collectRelays(event.tags) };\n}\n\n/** Parse kind:10012 favorite relays public `relay` tags and kind:30002 `a` tags. */\nexport function parseFavoriteRelays(event: Pick<Event, \"kind\" | \"tags\">): {\n relays: string[];\n sets: string[];\n} {\n requireKind(event, Kind.FavoriteRelays);\n const sets: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"a\" || value === undefined || !isRelaySetAddress(value)) {\n continue;\n }\n sets.push(value);\n }\n return { relays: collectRelays(event.tags), sets };\n}\n\n/** Parse kind:30030 emoji set public `d` / `title` / `emoji` tags. */\nexport function parseEmojiSet(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n title?: string;\n emoji: Array<{ shortcode: string; url: string }>;\n} {\n requireKind(event, Kind.EmojiSet);\n const result: {\n d: string;\n title?: string;\n emoji: Array<{ shortcode: string; url: string }>;\n } = { d: identifier(event.tags), emoji: collectEmoji(event.tags) };\n const title = firstTagValue(event.tags, \"title\");\n // empty title is treated as absent\n if (title !== undefined && title !== \"\") {\n result.title = title;\n }\n return result;\n}\n\n/** Parse kind:39089 starter pack (follow pack) public `d` and `p` tags. */\nexport function parseFollowPack(event: Pick<Event, \"kind\" | \"tags\">): {\n d: string;\n pubkeys: string[];\n} {\n requireKind(event, Kind.StarterPack);\n const pubkeys: string[] = [];\n for (const tag of event.tags) {\n const value = tag.at(1);\n if (tag[0] !== \"p\" || value === undefined || !isHex32(value)) {\n continue;\n }\n pubkeys.push(value);\n }\n return { d: identifier(event.tags), pubkeys };\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { g as utf8Encoder, p as nowSeconds, s as hexToBytes, v as EventValidationError } from "../util-DfsyJ9nz.mjs";
1
+ import { b as EventValidationError, d as lowerAscii, m as nowSeconds, s as hexToBytes, v as utf8Encoder } from "../util-BcRBO6o-.mjs";
2
2
  import { r as isAddressableKind, t as Kind } from "../kind-I2Tvbe82.mjs";
3
- import { S as parseEventAddress, _ as eventAddress, b as getDTag, g as Tag, h as validateSignedEvent, v as firstTagValue } from "../event-DvM1f03b.mjs";
4
- import { n as verifyEvent } from "../verifier-DEHUy4AD.mjs";
3
+ import { S as parseEventAddress, _ as eventAddress, b as getDTag, g as Tag, h as validateSignedEvent, v as firstTagValue } from "../event-DHtcm0jM.mjs";
4
+ import { n as verifyEvent } from "../verifier-DBSPHzGg.mjs";
5
5
  import { sha256 } from "@noble/hashes/sha2.js";
6
6
  import { bech32 } from "@scure/base";
7
7
  import { equalBytes } from "@noble/ciphers/utils.js";
@@ -84,8 +84,8 @@ function countTags(tags, name) {
84
84
  return n;
85
85
  }
86
86
  function hasHexTagValue(tags, name, value) {
87
- const needle = value.toLowerCase();
88
- for (const tag of tags) if (tag[0] === name && tag[1] !== void 0 && tag[1].toLowerCase() === needle) return true;
87
+ const needle = lowerAscii(value);
88
+ for (const tag of tags) if (tag[0] === name && tag[1] !== void 0 && tag[1] === needle) return true;
89
89
  return false;
90
90
  }
91
91
  function hasAddressTag(tags, value) {
@@ -117,7 +117,7 @@ function wordsToInt(words) {
117
117
  /** Decode a BOLT11 invoice. Never throws. Requires a 32-byte payment hash (type 1). */
118
118
  function parseBolt11(pr) {
119
119
  try {
120
- const { prefix, words } = bech32.decode(pr.toLowerCase(), false);
120
+ const { prefix, words } = bech32.decode(lowerAscii(pr), false);
121
121
  if (!prefix.startsWith("ln")) return;
122
122
  if (words.length < 111) return;
123
123
  const fields = {
@@ -183,7 +183,7 @@ function parseZapRequestFromReceipt(receipt) {
183
183
  function validateZapReceipt(receipt, ctx) {
184
184
  try {
185
185
  if (receipt.kind !== Kind.Zap || !verifyEvent(receipt)) return fail("invalid receipt");
186
- if (receipt.pubkey.toLowerCase() !== ctx.nostrPubkey.toLowerCase()) return fail("pubkey mismatch");
186
+ if (receipt.pubkey !== lowerAscii(ctx.nostrPubkey)) return fail("pubkey mismatch");
187
187
  const descriptionRaw = firstTagValue(receipt.tags, "description");
188
188
  const request = parseZapRequestFromReceipt(receipt);
189
189
  if (descriptionRaw === void 0 || !request) return fail("invalid zap request");
@@ -201,7 +201,7 @@ function validateZapReceipt(receipt, ctx) {
201
201
  if (requestPCount > 1) return fail("too many P tags");
202
202
  if (requestPCount === 1) {
203
203
  const requestP = firstTagValue(request.tags, "P");
204
- if (requestP === void 0 || requestP.toLowerCase() !== receipt.pubkey.toLowerCase()) return fail("request P mismatch");
204
+ if (requestP === void 0 || requestP !== receipt.pubkey) return fail("request P mismatch");
205
205
  }
206
206
  const bolt11Tag = firstTagValue(receipt.tags, "bolt11");
207
207
  if (bolt11Tag === void 0) return fail("missing bolt11");
@@ -215,7 +215,7 @@ function validateZapReceipt(receipt, ctx) {
215
215
  const digest = sha256(utf8Encoder.encode(descriptionRaw));
216
216
  if (!equalBytes(digest, bolt11.descriptionHash)) return fail("description hash mismatch");
217
217
  const requestLnurl = firstTagValue(request.tags, "lnurl");
218
- if (requestLnurl !== void 0 && ctx.lnurl !== void 0 && requestLnurl.toLowerCase() !== ctx.lnurl.toLowerCase()) return fail("lnurl mismatch");
218
+ if (requestLnurl !== void 0 && ctx.lnurl !== void 0 && lowerAscii(requestLnurl) !== lowerAscii(ctx.lnurl)) return fail("lnurl mismatch");
219
219
  const preimageHex = firstTagValue(receipt.tags, "preimage");
220
220
  if (preimageHex !== void 0 && bolt11.paymentHash) {
221
221
  let preimage;
@@ -236,7 +236,7 @@ function validateZapReceipt(receipt, ctx) {
236
236
  }
237
237
  for (const tag of receipt.tags) {
238
238
  if (tag[0] !== "P") continue;
239
- if (tag[1] === void 0 || tag[1].toLowerCase() !== request.pubkey.toLowerCase()) return fail("receipt P mismatch");
239
+ if (tag[1] === void 0 || tag[1] !== lowerAscii(request.pubkey)) return fail("receipt P mismatch");
240
240
  }
241
241
  return {
242
242
  valid: true,
@@ -1 +1 @@
1
- {"version":3,"file":"nip57.mjs","names":[],"sources":["../../src/nips/nip57.ts"],"sourcesContent":["/**\n * NIP-57 Lightning Zaps — zap request template (kind 9734) and receipt validation (kind 9735). Does\n * not fetch LNURL. Receipt checks never throw.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/57.md\n */\nimport { equalBytes } from \"@noble/ciphers/utils.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bech32 } from \"@scure/base\";\n\nimport { EventValidationError } from \"../core/error.ts\";\nimport { validateSignedEvent } from \"../core/event.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { isAddressableKind, Kind } from \"../core/kind.ts\";\nimport { eventAddress, firstTagValue, getDTag, parseEventAddress, Tag } from \"../core/tag.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport { hexToBytes, nowSeconds, utf8Encoder } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\n\nexport type ProfileZapRequest = {\n readonly pubkey: string;\n /** Amount in millisats. */\n readonly amount: number;\n readonly relays: ReadonlyArray<string>;\n readonly comment?: string | undefined;\n readonly lnurl?: string | undefined;\n};\n\nexport type EventZapRequest = {\n readonly event: Event;\n /** Amount in millisats. */\n readonly amount: number;\n readonly relays: ReadonlyArray<string>;\n readonly comment?: string | undefined;\n readonly lnurl?: string | undefined;\n};\n\nexport function makeZapRequest(params: ProfileZapRequest | EventZapRequest): EventTemplate {\n if (!Number.isSafeInteger(params.amount) || params.amount <= 0) {\n throw new EventValidationError(\"zap amount must be a positive integer (msats)\");\n }\n if (params.relays.length === 0) {\n throw new EventValidationError(\"relays tag requires one or more URLs\");\n }\n\n const recipient = \"event\" in params ? params.event.pubkey : params.pubkey;\n const tags: Tag[] = [\n Tag.p(recipient),\n [\"amount\", params.amount.toString()],\n [\"relays\", ...params.relays],\n ];\n\n if (\"event\" in params) {\n const { event } = params;\n tags.push(Tag.e(event.id));\n if (isAddressableKind(event.kind)) {\n const d = getDTag(event.tags);\n if (d === undefined) {\n throw new EventValidationError(\"d tag not found\");\n }\n const addr = eventAddress(event);\n if (addr !== undefined) {\n tags.push([\"a\", addr]);\n }\n }\n tags.push([\"k\", event.kind.toString()]);\n }\n\n if (params.lnurl !== undefined && params.lnurl !== \"\") {\n tags.push([\"lnurl\", params.lnurl]);\n }\n\n return {\n kind: Kind.ZapRequest,\n created_at: nowSeconds(),\n content: params.comment ?? \"\",\n tags,\n };\n}\n\nexport type ZapReceiptContext = {\n readonly nostrPubkey: string;\n readonly lnurl?: string | undefined;\n};\n\nexport type ZapReceiptValidation =\n | {\n readonly valid: true;\n readonly request: Event;\n readonly amountMsats: number | undefined;\n }\n | { readonly valid: false; readonly reason: string };\n\nexport type Bolt11Fields = {\n readonly amountMsats?: number | undefined;\n /** The `d` tagged field, UTF-8 decoded; omitted on invalid UTF-8. */\n readonly description?: string | undefined;\n readonly descriptionHash?: Uint8Array | undefined;\n readonly paymentHash?: Uint8Array | undefined;\n /** Invoice creation time (unix seconds). */\n readonly timestamp: number;\n /** `x` tagged field — seconds after `timestamp` until expiry. Defaults to 3600 when absent. */\n readonly expiry: number;\n};\n\nfunction fail(reason: string): ZapReceiptValidation {\n return { valid: false, reason };\n}\n\nconst MSATS_PER_BTC = 100_000_000_000;\nconst MSATS_PER_MILLI = 100_000_000;\nconst MSATS_PER_MICRO = 100_000;\nconst MSATS_PER_NANO = 100;\n\n/** Amount is digits after `ln` + currency letters, optional m/u/n/p. Non-integer pico is omitted. */\nfunction amountMsatsFromHrp(hrp: string): number | undefined {\n if (!hrp.startsWith(\"ln\")) {\n return undefined;\n }\n let i = 2;\n while (i < hrp.length) {\n const c = hrp.codePointAt(i);\n if (c === undefined || c < 97 || c > 122) {\n break;\n }\n i++;\n }\n if (i === hrp.length) {\n return undefined;\n }\n const rest = hrp.slice(i);\n const m = /^([0-9]+)([munp])?$/.exec(rest);\n if (!m) {\n return undefined;\n }\n const n = Number(m[1]);\n if (!Number.isSafeInteger(n)) {\n return undefined;\n }\n const mul = m.at(2);\n const factor =\n mul === undefined\n ? MSATS_PER_BTC\n : mul === \"m\"\n ? MSATS_PER_MILLI\n : mul === \"u\"\n ? MSATS_PER_MICRO\n : mul === \"n\"\n ? MSATS_PER_NANO\n : 0;\n if (factor !== 0) {\n const msats = n * factor;\n return Number.isSafeInteger(msats) ? msats : undefined;\n }\n // p: 0.1 msat; drop amounts that are not whole millisats\n if (n % 10 !== 0) {\n return undefined;\n }\n return n / 10;\n}\n\nfunction parseMsatsTag(value: string): number | undefined {\n if (!/^[0-9]+$/.test(value)) {\n return undefined;\n }\n const n = Number(value);\n return Number.isSafeInteger(n) ? n : undefined;\n}\n\nfunction countTags(tags: ReadonlyArray<Tag>, name: string): number {\n let n = 0;\n for (const tag of tags) {\n if (tag[0] === name) {\n n++;\n }\n }\n return n;\n}\n\nfunction hasHexTagValue(tags: ReadonlyArray<Tag>, name: string, value: string): boolean {\n const needle = value.toLowerCase();\n for (const tag of tags) {\n if (tag[0] === name && tag[1] !== undefined && tag[1].toLowerCase() === needle) {\n return true;\n }\n }\n return false;\n}\n\nfunction hasAddressTag(tags: ReadonlyArray<Tag>, value: string): boolean {\n const want = parseEventAddress(value);\n if (!want) {\n return false;\n }\n for (const tag of tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined) {\n continue;\n }\n const got = parseEventAddress(tag[1]);\n if (\n got &&\n got.kind === want.kind &&\n got.pubkey === want.pubkey &&\n got.identifier === want.identifier\n ) {\n return true;\n }\n }\n return false;\n}\n\nconst BOLT11_TIMESTAMP_WORDS = 7;\nconst BOLT11_SIGNATURE_WORDS = 104;\nconst BOLT11_HASH_WORDS = 52;\nconst HASH_BYTES = 32;\nconst BOLT11_TAG_PAYMENT_HASH = 1;\nconst BOLT11_TAG_EXPIRY = 6;\nconst BOLT11_TAG_DESCRIPTION = 13;\nconst BOLT11_TAG_DESCRIPTION_HASH = 23;\nconst DEFAULT_EXPIRY_SECONDS = 3600;\n\nconst utf8DecoderFatal = new TextDecoder(\"utf-8\", { fatal: true });\n\n/** Big-endian 5-bit words as a JS number (safe below 2^53). */\nfunction wordsToInt(words: ReadonlyArray<number>): number {\n let value = 0;\n for (const word of words) {\n value = value * 32 + word;\n }\n return value;\n}\n\n/** Decode a BOLT11 invoice. Never throws. Requires a 32-byte payment hash (type 1). */\nexport function parseBolt11(pr: string): Bolt11Fields | undefined {\n try {\n // Invoices exceed bech32's default 90-char limit.\n const { prefix, words } = bech32.decode(pr.toLowerCase(), false);\n if (!prefix.startsWith(\"ln\")) {\n return undefined;\n }\n // timestamp (7) + tagged fields + secp256k1 signature (104)\n if (words.length < BOLT11_TIMESTAMP_WORDS + BOLT11_SIGNATURE_WORDS) {\n return undefined;\n }\n const fields: Mutable<Bolt11Fields> = {\n timestamp: wordsToInt(words.slice(0, BOLT11_TIMESTAMP_WORDS)),\n expiry: DEFAULT_EXPIRY_SECONDS,\n };\n let sawExpiry = false;\n const amountMsats = amountMsatsFromHrp(prefix);\n if (amountMsats !== undefined) {\n fields.amountMsats = amountMsats;\n }\n const tlvEnd = words.length - BOLT11_SIGNATURE_WORDS;\n let i = BOLT11_TIMESTAMP_WORDS;\n while (i + 3 <= tlvEnd) {\n const type = words[i];\n const lenHigh = words[i + 1];\n const lenLow = words[i + 2];\n if (type === undefined || lenHigh === undefined || lenLow === undefined) {\n break;\n }\n const dataLen = lenHigh * 32 + lenLow;\n i += 3;\n if (i + dataLen > tlvEnd) {\n break;\n }\n const data = words.slice(i, i + dataLen);\n i += dataLen;\n if (type === BOLT11_TAG_EXPIRY) {\n if (!sawExpiry) {\n sawExpiry = true;\n // An `x` beyond 2^53-1 would lose precision as a JS number —\n // ignore the field and keep the 3600s default (N8).\n const expiry = wordsToInt(data);\n if (Number.isSafeInteger(expiry)) {\n fields.expiry = expiry;\n }\n }\n continue;\n }\n if (type === BOLT11_TAG_DESCRIPTION) {\n const bytes = bech32.fromWordsUnsafe(data);\n if (bytes !== undefined) {\n try {\n fields.description ??= utf8DecoderFatal.decode(bytes);\n } catch {\n // Invalid UTF-8 — drop the field, not the invoice.\n }\n }\n continue;\n }\n if (\n (type !== BOLT11_TAG_PAYMENT_HASH && type !== BOLT11_TAG_DESCRIPTION_HASH) ||\n dataLen !== BOLT11_HASH_WORDS\n ) {\n continue;\n }\n const bytes = bech32.fromWordsUnsafe(data);\n if (!bytes || bytes.length !== HASH_BYTES) {\n continue;\n }\n if (type === BOLT11_TAG_PAYMENT_HASH) {\n fields.paymentHash ??= bytes;\n } else {\n fields.descriptionHash ??= bytes;\n }\n }\n if (!fields.paymentHash) {\n return undefined;\n }\n return fields;\n } catch {\n return undefined;\n }\n}\n\n/** Parse the `description` tag of a kind 9735 receipt as a signed kind 9734. Never throws. */\nexport function parseZapRequestFromReceipt(receipt: Event): Event | undefined {\n try {\n const raw = firstTagValue(receipt.tags, \"description\");\n if (raw === undefined) {\n return undefined;\n }\n const parsed: unknown = JSON.parse(raw);\n if (!validateSignedEvent(parsed) || parsed.kind !== Kind.ZapRequest) {\n return undefined;\n }\n if (!verifyEvent(parsed)) {\n return undefined;\n }\n return parsed;\n } catch {\n return undefined;\n }\n}\n\n/** Validate a kind 9735 zap receipt. Never throws. */\nexport function validateZapReceipt(receipt: Event, ctx: ZapReceiptContext): ZapReceiptValidation {\n try {\n if (receipt.kind !== Kind.Zap || !verifyEvent(receipt)) {\n return fail(\"invalid receipt\");\n }\n if (receipt.pubkey.toLowerCase() !== ctx.nostrPubkey.toLowerCase()) {\n return fail(\"pubkey mismatch\");\n }\n\n const descriptionRaw = firstTagValue(receipt.tags, \"description\");\n const request = parseZapRequestFromReceipt(receipt);\n if (descriptionRaw === undefined || !request) {\n return fail(\"invalid zap request\");\n }\n if (countTags(request.tags, \"p\") !== 1) {\n return fail(\"invalid p count\");\n }\n if (countTags(request.tags, \"e\") > 1) {\n return fail(\"too many e tags\");\n }\n const relaysValue = firstTagValue(request.tags, \"relays\");\n if (relaysValue === undefined || relaysValue === \"\") {\n return fail(\"missing relays\");\n }\n\n for (const tag of request.tags) {\n if (tag[0] !== \"a\") {\n continue;\n }\n if (tag[1] === undefined) {\n return fail(\"invalid a\");\n }\n const parsed = parseEventAddress(tag[1]);\n if (!parsed || !isAddressableKind(parsed.kind)) {\n return fail(\"invalid a\");\n }\n }\n\n // Request P is the LNURL provider (receipt pubkey), not the zap sender.\n const requestPCount = countTags(request.tags, \"P\");\n if (requestPCount > 1) {\n return fail(\"too many P tags\");\n }\n if (requestPCount === 1) {\n const requestP = firstTagValue(request.tags, \"P\");\n if (requestP === undefined || requestP.toLowerCase() !== receipt.pubkey.toLowerCase()) {\n return fail(\"request P mismatch\");\n }\n }\n\n const bolt11Tag = firstTagValue(receipt.tags, \"bolt11\");\n if (bolt11Tag === undefined) {\n return fail(\"missing bolt11\");\n }\n const bolt11 = parseBolt11(bolt11Tag);\n if (!bolt11 || !bolt11.descriptionHash) {\n return fail(\"invalid bolt11\");\n }\n\n const requestAmount = firstTagValue(request.tags, \"amount\");\n if (requestAmount !== undefined) {\n const msats = parseMsatsTag(requestAmount);\n if (msats === undefined || bolt11.amountMsats !== msats) {\n return fail(\"amount mismatch\");\n }\n }\n\n // Hash the tag payload, not JSON.stringify(parsed) (key order may differ).\n const digest = sha256(utf8Encoder.encode(descriptionRaw));\n if (!equalBytes(digest, bolt11.descriptionHash)) {\n return fail(\"description hash mismatch\");\n }\n\n const requestLnurl = firstTagValue(request.tags, \"lnurl\");\n if (\n requestLnurl !== undefined &&\n ctx.lnurl !== undefined &&\n requestLnurl.toLowerCase() !== ctx.lnurl.toLowerCase()\n ) {\n return fail(\"lnurl mismatch\");\n }\n\n const preimageHex = firstTagValue(receipt.tags, \"preimage\");\n if (preimageHex !== undefined && bolt11.paymentHash) {\n let preimage: Uint8Array;\n try {\n preimage = hexToBytes(preimageHex);\n } catch {\n return fail(\"preimage mismatch\");\n }\n if (!equalBytes(sha256(preimage), bolt11.paymentHash)) {\n return fail(\"preimage mismatch\");\n }\n }\n\n const recipient = firstTagValue(request.tags, \"p\");\n if (recipient === undefined || !hasHexTagValue(receipt.tags, \"p\", recipient)) {\n return fail(\"missing p\");\n }\n const requestE = firstTagValue(request.tags, \"e\");\n if (requestE !== undefined && !hasHexTagValue(receipt.tags, \"e\", requestE)) {\n return fail(\"missing e\");\n }\n for (const tag of request.tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined) {\n continue;\n }\n if (!hasAddressTag(receipt.tags, tag[1])) {\n return fail(\"missing a\");\n }\n }\n\n // Receipt P is the zap sender (request pubkey). Do not copy request tag P.\n for (const tag of receipt.tags) {\n if (tag[0] !== \"P\") {\n continue;\n }\n if (tag[1] === undefined || tag[1].toLowerCase() !== request.pubkey.toLowerCase()) {\n return fail(\"receipt P mismatch\");\n }\n }\n\n const amountMsats =\n bolt11.amountMsats ??\n (requestAmount === undefined ? undefined : parseMsatsTag(requestAmount));\n return { valid: true, request, amountMsats };\n } catch {\n return fail(\"invalid receipt\");\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip57.mjs","names":[],"sources":["../../src/nips/nip57.ts"],"sourcesContent":["/**\n * NIP-57 Lightning Zaps — zap request template (kind 9734) and receipt validation (kind 9735). Does\n * not fetch LNURL. Receipt checks never throw.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/57.md\n */\nimport { equalBytes } from \"@noble/ciphers/utils.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bech32 } from \"@scure/base\";\n\nimport { EventValidationError } from \"../core/error.ts\";\nimport { validateSignedEvent } from \"../core/event.ts\";\nimport type { Event, EventTemplate } from \"../core/event.ts\";\nimport { isAddressableKind, Kind } from \"../core/kind.ts\";\nimport { eventAddress, firstTagValue, getDTag, parseEventAddress, Tag } from \"../core/tag.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport { hexToBytes, nowSeconds, utf8Encoder, lowerAscii } from \"../core/util.ts\";\nimport { verifyEvent } from \"../core/verifier.ts\";\n\nexport type ProfileZapRequest = {\n readonly pubkey: string;\n /** Amount in millisats. */\n readonly amount: number;\n readonly relays: ReadonlyArray<string>;\n readonly comment?: string | undefined;\n readonly lnurl?: string | undefined;\n};\n\nexport type EventZapRequest = {\n readonly event: Event;\n /** Amount in millisats. */\n readonly amount: number;\n readonly relays: ReadonlyArray<string>;\n readonly comment?: string | undefined;\n readonly lnurl?: string | undefined;\n};\n\nexport function makeZapRequest(params: ProfileZapRequest | EventZapRequest): EventTemplate {\n if (!Number.isSafeInteger(params.amount) || params.amount <= 0) {\n throw new EventValidationError(\"zap amount must be a positive integer (msats)\");\n }\n if (params.relays.length === 0) {\n throw new EventValidationError(\"relays tag requires one or more URLs\");\n }\n\n const recipient = \"event\" in params ? params.event.pubkey : params.pubkey;\n const tags: Tag[] = [\n Tag.p(recipient),\n [\"amount\", params.amount.toString()],\n [\"relays\", ...params.relays],\n ];\n\n if (\"event\" in params) {\n const { event } = params;\n tags.push(Tag.e(event.id));\n if (isAddressableKind(event.kind)) {\n const d = getDTag(event.tags);\n if (d === undefined) {\n throw new EventValidationError(\"d tag not found\");\n }\n const addr = eventAddress(event);\n if (addr !== undefined) {\n tags.push([\"a\", addr]);\n }\n }\n tags.push([\"k\", event.kind.toString()]);\n }\n\n if (params.lnurl !== undefined && params.lnurl !== \"\") {\n tags.push([\"lnurl\", params.lnurl]);\n }\n\n return {\n kind: Kind.ZapRequest,\n created_at: nowSeconds(),\n content: params.comment ?? \"\",\n tags,\n };\n}\n\nexport type ZapReceiptContext = {\n readonly nostrPubkey: string;\n readonly lnurl?: string | undefined;\n};\n\nexport type ZapReceiptValidation =\n | {\n readonly valid: true;\n readonly request: Event;\n readonly amountMsats: number | undefined;\n }\n | { readonly valid: false; readonly reason: string };\n\nexport type Bolt11Fields = {\n readonly amountMsats?: number | undefined;\n /** The `d` tagged field, UTF-8 decoded; omitted on invalid UTF-8. */\n readonly description?: string | undefined;\n readonly descriptionHash?: Uint8Array | undefined;\n readonly paymentHash?: Uint8Array | undefined;\n /** Invoice creation time (unix seconds). */\n readonly timestamp: number;\n /** `x` tagged field — seconds after `timestamp` until expiry. Defaults to 3600 when absent. */\n readonly expiry: number;\n};\n\nfunction fail(reason: string): ZapReceiptValidation {\n return { valid: false, reason };\n}\n\nconst MSATS_PER_BTC = 100_000_000_000;\nconst MSATS_PER_MILLI = 100_000_000;\nconst MSATS_PER_MICRO = 100_000;\nconst MSATS_PER_NANO = 100;\n\n/** Amount is digits after `ln` + currency letters, optional m/u/n/p. Non-integer pico is omitted. */\nfunction amountMsatsFromHrp(hrp: string): number | undefined {\n if (!hrp.startsWith(\"ln\")) {\n return undefined;\n }\n let i = 2;\n while (i < hrp.length) {\n const c = hrp.codePointAt(i);\n if (c === undefined || c < 97 || c > 122) {\n break;\n }\n i++;\n }\n if (i === hrp.length) {\n return undefined;\n }\n const rest = hrp.slice(i);\n const m = /^([0-9]+)([munp])?$/.exec(rest);\n if (!m) {\n return undefined;\n }\n const n = Number(m[1]);\n if (!Number.isSafeInteger(n)) {\n return undefined;\n }\n const mul = m.at(2);\n const factor =\n mul === undefined\n ? MSATS_PER_BTC\n : mul === \"m\"\n ? MSATS_PER_MILLI\n : mul === \"u\"\n ? MSATS_PER_MICRO\n : mul === \"n\"\n ? MSATS_PER_NANO\n : 0;\n if (factor !== 0) {\n const msats = n * factor;\n return Number.isSafeInteger(msats) ? msats : undefined;\n }\n // p: 0.1 msat; drop amounts that are not whole millisats\n if (n % 10 !== 0) {\n return undefined;\n }\n return n / 10;\n}\n\nfunction parseMsatsTag(value: string): number | undefined {\n if (!/^[0-9]+$/.test(value)) {\n return undefined;\n }\n const n = Number(value);\n return Number.isSafeInteger(n) ? n : undefined;\n}\n\nfunction countTags(tags: ReadonlyArray<Tag>, name: string): number {\n let n = 0;\n for (const tag of tags) {\n if (tag[0] === name) {\n n++;\n }\n }\n return n;\n}\n\nfunction hasHexTagValue(tags: ReadonlyArray<Tag>, name: string, value: string): boolean {\n const needle = lowerAscii(value);\n for (const tag of tags) {\n if (tag[0] === name && tag[1] !== undefined && tag[1] === needle) {\n return true;\n }\n }\n return false;\n}\n\nfunction hasAddressTag(tags: ReadonlyArray<Tag>, value: string): boolean {\n const want = parseEventAddress(value);\n if (!want) {\n return false;\n }\n for (const tag of tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined) {\n continue;\n }\n const got = parseEventAddress(tag[1]);\n if (\n got &&\n got.kind === want.kind &&\n got.pubkey === want.pubkey &&\n got.identifier === want.identifier\n ) {\n return true;\n }\n }\n return false;\n}\n\nconst BOLT11_TIMESTAMP_WORDS = 7;\nconst BOLT11_SIGNATURE_WORDS = 104;\nconst BOLT11_HASH_WORDS = 52;\nconst HASH_BYTES = 32;\nconst BOLT11_TAG_PAYMENT_HASH = 1;\nconst BOLT11_TAG_EXPIRY = 6;\nconst BOLT11_TAG_DESCRIPTION = 13;\nconst BOLT11_TAG_DESCRIPTION_HASH = 23;\nconst DEFAULT_EXPIRY_SECONDS = 3600;\n\nconst utf8DecoderFatal = new TextDecoder(\"utf-8\", { fatal: true });\n\n/** Big-endian 5-bit words as a JS number (safe below 2^53). */\nfunction wordsToInt(words: ReadonlyArray<number>): number {\n let value = 0;\n for (const word of words) {\n value = value * 32 + word;\n }\n return value;\n}\n\n/** Decode a BOLT11 invoice. Never throws. Requires a 32-byte payment hash (type 1). */\nexport function parseBolt11(pr: string): Bolt11Fields | undefined {\n try {\n // Invoices exceed bech32's default 90-char limit.\n const { prefix, words } = bech32.decode(lowerAscii(pr), false);\n if (!prefix.startsWith(\"ln\")) {\n return undefined;\n }\n // timestamp (7) + tagged fields + secp256k1 signature (104)\n if (words.length < BOLT11_TIMESTAMP_WORDS + BOLT11_SIGNATURE_WORDS) {\n return undefined;\n }\n const fields: Mutable<Bolt11Fields> = {\n timestamp: wordsToInt(words.slice(0, BOLT11_TIMESTAMP_WORDS)),\n expiry: DEFAULT_EXPIRY_SECONDS,\n };\n let sawExpiry = false;\n const amountMsats = amountMsatsFromHrp(prefix);\n if (amountMsats !== undefined) {\n fields.amountMsats = amountMsats;\n }\n const tlvEnd = words.length - BOLT11_SIGNATURE_WORDS;\n let i = BOLT11_TIMESTAMP_WORDS;\n while (i + 3 <= tlvEnd) {\n const type = words[i];\n const lenHigh = words[i + 1];\n const lenLow = words[i + 2];\n if (type === undefined || lenHigh === undefined || lenLow === undefined) {\n break;\n }\n const dataLen = lenHigh * 32 + lenLow;\n i += 3;\n if (i + dataLen > tlvEnd) {\n break;\n }\n const data = words.slice(i, i + dataLen);\n i += dataLen;\n if (type === BOLT11_TAG_EXPIRY) {\n if (!sawExpiry) {\n sawExpiry = true;\n // An `x` beyond 2^53-1 would lose precision as a JS number —\n // ignore the field and keep the 3600s default (N8).\n const expiry = wordsToInt(data);\n if (Number.isSafeInteger(expiry)) {\n fields.expiry = expiry;\n }\n }\n continue;\n }\n if (type === BOLT11_TAG_DESCRIPTION) {\n const bytes = bech32.fromWordsUnsafe(data);\n if (bytes !== undefined) {\n try {\n fields.description ??= utf8DecoderFatal.decode(bytes);\n } catch {\n // Invalid UTF-8 — drop the field, not the invoice.\n }\n }\n continue;\n }\n if (\n (type !== BOLT11_TAG_PAYMENT_HASH && type !== BOLT11_TAG_DESCRIPTION_HASH) ||\n dataLen !== BOLT11_HASH_WORDS\n ) {\n continue;\n }\n const bytes = bech32.fromWordsUnsafe(data);\n if (!bytes || bytes.length !== HASH_BYTES) {\n continue;\n }\n if (type === BOLT11_TAG_PAYMENT_HASH) {\n fields.paymentHash ??= bytes;\n } else {\n fields.descriptionHash ??= bytes;\n }\n }\n if (!fields.paymentHash) {\n return undefined;\n }\n return fields;\n } catch {\n return undefined;\n }\n}\n\n/** Parse the `description` tag of a kind 9735 receipt as a signed kind 9734. Never throws. */\nexport function parseZapRequestFromReceipt(receipt: Event): Event | undefined {\n try {\n const raw = firstTagValue(receipt.tags, \"description\");\n if (raw === undefined) {\n return undefined;\n }\n const parsed: unknown = JSON.parse(raw);\n if (!validateSignedEvent(parsed) || parsed.kind !== Kind.ZapRequest) {\n return undefined;\n }\n if (!verifyEvent(parsed)) {\n return undefined;\n }\n return parsed;\n } catch {\n return undefined;\n }\n}\n\n/** Validate a kind 9735 zap receipt. Never throws. */\nexport function validateZapReceipt(receipt: Event, ctx: ZapReceiptContext): ZapReceiptValidation {\n try {\n if (receipt.kind !== Kind.Zap || !verifyEvent(receipt)) {\n return fail(\"invalid receipt\");\n }\n if (receipt.pubkey !== lowerAscii(ctx.nostrPubkey)) {\n return fail(\"pubkey mismatch\");\n }\n\n const descriptionRaw = firstTagValue(receipt.tags, \"description\");\n const request = parseZapRequestFromReceipt(receipt);\n if (descriptionRaw === undefined || !request) {\n return fail(\"invalid zap request\");\n }\n if (countTags(request.tags, \"p\") !== 1) {\n return fail(\"invalid p count\");\n }\n if (countTags(request.tags, \"e\") > 1) {\n return fail(\"too many e tags\");\n }\n const relaysValue = firstTagValue(request.tags, \"relays\");\n if (relaysValue === undefined || relaysValue === \"\") {\n return fail(\"missing relays\");\n }\n\n for (const tag of request.tags) {\n if (tag[0] !== \"a\") {\n continue;\n }\n if (tag[1] === undefined) {\n return fail(\"invalid a\");\n }\n const parsed = parseEventAddress(tag[1]);\n if (!parsed || !isAddressableKind(parsed.kind)) {\n return fail(\"invalid a\");\n }\n }\n\n // Request P is the LNURL provider (receipt pubkey), not the zap sender.\n const requestPCount = countTags(request.tags, \"P\");\n if (requestPCount > 1) {\n return fail(\"too many P tags\");\n }\n if (requestPCount === 1) {\n const requestP = firstTagValue(request.tags, \"P\");\n if (requestP === undefined || requestP !== receipt.pubkey) {\n return fail(\"request P mismatch\");\n }\n }\n\n const bolt11Tag = firstTagValue(receipt.tags, \"bolt11\");\n if (bolt11Tag === undefined) {\n return fail(\"missing bolt11\");\n }\n const bolt11 = parseBolt11(bolt11Tag);\n if (!bolt11 || !bolt11.descriptionHash) {\n return fail(\"invalid bolt11\");\n }\n\n const requestAmount = firstTagValue(request.tags, \"amount\");\n if (requestAmount !== undefined) {\n const msats = parseMsatsTag(requestAmount);\n if (msats === undefined || bolt11.amountMsats !== msats) {\n return fail(\"amount mismatch\");\n }\n }\n\n // Hash the tag payload, not JSON.stringify(parsed) (key order may differ).\n const digest = sha256(utf8Encoder.encode(descriptionRaw));\n if (!equalBytes(digest, bolt11.descriptionHash)) {\n return fail(\"description hash mismatch\");\n }\n\n const requestLnurl = firstTagValue(request.tags, \"lnurl\");\n if (\n requestLnurl !== undefined &&\n ctx.lnurl !== undefined &&\n lowerAscii(requestLnurl) !== lowerAscii(ctx.lnurl)\n ) {\n return fail(\"lnurl mismatch\");\n }\n\n const preimageHex = firstTagValue(receipt.tags, \"preimage\");\n if (preimageHex !== undefined && bolt11.paymentHash) {\n let preimage: Uint8Array;\n try {\n preimage = hexToBytes(preimageHex);\n } catch {\n return fail(\"preimage mismatch\");\n }\n if (!equalBytes(sha256(preimage), bolt11.paymentHash)) {\n return fail(\"preimage mismatch\");\n }\n }\n\n const recipient = firstTagValue(request.tags, \"p\");\n if (recipient === undefined || !hasHexTagValue(receipt.tags, \"p\", recipient)) {\n return fail(\"missing p\");\n }\n const requestE = firstTagValue(request.tags, \"e\");\n if (requestE !== undefined && !hasHexTagValue(receipt.tags, \"e\", requestE)) {\n return fail(\"missing e\");\n }\n for (const tag of request.tags) {\n if (tag[0] !== \"a\" || tag[1] === undefined) {\n continue;\n }\n if (!hasAddressTag(receipt.tags, tag[1])) {\n return fail(\"missing a\");\n }\n }\n\n // Receipt P is the zap sender (request pubkey). Do not copy request tag P.\n for (const tag of receipt.tags) {\n if (tag[0] !== \"P\") {\n continue;\n }\n if (tag[1] === undefined || tag[1] !== lowerAscii(request.pubkey)) {\n return fail(\"receipt P mismatch\");\n }\n }\n\n const amountMsats =\n bolt11.amountMsats ??\n (requestAmount === undefined ? undefined : parseMsatsTag(requestAmount));\n return { valid: true, request, amountMsats };\n } catch {\n return fail(\"invalid receipt\");\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { _ as requireNip59Crypto, a as Rumor, b as wrap, c as TimestampRandomize, d as createRumor, f as createSeal, g as requireNip44Decryptor, h as randomPastTimestamp, i as Nip59Error, l as WrapOptions, m as isGiftWrapKind, n as Nip44Decryptor, o as SealOptions, p as eventToJson, r as Nip59Crypto, s as TWO_DAYS_SECS, t as GiftWrapTimestamps, u as createGiftWrap, v as rumorToJson, y as unwrap } from "../nip59-DfJ6Moeu.mjs";
1
+ import { _ as requireNip59Crypto, a as Rumor, b as wrap, c as TimestampRandomize, d as createRumor, f as createSeal, g as requireNip44Decryptor, h as randomPastTimestamp, i as Nip59Error, l as WrapOptions, m as isGiftWrapKind, n as Nip44Decryptor, o as SealOptions, p as eventToJson, r as Nip59Crypto, s as TWO_DAYS_SECS, t as GiftWrapTimestamps, u as createGiftWrap, v as rumorToJson, y as unwrap } from "../nip59-B6Ey8L9z.mjs";
2
2
  export { GiftWrapTimestamps, Nip44Decryptor, Nip59Crypto, Nip59Error, Rumor, SealOptions, TWO_DAYS_SECS, TimestampRandomize, WrapOptions, createGiftWrap, createRumor, createSeal, eventToJson, isGiftWrapKind, randomPastTimestamp, requireNip44Decryptor, requireNip59Crypto, rumorToJson, unwrap, wrap };