@qntx/nostr 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/dist/{builder-BrmLOVj_.d.mts → builder-C72nJITG.d.mts} +2 -2
  2. package/dist/{builder-Cf2N12CG.mjs → builder-CGgM_QHT.mjs} +5 -5
  3. package/dist/{builder-Cf2N12CG.mjs.map → builder-CGgM_QHT.mjs.map} +1 -1
  4. package/dist/{client-B3_vToDX.mjs → client-COkEihj_.mjs} +87 -79
  5. package/dist/client-COkEihj_.mjs.map +1 -0
  6. package/dist/client.d.mts +1 -1
  7. package/dist/client.mjs +1 -1
  8. package/dist/core.d.mts +7 -7
  9. package/dist/core.mjs +9 -9
  10. package/dist/{emitter-DkCY6NkU.mjs → emitter-u4kJqQDk.mjs} +2 -2
  11. package/dist/{emitter-DkCY6NkU.mjs.map → emitter-u4kJqQDk.mjs.map} +1 -1
  12. package/dist/{error-BquXDmIN.d.mts → error-BPqAU5RB.d.mts} +2 -6
  13. package/dist/{error-BD_0QFKT.d.mts → error-BjSUsx6M.d.mts} +2 -2
  14. package/dist/{error-D3VNRVhL.mjs → error-Dyb89ahb.mjs} +2 -2
  15. package/dist/{error-D3VNRVhL.mjs.map → error-Dyb89ahb.mjs.map} +1 -1
  16. package/dist/{error-DH4guike.mjs → error-QyECUDIp.mjs} +2 -2
  17. package/dist/{error-DH4guike.mjs.map → error-QyECUDIp.mjs.map} +1 -1
  18. package/dist/{event-B3ua2_oL.mjs → event-DvM1f03b.mjs} +7 -3
  19. package/dist/event-DvM1f03b.mjs.map +1 -0
  20. package/dist/{filter-B16P33dz.mjs → filter-3jOh4uLN.mjs} +50 -20
  21. package/dist/filter-3jOh4uLN.mjs.map +1 -0
  22. package/dist/{filter-PG7r79rt.d.mts → filter-oYG2zjJ3.d.mts} +6 -2
  23. package/dist/{gossip-u0iBlZ1U.d.mts → gossip-BvCItMVZ.d.mts} +3 -3
  24. package/dist/{gossip-K_JIexQs.mjs → gossip-CRyhK6E6.mjs} +4 -4
  25. package/dist/{gossip-K_JIexQs.mjs.map → gossip-CRyhK6E6.mjs.map} +1 -1
  26. package/dist/gossip.d.mts +1 -1
  27. package/dist/gossip.mjs +1 -1
  28. package/dist/http-CuazNRIP.mjs.map +1 -1
  29. package/dist/{index-DD0Ywpay.d.mts → index-B2JlP2Jf.d.mts} +9 -9
  30. package/dist/{index-SwMknboJ.d.mts → index-CcgmG3F6.d.mts} +19 -18
  31. package/dist/{index-yq9xkONo.d.mts → index-DCobP2Jj.d.mts} +10 -10
  32. package/dist/{index-Hp26bgYG.d.mts → index-Dpj_MUxb.d.mts} +8 -5
  33. package/dist/{index-rKcmI6ex.d.mts → index-DzHmhHqD.d.mts} +3 -3
  34. package/dist/{index-DxQO1eWA.d.mts → index-muydfuYD.d.mts} +21 -21
  35. package/dist/index.d.mts +19 -20
  36. package/dist/index.mjs +20 -20
  37. package/dist/{key-B218_SoL.mjs → key-D_oEutQ6.mjs} +5 -7
  38. package/dist/{key-B218_SoL.mjs.map → key-D_oEutQ6.mjs.map} +1 -1
  39. package/dist/{key-CwMVo14x.d.mts → key-DojW2QZl.d.mts} +3 -5
  40. package/dist/{kind-D8fENiiL.mjs → kind-I2Tvbe82.mjs} +6 -5
  41. package/dist/kind-I2Tvbe82.mjs.map +1 -0
  42. package/dist/{loaders-Xdbx-K61.mjs → loaders-BRh2riV9.mjs} +17 -16
  43. package/dist/loaders-BRh2riV9.mjs.map +1 -0
  44. package/dist/loaders.d.mts +2 -2
  45. package/dist/loaders.mjs +1 -1
  46. package/dist/{memory-VyPnOBU3.mjs → memory-D_GdEQYG.mjs} +2 -2
  47. package/dist/memory-D_GdEQYG.mjs.map +1 -0
  48. package/dist/memory-index-3GSrPIWj.mjs +671 -0
  49. package/dist/memory-index-3GSrPIWj.mjs.map +1 -0
  50. package/dist/{message-pYfVKxPn.mjs → message-BMfreCQc.mjs} +6 -6
  51. package/dist/message-BMfreCQc.mjs.map +1 -0
  52. package/dist/{nip17-BM4hJYBq.d.mts → nip17-BZvFQLcs.d.mts} +4 -4
  53. package/dist/{nip19-DpPN17bY.d.mts → nip19-D4RKn6GC.d.mts} +2 -2
  54. package/dist/{nip46-BHaxC9TZ.d.mts → nip46-i8L8_nIm.d.mts} +2 -2
  55. package/dist/{nip51-D_bu8Hph.d.mts → nip51-C_kTYcud.d.mts} +2 -2
  56. package/dist/{nip59-CGWfoItR.d.mts → nip59-DfJ6Moeu.d.mts} +2 -2
  57. package/dist/{nip65-DaWMg943.d.mts → nip65-CPNE0SV0.d.mts} +2 -2
  58. package/dist/{nip77-wHA_nEV5.d.mts → nip77-DhpoL7d8.d.mts} +2 -2
  59. package/dist/nips/blossom.d.mts +2 -2
  60. package/dist/nips/blossom.mjs +3 -3
  61. package/dist/nips/blossom.mjs.map +1 -1
  62. package/dist/nips/nip04.d.mts +9 -12
  63. package/dist/nips/nip04.mjs +38 -45
  64. package/dist/nips/nip04.mjs.map +1 -1
  65. package/dist/nips/nip05.d.mts +6 -3
  66. package/dist/nips/nip05.mjs +6 -3
  67. package/dist/nips/nip05.mjs.map +1 -1
  68. package/dist/nips/nip10.d.mts +2 -2
  69. package/dist/nips/nip10.mjs +4 -4
  70. package/dist/nips/nip11.d.mts +1 -1
  71. package/dist/nips/nip11.mjs +1 -1
  72. package/dist/nips/nip13.d.mts +1 -1
  73. package/dist/nips/nip13.mjs +1 -1
  74. package/dist/nips/nip13.mjs.map +1 -1
  75. package/dist/nips/nip17.d.mts +1 -1
  76. package/dist/nips/nip17.mjs +4 -4
  77. package/dist/nips/nip17.mjs.map +1 -1
  78. package/dist/nips/nip19.d.mts +1 -1
  79. package/dist/nips/nip19.mjs +2 -2
  80. package/dist/nips/nip19.mjs.map +1 -1
  81. package/dist/nips/nip21.d.mts +6 -3
  82. package/dist/nips/nip21.mjs +8 -5
  83. package/dist/nips/nip21.mjs.map +1 -1
  84. package/dist/nips/nip27.d.mts +1 -1
  85. package/dist/nips/nip27.mjs +15 -9
  86. package/dist/nips/nip27.mjs.map +1 -1
  87. package/dist/nips/nip42.mjs +2 -2
  88. package/dist/nips/nip44.mjs +1 -1
  89. package/dist/nips/nip46.d.mts +1 -1
  90. package/dist/nips/nip46.mjs +9 -7
  91. package/dist/nips/nip46.mjs.map +1 -1
  92. package/dist/nips/nip49.d.mts +1 -1
  93. package/dist/nips/nip49.mjs +1 -1
  94. package/dist/nips/nip51.d.mts +1 -1
  95. package/dist/nips/nip51.mjs +5 -5
  96. package/dist/nips/nip51.mjs.map +1 -1
  97. package/dist/nips/nip57.mjs +6 -5
  98. package/dist/nips/nip57.mjs.map +1 -1
  99. package/dist/nips/nip59.d.mts +1 -1
  100. package/dist/nips/nip59.mjs +5 -5
  101. package/dist/nips/nip59.mjs.map +1 -1
  102. package/dist/nips/nip65.d.mts +1 -1
  103. package/dist/nips/nip65.mjs +3 -3
  104. package/dist/nips/nip77.d.mts +1 -1
  105. package/dist/nips/nip77.mjs +1 -1
  106. package/dist/nips/nip77.mjs.map +1 -1
  107. package/dist/nips/nip98.d.mts +1 -1
  108. package/dist/nips/nip98.mjs +9 -6
  109. package/dist/nips/nip98.mjs.map +1 -1
  110. package/dist/{pool-DUuYJ0x4.d.mts → pool-MehgIIzm.d.mts} +20 -16
  111. package/dist/{pool-THuuOW4S.mjs → pool-lMT_mvzn.mjs} +25 -25
  112. package/dist/pool-lMT_mvzn.mjs.map +1 -0
  113. package/dist/{put-DRmV1wHD.mjs → put-BEM5j8xK.mjs} +5 -5
  114. package/dist/{put-DRmV1wHD.mjs.map → put-BEM5j8xK.mjs.map} +1 -1
  115. package/dist/{reactive-jyKrA9ej.d.mts → reactive-B7lPFgTS.d.mts} +3 -3
  116. package/dist/{reactive--pDpLt2H.mjs → reactive-DFHY-f00.mjs} +24 -9
  117. package/dist/reactive-DFHY-f00.mjs.map +1 -0
  118. package/dist/{relay-BGPqiGKF.mjs → relay-DIR77xQ2.mjs} +102 -89
  119. package/dist/relay-DIR77xQ2.mjs.map +1 -0
  120. package/dist/relay.d.mts +5 -5
  121. package/dist/relay.mjs +3 -3
  122. package/dist/{report-CHld4uWj.mjs → report-D2BM5dgn.mjs} +2 -2
  123. package/dist/{report-CHld4uWj.mjs.map → report-D2BM5dgn.mjs.map} +1 -1
  124. package/dist/{signer-CNTIeYH4.mjs → signer-Bha9gQy8.mjs} +109 -81
  125. package/dist/signer-Bha9gQy8.mjs.map +1 -0
  126. package/dist/signer.d.mts +1 -1
  127. package/dist/signer.mjs +2 -2
  128. package/dist/{storage-B5Wdt_Mn.mjs → storage-n2BE1bdY.mjs} +108 -179
  129. package/dist/storage-n2BE1bdY.mjs.map +1 -0
  130. package/dist/storage.d.mts +3 -3
  131. package/dist/storage.mjs +4 -4
  132. package/dist/store.d.mts +1 -1
  133. package/dist/store.mjs +1 -1
  134. package/dist/testing.d.mts +4 -4
  135. package/dist/testing.mjs +35 -10
  136. package/dist/testing.mjs.map +1 -1
  137. package/dist/{types-CqhnT-Mx.d.mts → types-DiVVyf1e.d.mts} +2 -2
  138. package/dist/{util-DDpPkiED.mjs → util-DfsyJ9nz.mjs} +30 -10
  139. package/dist/util-DfsyJ9nz.mjs.map +1 -0
  140. package/dist/{verifier-CjH1vnP2.mjs → verifier-DEHUy4AD.mjs} +3 -3
  141. package/dist/{verifier-CjH1vnP2.mjs.map → verifier-DEHUy4AD.mjs.map} +1 -1
  142. package/dist/{verifier-Cl24c9ph.d.mts → verifier-DhAbqVJF.d.mts} +2 -2
  143. package/dist/{websocket-CmeqvAkp.d.mts → websocket-DaCvJkF9.d.mts} +2 -5
  144. package/package.json +4 -3
  145. package/dist/client-B3_vToDX.mjs.map +0 -1
  146. package/dist/event-B3ua2_oL.mjs.map +0 -1
  147. package/dist/filter-B16P33dz.mjs.map +0 -1
  148. package/dist/kind-D8fENiiL.mjs.map +0 -1
  149. package/dist/loaders-Xdbx-K61.mjs.map +0 -1
  150. package/dist/memory-VyPnOBU3.mjs.map +0 -1
  151. package/dist/memory-index-Bhwe0ZdG.mjs +0 -374
  152. package/dist/memory-index-Bhwe0ZdG.mjs.map +0 -1
  153. package/dist/message-pYfVKxPn.mjs.map +0 -1
  154. package/dist/pool-THuuOW4S.mjs.map +0 -1
  155. package/dist/reactive--pDpLt2H.mjs.map +0 -1
  156. package/dist/relay-BGPqiGKF.mjs.map +0 -1
  157. package/dist/signer-CNTIeYH4.mjs.map +0 -1
  158. package/dist/storage-B5Wdt_Mn.mjs.map +0 -1
  159. package/dist/util-DDpPkiED.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"nip04.mjs","names":["hexToBytes"],"sources":["../../src/nips/nip04.ts"],"sourcesContent":["/** NIP-04 legacy encrypted direct messages. Prefer NIP-44 for new applications. */\nimport { cbc } from \"@noble/ciphers/aes.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { CryptoError } from \"../core/error.ts\";\nimport {\n assertByteLength,\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\nfunction normalizeSharedSecret(privkey: Uint8Array, pubkey: string): Uint8Array {\n assertHex32(pubkey, \"public key\");\n try {\n const key = secp256k1.getSharedSecret(privkey, hexToBytes(`02${pubkey.toLowerCase()}`));\n return key.slice(1, 33);\n } catch (error) {\n throw new CryptoError(\"invalid NIP-04 public key\", { cause: error });\n }\n}\n\nfunction resolveSecret(secretKey: string | Uint8Array): Uint8Array {\n if (typeof secretKey === \"string\") {\n return hexToBytes(assertHex32(secretKey, \"secret key\"));\n }\n assertSecretKeyBytes(secretKey);\n return secretKey;\n}\n\n/** Encrypt plaintext to a peer pubkey (NIP-04). */\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n const privkey = resolveSecret(secretKey);\n const normalizedKey = normalizeSharedSecret(privkey, pubkey);\n try {\n return encryptWithSharedSecret(normalizedKey, text);\n } finally {\n normalizedKey.fill(0);\n }\n}\n\n/**\n * Encrypt with the raw 32-byte ECDH x-coordinate instead of a secret key, for key holders that only\n * expose ECDH ({@link NostrKeyOperations}).\n */\nexport function encryptWithSharedSecret(sharedX: Uint8Array, text: string): string {\n assertByteLength(sharedX, 32, \"shared secret\");\n const iv = randomBytes(16);\n const plaintext = utf8Encoder.encode(text);\n const ciphertext = cbc(sharedX, iv).encrypt(plaintext);\n return `${base64.encode(ciphertext)}?iv=${base64.encode(iv)}`;\n}\n\n/** Decrypt a NIP-04 payload from a peer pubkey. */\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n const privkey = resolveSecret(secretKey);\n splitPayload(data);\n const normalizedKey = normalizeSharedSecret(privkey, pubkey);\n try {\n return decryptWithSharedSecret(normalizedKey, data);\n } finally {\n normalizedKey.fill(0);\n }\n}\n\nfunction splitPayload(data: string): [ciphertext: string, iv: string] {\n const parts = data.split(\"?iv=\");\n const [ciphertext, iv] = parts;\n if (\n parts.length !== 2 ||\n ciphertext === undefined ||\n ciphertext === \"\" ||\n iv === undefined ||\n iv === \"\"\n ) {\n throw new CryptoError(\"invalid NIP-04 payload: missing iv\");\n }\n return [ciphertext, iv];\n}\n\n/**\n * Decrypt a NIP-04 payload with 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 decryptWithSharedSecret(sharedX: Uint8Array, data: string): string {\n assertByteLength(sharedX, 32, \"shared secret\");\n const [ciphertextPart, ivPart] = splitPayload(data);\n try {\n const iv = base64.decode(ivPart);\n const ciphertext = base64.decode(ciphertextPart);\n const plaintext = cbc(sharedX, iv).decrypt(ciphertext);\n return utf8Decoder.decode(plaintext);\n } catch (error) {\n throw new CryptoError(\"invalid NIP-04 payload\", { cause: error });\n }\n}\n"],"mappings":";;;;;;;AAgBA,SAAS,sBAAsB,SAAqB,QAA4B;CAC9E,YAAY,QAAQ,YAAY;CAChC,IAAI;EAEF,OADY,UAAU,gBAAgB,SAASA,aAAW,KAAK,OAAO,YAAY,GAAG,CAC5E,CAAC,CAAC,MAAM,GAAG,EAAE;CACxB,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,6BAA6B,EAAE,OAAO,MAAM,CAAC;CACrE;AACF;AAEA,SAAS,cAAc,WAA4C;CACjE,IAAI,OAAO,cAAc,UACvB,OAAOA,aAAW,YAAY,WAAW,YAAY,CAAC;CAExD,qBAAqB,SAAS;CAC9B,OAAO;AACT;;AAGA,SAAgB,QAAQ,WAAgC,QAAgB,MAAsB;CAE5F,MAAM,gBAAgB,sBADN,cAAc,SACoB,GAAG,MAAM;CAC3D,IAAI;EACF,OAAO,wBAAwB,eAAe,IAAI;CACpD,UAAU;EACR,cAAc,KAAK,CAAC;CACtB;AACF;;;;;AAMA,SAAgB,wBAAwB,SAAqB,MAAsB;CACjF,iBAAiB,SAAS,IAAI,eAAe;CAC7C,MAAM,KAAK,YAAY,EAAE;CACzB,MAAM,YAAY,YAAY,OAAO,IAAI;CACzC,MAAM,aAAa,IAAI,SAAS,EAAE,CAAC,CAAC,QAAQ,SAAS;CACrD,OAAO,GAAG,OAAO,OAAO,UAAU,EAAE,MAAM,OAAO,OAAO,EAAE;AAC5D;;AAGA,SAAgB,QAAQ,WAAgC,QAAgB,MAAsB;CAC5F,MAAM,UAAU,cAAc,SAAS;CACvC,aAAa,IAAI;CACjB,MAAM,gBAAgB,sBAAsB,SAAS,MAAM;CAC3D,IAAI;EACF,OAAO,wBAAwB,eAAe,IAAI;CACpD,UAAU;EACR,cAAc,KAAK,CAAC;CACtB;AACF;AAEA,SAAS,aAAa,MAAgD;CACpE,MAAM,QAAQ,KAAK,MAAM,MAAM;CAC/B,MAAM,CAAC,YAAY,MAAM;CACzB,IACE,MAAM,WAAW,KACjB,eAAe,KAAA,KACf,eAAe,MACf,OAAO,KAAA,KACP,OAAO,IAEP,MAAM,IAAI,YAAY,oCAAoC;CAE5D,OAAO,CAAC,YAAY,EAAE;AACxB;;;;;AAMA,SAAgB,wBAAwB,SAAqB,MAAsB;CACjF,iBAAiB,SAAS,IAAI,eAAe;CAC7C,MAAM,CAAC,gBAAgB,UAAU,aAAa,IAAI;CAClD,IAAI;EACF,MAAM,KAAK,OAAO,OAAO,MAAM;EAC/B,MAAM,aAAa,OAAO,OAAO,cAAc;EAC/C,MAAM,YAAY,IAAI,SAAS,EAAE,CAAC,CAAC,QAAQ,UAAU;EACrD,OAAO,YAAY,OAAO,SAAS;CACrC,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,0BAA0B,EAAE,OAAO,MAAM,CAAC;CAClE;AACF"}
1
+ {"version":3,"file":"nip04.mjs","names":["hexToBytes"],"sources":["../../src/nips/nip04.ts"],"sourcesContent":["/** NIP-04 legacy encrypted direct messages. Prefer NIP-44 for new applications. */\nimport { cbc } from \"@noble/ciphers/aes.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { CryptoError } from \"../core/error.ts\";\nimport {\n assertByteLength,\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\nfunction normalizeSharedSecret(privkey: Uint8Array, pubkey: string): Uint8Array {\n assertHex32(pubkey, \"public key\");\n try {\n const key = secp256k1.getSharedSecret(privkey, hexToBytes(`02${pubkey.toLowerCase()}`));\n return key.slice(1, 33);\n } catch (error) {\n throw new CryptoError(\"invalid NIP-04 public key\", { cause: error });\n }\n}\n\n/**\n * Encrypt plaintext with the raw 32-byte ECDH x-coordinate (NIP-04's key material, like NIP-44's\n * conversation key), for key holders that only expose ECDH ({@link NostrKeyOperations}).\n */\nexport function encrypt(plaintext: string, sharedSecret: Uint8Array): string {\n assertByteLength(sharedSecret, 32, \"shared secret\");\n const iv = randomBytes(16);\n const plaintextBytes = utf8Encoder.encode(plaintext);\n const ciphertext = cbc(sharedSecret, iv).encrypt(plaintextBytes);\n return `${base64.encode(ciphertext)}?iv=${base64.encode(iv)}`;\n}\n\n/** Decrypt a NIP-04 payload with the raw 32-byte ECDH x-coordinate. */\nexport function decrypt(payload: string, sharedSecret: Uint8Array): string {\n assertByteLength(sharedSecret, 32, \"shared secret\");\n const [ciphertextPart, ivPart] = splitPayload(payload);\n try {\n const iv = base64.decode(ivPart);\n const ciphertext = base64.decode(ciphertextPart);\n const plaintext = cbc(sharedSecret, iv).decrypt(ciphertext);\n return utf8Decoder.decode(plaintext);\n } catch (error) {\n throw new CryptoError(\"invalid NIP-04 payload\", { cause: error });\n }\n}\n\n/** Encrypt plaintext to a peer pubkey, deriving the shared secret from `secretKey`. */\nexport function encryptToPubkey(\n plaintext: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n assertSecretKeyBytes(secretKey);\n const sharedSecret = normalizeSharedSecret(secretKey, peerPubkey);\n try {\n return encrypt(plaintext, sharedSecret);\n } finally {\n sharedSecret.fill(0);\n }\n}\n\n/** Decrypt a NIP-04 payload from a peer pubkey, deriving the shared secret from `secretKey`. */\nexport function decryptFromPubkey(\n payload: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n assertSecretKeyBytes(secretKey);\n splitPayload(payload);\n const sharedSecret = normalizeSharedSecret(secretKey, peerPubkey);\n try {\n return decrypt(payload, sharedSecret);\n } finally {\n sharedSecret.fill(0);\n }\n}\n\nfunction splitPayload(data: string): [ciphertext: string, iv: string] {\n const parts = data.split(\"?iv=\");\n const [ciphertext, iv] = parts;\n if (\n parts.length !== 2 ||\n ciphertext === undefined ||\n ciphertext === \"\" ||\n iv === undefined ||\n iv === \"\"\n ) {\n throw new CryptoError(\"invalid NIP-04 payload: missing iv\");\n }\n return [ciphertext, iv];\n}\n"],"mappings":";;;;;;;AAgBA,SAAS,sBAAsB,SAAqB,QAA4B;CAC9E,YAAY,QAAQ,YAAY;CAChC,IAAI;EAEF,OADY,UAAU,gBAAgB,SAASA,aAAW,KAAK,OAAO,YAAY,GAAG,CAC5E,CAAC,CAAC,MAAM,GAAG,EAAE;CACxB,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,6BAA6B,EAAE,OAAO,MAAM,CAAC;CACrE;AACF;;;;;AAMA,SAAgB,QAAQ,WAAmB,cAAkC;CAC3E,iBAAiB,cAAc,IAAI,eAAe;CAClD,MAAM,KAAK,YAAY,EAAE;CACzB,MAAM,iBAAiB,YAAY,OAAO,SAAS;CACnD,MAAM,aAAa,IAAI,cAAc,EAAE,CAAC,CAAC,QAAQ,cAAc;CAC/D,OAAO,GAAG,OAAO,OAAO,UAAU,EAAE,MAAM,OAAO,OAAO,EAAE;AAC5D;;AAGA,SAAgB,QAAQ,SAAiB,cAAkC;CACzE,iBAAiB,cAAc,IAAI,eAAe;CAClD,MAAM,CAAC,gBAAgB,UAAU,aAAa,OAAO;CACrD,IAAI;EACF,MAAM,KAAK,OAAO,OAAO,MAAM;EAC/B,MAAM,aAAa,OAAO,OAAO,cAAc;EAC/C,MAAM,YAAY,IAAI,cAAc,EAAE,CAAC,CAAC,QAAQ,UAAU;EAC1D,OAAO,YAAY,OAAO,SAAS;CACrC,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,0BAA0B,EAAE,OAAO,MAAM,CAAC;CAClE;AACF;;AAGA,SAAgB,gBACd,WACA,WACA,YACQ;CACR,qBAAqB,SAAS;CAC9B,MAAM,eAAe,sBAAsB,WAAW,UAAU;CAChE,IAAI;EACF,OAAO,QAAQ,WAAW,YAAY;CACxC,UAAU;EACR,aAAa,KAAK,CAAC;CACrB;AACF;;AAGA,SAAgB,kBACd,SACA,WACA,YACQ;CACR,qBAAqB,SAAS;CAC9B,aAAa,OAAO;CACpB,MAAM,eAAe,sBAAsB,WAAW,UAAU;CAChE,IAAI;EACF,OAAO,QAAQ,SAAS,YAAY;CACtC,UAAU;EACR,aAAa,KAAK,CAAC;CACrB;AACF;AAEA,SAAS,aAAa,MAAgD;CACpE,MAAM,QAAQ,KAAK,MAAM,MAAM;CAC/B,MAAM,CAAC,YAAY,MAAM;CACzB,IACE,MAAM,WAAW,KACjB,eAAe,KAAA,KACf,eAAe,MACf,OAAO,KAAA,KACP,OAAO,IAEP,MAAM,IAAI,YAAY,oCAAoC;CAE5D,OAAO,CAAC,YAAY,EAAE;AACxB"}
@@ -1,5 +1,5 @@
1
- import { a as NostrError } from "../error-BquXDmIN.mjs";
2
- import { f as ProfilePointer } from "../nip19-DpPN17bY.mjs";
1
+ import { a as NostrError } from "../error-BPqAU5RB.mjs";
2
+ import { f as ProfilePointer } from "../nip19-D4RKn6GC.mjs";
3
3
  import { t as ManualFetch } from "../http-kwWG-V1X.mjs";
4
4
  //#region src/nips/nip05.d.ts
5
5
  /** Root local-part (`_@domain` rendered as just the domain). */
@@ -33,7 +33,10 @@ export type Nip05Fetch = ManualFetch;
33
33
  export declare class Nip05Error extends NostrError {
34
34
  name: string;
35
35
  }
36
- /** Matches optional local@domain. Groups: 1=local (optional), 2=domain. */
36
+ /**
37
+ * Matches optional local@domain. Groups: 1=local (optional), 2=domain. Case classes are written out
38
+ * so matching is ASCII-only: the `i` flag would also fold U+212A (K) into `k`.
39
+ */
37
40
  export declare const NIP05_REGEX: RegExp;
38
41
  export declare function isNip05(value: unknown): value is string;
39
42
  /**
@@ -1,4 +1,4 @@
1
- import { l as isRecord, s as isHex32, y as NostrError } from "../util-DDpPkiED.mjs";
1
+ import { c as isHex32, u as isRecord, x as NostrError } from "../util-DfsyJ9nz.mjs";
2
2
  import { n as requireGlobalFetch, t as fetchManual } from "../http-CuazNRIP.mjs";
3
3
  //#region src/nips/nip05.ts
4
4
  /**
@@ -12,8 +12,11 @@ const WELL_KNOWN_PATH = "/.well-known/nostr.json";
12
12
  var Nip05Error = class extends NostrError {
13
13
  name = "Nip05Error";
14
14
  };
15
- /** Matches optional local@domain. Groups: 1=local (optional), 2=domain. */
16
- const NIP05_REGEX = /^(?:([a-z0-9._-]+)@)?([a-z0-9-]+(?:\.[a-z0-9-]+)+)$/i;
15
+ /**
16
+ * Matches optional local@domain. Groups: 1=local (optional), 2=domain. Case classes are written out
17
+ * so matching is ASCII-only: the `i` flag would also fold U+212A (K) into `k`.
18
+ */
19
+ const NIP05_REGEX = /^(?:([A-Za-z0-9._-]+)@)?([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)$/;
17
20
  function isNip05(value) {
18
21
  if (typeof value !== "string") return false;
19
22
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"nip05.mjs","names":[],"sources":["../../src/nips/nip05.ts"],"sourcesContent":["/**\n * NIP-05: Mapping Nostr keys to DNS-based internet identifiers\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/05.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport { isHex32, isRecord } from \"../core/util.ts\";\nimport { fetchManual, requireGlobalFetch } from \"./http.ts\";\nimport type { ManualFetch } from \"./http.ts\";\nimport type { ProfilePointer } from \"./nip19.ts\";\n\n/** Root local-part (`_@domain` rendered as just the domain). */\nexport const NIP05_ROOT_LOCAL = \"_\";\n\nexport const WELL_KNOWN_PATH = \"/.well-known/nostr.json\";\n\n/**\n * NIP-05 identifier string.\n *\n * - Full: `name@domain`\n * - Root: `domain` or `_@domain`\n */\nexport type Nip05 = string;\n\nexport type Nip05Address = {\n /** Local part (lowercased). `_` for domain-only identifiers. */\n readonly local: string;\n /** Domain (lowercased). */\n readonly domain: string;\n};\n\n/** 46.md appendix discovery metadata (`{relays, nostrconnect_url}`). Not a bunker pointer. */\nexport type Nip05Nip46 = {\n readonly relays?: ReadonlyArray<string> | undefined;\n readonly nostrconnectUrl?: string | undefined;\n};\n\nexport type Nip05Document = {\n readonly names: Readonly<Record<string, string>>;\n /** NIP-05 profile relay hints */\n readonly relays?: Readonly<Record<string, ReadonlyArray<string>>> | undefined;\n readonly nip46?: Nip05Nip46 | undefined;\n};\n\nexport type Nip05Fetch = ManualFetch;\n\nexport class Nip05Error extends NostrError {\n override name = \"Nip05Error\";\n}\n\n/** Matches optional local@domain. Groups: 1=local (optional), 2=domain. */\n// oxlint-disable-next-line no-inferrable-types -- isolatedDeclarations requires the annotation for dts emit\nexport const NIP05_REGEX: RegExp = /^(?:([a-z0-9._-]+)@)?([a-z0-9-]+(?:\\.[a-z0-9-]+)+)$/i;\n\nexport function isNip05(value: unknown): value is string {\n if (typeof value !== \"string\") {\n return false;\n }\n try {\n parseNip05(value);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse an identifier into local + domain. Accepts `name@domain`, `_@domain`, or bare `domain`\n * (local becomes `_`).\n */\nexport function parseNip05(input: string): Nip05Address {\n const match = NIP05_REGEX.exec(input.trim());\n const domain = match?.at(2);\n if (match === null || domain === undefined || domain === \"\") {\n throw new Nip05Error(`invalid NIP-05 identifier: ${input}`);\n }\n const localRaw = match.at(1) ?? NIP05_ROOT_LOCAL;\n return { local: localRaw.toLowerCase(), domain: domain.toLowerCase() };\n}\n\n/** Build the well-known HTTPS URL for an address. */\nexport function wellKnownUrl(address: Nip05Address): string {\n const url = new URL(`https://${address.domain}${WELL_KNOWN_PATH}`);\n url.searchParams.set(\"name\", address.local);\n return url.toString();\n}\n\nfunction stringUrls(list: unknown): string[] | undefined {\n if (!Array.isArray(list)) {\n return undefined;\n }\n return list.filter((u): u is string => typeof u === \"string\" && u.length > 0);\n}\n\n/**\n * Parse nostr.json `nip46`: 46.md appendix `{relays, nostrconnect_url}`. Hex-pubkey maps and other\n * keys are ignored.\n */\nexport function parseNip05Nip46(raw: unknown): Nip05Nip46 | undefined {\n if (!isRecord(raw)) {\n return undefined;\n }\n const obj = raw;\n const result: Mutable<Nip05Nip46> = {};\n\n if (Array.isArray(obj[\"relays\"])) {\n result.relays = stringUrls(obj[\"relays\"]) ?? [];\n }\n\n if (typeof obj[\"nostrconnect_url\"] === \"string\" && obj[\"nostrconnect_url\"].length > 0) {\n result.nostrconnectUrl = obj[\"nostrconnect_url\"];\n }\n\n if (result.relays === undefined && result.nostrconnectUrl === undefined) {\n return undefined;\n }\n return result;\n}\n\n/** Parse and validate a nostr.json document body. */\nexport function parseNip05Document(json: unknown): Nip05Document {\n if (!isRecord(json)) {\n throw new Nip05Error(\"NIP-05 document must be a JSON object\");\n }\n const raw = json;\n if (!isRecord(raw[\"names\"])) {\n throw new Nip05Error(\"NIP-05 document missing names map\");\n }\n\n const names: Record<string, string> = {};\n for (const [k, v] of Object.entries(raw[\"names\"])) {\n if (typeof v !== \"string\" || !isHex32(v.toLowerCase())) {\n continue;\n }\n names[k.toLowerCase()] = v.toLowerCase();\n }\n\n const rawRelays = raw[\"relays\"];\n let relays: Record<string, string[]> | undefined;\n if (isRecord(rawRelays)) {\n relays = {};\n for (const [pk, list] of Object.entries(rawRelays)) {\n if (!isHex32(pk.toLowerCase())) {\n continue;\n }\n const urls = stringUrls(list);\n if (urls !== undefined && urls.length > 0) {\n relays[pk.toLowerCase()] = urls;\n }\n }\n }\n\n const doc: Mutable<Nip05Document> = { names };\n if (relays !== undefined) {\n doc.relays = relays;\n }\n const nip46 = parseNip05Nip46(raw[\"nip46\"]);\n if (nip46 !== undefined) {\n doc.nip46 = nip46;\n }\n return doc;\n}\n\n/** Resolve local name from an already-parsed document. */\nexport function lookupFromDocument(\n doc: Nip05Document,\n address: Nip05Address,\n): ProfilePointer | undefined {\n const pubkey = doc.names[address.local];\n if (pubkey === undefined) {\n return undefined;\n }\n const relays = doc.relays?.[pubkey];\n return relays !== undefined && relays.length > 0 ? { pubkey, relays: [...relays] } : { pubkey };\n}\n\n/**\n * Fetch and parse `/.well-known/nostr.json`. Returns `undefined` on network/parse failure (does not\n * throw for those). Rejects HTTP redirects (non-200) per NIP-05 security constraints. AbortError\n * rethrows; it is not mapped to `undefined`.\n */\nexport async function queryNip05Document(\n identifier: string,\n opts?: { fetch?: Nip05Fetch; signal?: AbortSignal },\n): Promise<{ address: Nip05Address; doc: Nip05Document } | undefined> {\n let address: Nip05Address;\n try {\n address = parseNip05(identifier);\n } catch {\n return undefined;\n }\n\n const url = wellKnownUrl(address);\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip05Error(\"no fetch implementation available; pass opts.fetch\"));\n\n try {\n const res = await fetchManual(fetchImpl, url, { signal: opts?.signal }, (err) =>\n err instanceof Error ? err : new Error(\"NIP-05 request failed\"),\n );\n // Redirects and errors must not be trusted (NIP-05 security).\n if (res.status !== 200) {\n return undefined;\n }\n const json = await res.json();\n return { address, doc: parseNip05Document(json) };\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw error;\n }\n return undefined;\n }\n}\n\n/**\n * Query `/.well-known/nostr.json` for an identifier. Returns `undefined` on network/parse/lookup\n * failure (does not throw for those). Profile `relays` only. `nip46` is discovery metadata, not a\n * bunker pointer.\n */\nexport async function queryProfile(\n identifier: string,\n opts?: { fetch?: Nip05Fetch; signal?: AbortSignal },\n): Promise<ProfilePointer | undefined> {\n const fetched = await queryNip05Document(identifier, opts);\n if (fetched === undefined) {\n return undefined;\n }\n return lookupFromDocument(fetched.doc, fetched.address);\n}\n\n/** True when the identifier resolves to exactly `pubkey`. */\nexport async function verifyNip05(\n pubkey: string,\n identifier: string,\n opts?: { fetch?: Nip05Fetch; signal?: AbortSignal },\n): Promise<boolean> {\n if (!isHex32(pubkey.toLowerCase())) {\n return false;\n }\n const profile = await queryProfile(identifier, opts);\n return profile !== undefined && profile.pubkey === pubkey.toLowerCase();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip05.mjs","names":[],"sources":["../../src/nips/nip05.ts"],"sourcesContent":["/**\n * NIP-05: Mapping Nostr keys to DNS-based internet identifiers\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/05.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport { isHex32, isRecord } from \"../core/util.ts\";\nimport { fetchManual, requireGlobalFetch } from \"./http.ts\";\nimport type { ManualFetch } from \"./http.ts\";\nimport type { ProfilePointer } from \"./nip19.ts\";\n\n/** Root local-part (`_@domain` rendered as just the domain). */\nexport const NIP05_ROOT_LOCAL = \"_\";\n\nexport const WELL_KNOWN_PATH = \"/.well-known/nostr.json\";\n\n/**\n * NIP-05 identifier string.\n *\n * - Full: `name@domain`\n * - Root: `domain` or `_@domain`\n */\nexport type Nip05 = string;\n\nexport type Nip05Address = {\n /** Local part (lowercased). `_` for domain-only identifiers. */\n readonly local: string;\n /** Domain (lowercased). */\n readonly domain: string;\n};\n\n/** 46.md appendix discovery metadata (`{relays, nostrconnect_url}`). Not a bunker pointer. */\nexport type Nip05Nip46 = {\n readonly relays?: ReadonlyArray<string> | undefined;\n readonly nostrconnectUrl?: string | undefined;\n};\n\nexport type Nip05Document = {\n readonly names: Readonly<Record<string, string>>;\n /** NIP-05 profile relay hints */\n readonly relays?: Readonly<Record<string, ReadonlyArray<string>>> | undefined;\n readonly nip46?: Nip05Nip46 | undefined;\n};\n\nexport type Nip05Fetch = ManualFetch;\n\nexport class Nip05Error extends NostrError {\n override name = \"Nip05Error\";\n}\n\n/**\n * Matches optional local@domain. Groups: 1=local (optional), 2=domain. Case classes are written out\n * so matching is ASCII-only: the `i` flag would also fold U+212A (K) into `k`.\n */\n// oxlint-disable-next-line typescript/no-inferrable-types -- isolatedDeclarations requires the annotation for dts emit\nexport const NIP05_REGEX: RegExp = /^(?:([A-Za-z0-9._-]+)@)?([A-Za-z0-9-]+(?:\\.[A-Za-z0-9-]+)+)$/;\n\nexport function isNip05(value: unknown): value is string {\n if (typeof value !== \"string\") {\n return false;\n }\n try {\n parseNip05(value);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse an identifier into local + domain. Accepts `name@domain`, `_@domain`, or bare `domain`\n * (local becomes `_`).\n */\nexport function parseNip05(input: string): Nip05Address {\n const match = NIP05_REGEX.exec(input.trim());\n const domain = match?.at(2);\n if (match === null || domain === undefined || domain === \"\") {\n throw new Nip05Error(`invalid NIP-05 identifier: ${input}`);\n }\n const localRaw = match.at(1) ?? NIP05_ROOT_LOCAL;\n return { local: localRaw.toLowerCase(), domain: domain.toLowerCase() };\n}\n\n/** Build the well-known HTTPS URL for an address. */\nexport function wellKnownUrl(address: Nip05Address): string {\n const url = new URL(`https://${address.domain}${WELL_KNOWN_PATH}`);\n url.searchParams.set(\"name\", address.local);\n return url.toString();\n}\n\nfunction stringUrls(list: unknown): string[] | undefined {\n if (!Array.isArray(list)) {\n return undefined;\n }\n return list.filter((u): u is string => typeof u === \"string\" && u.length > 0);\n}\n\n/**\n * Parse nostr.json `nip46`: 46.md appendix `{relays, nostrconnect_url}`. Hex-pubkey maps and other\n * keys are ignored.\n */\nexport function parseNip05Nip46(raw: unknown): Nip05Nip46 | undefined {\n if (!isRecord(raw)) {\n return undefined;\n }\n const obj = raw;\n const result: Mutable<Nip05Nip46> = {};\n\n if (Array.isArray(obj[\"relays\"])) {\n result.relays = stringUrls(obj[\"relays\"]) ?? [];\n }\n\n if (typeof obj[\"nostrconnect_url\"] === \"string\" && obj[\"nostrconnect_url\"].length > 0) {\n result.nostrconnectUrl = obj[\"nostrconnect_url\"];\n }\n\n if (result.relays === undefined && result.nostrconnectUrl === undefined) {\n return undefined;\n }\n return result;\n}\n\n/** Parse and validate a nostr.json document body. */\nexport function parseNip05Document(json: unknown): Nip05Document {\n if (!isRecord(json)) {\n throw new Nip05Error(\"NIP-05 document must be a JSON object\");\n }\n const raw = json;\n if (!isRecord(raw[\"names\"])) {\n throw new Nip05Error(\"NIP-05 document missing names map\");\n }\n\n const names: Record<string, string> = {};\n for (const [k, v] of Object.entries(raw[\"names\"])) {\n if (typeof v !== \"string\" || !isHex32(v.toLowerCase())) {\n continue;\n }\n names[k.toLowerCase()] = v.toLowerCase();\n }\n\n const rawRelays = raw[\"relays\"];\n let relays: Record<string, string[]> | undefined;\n if (isRecord(rawRelays)) {\n relays = {};\n for (const [pk, list] of Object.entries(rawRelays)) {\n if (!isHex32(pk.toLowerCase())) {\n continue;\n }\n const urls = stringUrls(list);\n if (urls !== undefined && urls.length > 0) {\n relays[pk.toLowerCase()] = urls;\n }\n }\n }\n\n const doc: Mutable<Nip05Document> = { names };\n if (relays !== undefined) {\n doc.relays = relays;\n }\n const nip46 = parseNip05Nip46(raw[\"nip46\"]);\n if (nip46 !== undefined) {\n doc.nip46 = nip46;\n }\n return doc;\n}\n\n/** Resolve local name from an already-parsed document. */\nexport function lookupFromDocument(\n doc: Nip05Document,\n address: Nip05Address,\n): ProfilePointer | undefined {\n const pubkey = doc.names[address.local];\n if (pubkey === undefined) {\n return undefined;\n }\n const relays = doc.relays?.[pubkey];\n return relays !== undefined && relays.length > 0 ? { pubkey, relays: [...relays] } : { pubkey };\n}\n\n/**\n * Fetch and parse `/.well-known/nostr.json`. Returns `undefined` on network/parse failure (does not\n * throw for those). Rejects HTTP redirects (non-200) per NIP-05 security constraints. AbortError\n * rethrows; it is not mapped to `undefined`.\n */\nexport async function queryNip05Document(\n identifier: string,\n opts?: { fetch?: Nip05Fetch; signal?: AbortSignal },\n): Promise<{ address: Nip05Address; doc: Nip05Document } | undefined> {\n let address: Nip05Address;\n try {\n address = parseNip05(identifier);\n } catch {\n return undefined;\n }\n\n const url = wellKnownUrl(address);\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip05Error(\"no fetch implementation available; pass opts.fetch\"));\n\n try {\n const res = await fetchManual(fetchImpl, url, { signal: opts?.signal }, (err) =>\n err instanceof Error ? err : new Error(\"NIP-05 request failed\"),\n );\n // Redirects and errors must not be trusted (NIP-05 security).\n if (res.status !== 200) {\n return undefined;\n }\n const json = await res.json();\n return { address, doc: parseNip05Document(json) };\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw error;\n }\n return undefined;\n }\n}\n\n/**\n * Query `/.well-known/nostr.json` for an identifier. Returns `undefined` on network/parse/lookup\n * failure (does not throw for those). Profile `relays` only. `nip46` is discovery metadata, not a\n * bunker pointer.\n */\nexport async function queryProfile(\n identifier: string,\n opts?: { fetch?: Nip05Fetch; signal?: AbortSignal },\n): Promise<ProfilePointer | undefined> {\n const fetched = await queryNip05Document(identifier, opts);\n if (fetched === undefined) {\n return undefined;\n }\n return lookupFromDocument(fetched.doc, fetched.address);\n}\n\n/** True when the identifier resolves to exactly `pubkey`. */\nexport async function verifyNip05(\n pubkey: string,\n identifier: string,\n opts?: { fetch?: Nip05Fetch; signal?: AbortSignal },\n): Promise<boolean> {\n if (!isHex32(pubkey.toLowerCase())) {\n return false;\n }\n const profile = await queryProfile(identifier, opts);\n return profile !== undefined && profile.pubkey === pubkey.toLowerCase();\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { h as Tag, t as Event } from "../event-j3BwI5dF.mjs";
2
- import { t as EventBuilder } from "../builder-BrmLOVj_.mjs";
3
- import { f as ProfilePointer, i as EventPointer, t as AddressPointer } from "../nip19-DpPN17bY.mjs";
2
+ import { t as EventBuilder } from "../builder-C72nJITG.mjs";
3
+ import { f as ProfilePointer, i as EventPointer, t as AddressPointer } from "../nip19-D4RKn6GC.mjs";
4
4
  //#region src/nips/nip10.d.ts
5
5
  /** Parsed NIP-10 thread references from an event's `e`/`q`/`p` tags. */
6
6
  export type ThreadReferences = {
@@ -1,7 +1,7 @@
1
- import { g as EventValidationError, s as isHex32 } from "../util-DDpPkiED.mjs";
2
- import { t as Kind } from "../kind-D8fENiiL.mjs";
3
- import { S as parseEventAddress, g as Tag, y as formatEventAddress } from "../event-B3ua2_oL.mjs";
4
- import { t as EventBuilder } from "../builder-Cf2N12CG.mjs";
1
+ import { c as isHex32, v as EventValidationError } from "../util-DfsyJ9nz.mjs";
2
+ import { t as Kind } from "../kind-I2Tvbe82.mjs";
3
+ import { S as parseEventAddress, g as Tag, y as formatEventAddress } from "../event-DvM1f03b.mjs";
4
+ import { t as EventBuilder } from "../builder-CGgM_QHT.mjs";
5
5
  //#region src/nips/nip10.ts
6
6
  /**
7
7
  * NIP-10: Text Notes and Threads
@@ -1,4 +1,4 @@
1
- import { a as NostrError } from "../error-BquXDmIN.mjs";
1
+ import { a as NostrError } from "../error-BPqAU5RB.mjs";
2
2
  import { t as ManualFetch } from "../http-kwWG-V1X.mjs";
3
3
  //#region src/nips/nip11.d.ts
4
4
  export type Nip11Fetch = ManualFetch;
@@ -1,4 +1,4 @@
1
- import { l as isRecord, y as NostrError } from "../util-DDpPkiED.mjs";
1
+ import { u as isRecord, x as NostrError } from "../util-DfsyJ9nz.mjs";
2
2
  import { n as requireGlobalFetch, t as fetchManual } from "../http-CuazNRIP.mjs";
3
3
  //#region src/nips/nip11.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { r as UnsignedEvent } from "../event-j3BwI5dF.mjs";
2
- import { a as NostrError } from "../error-BquXDmIN.mjs";
2
+ import { a as NostrError } from "../error-BPqAU5RB.mjs";
3
3
  //#region src/nips/nip13.d.ts
4
4
  export declare class Nip13Error extends NostrError {
5
5
  name: string;
@@ -1,4 +1,4 @@
1
- import { f as nowSeconds, i as bytesToHex, m as utf8Encoder, s as isHex32, y as NostrError } from "../util-DDpPkiED.mjs";
1
+ import { c as isHex32, g as utf8Encoder, i as bytesToHex, p as nowSeconds, x as NostrError } from "../util-DfsyJ9nz.mjs";
2
2
  import { t as abortReason } from "../abort-DBq2STPj.mjs";
3
3
  import { sha256 } from "@noble/hashes/sha2.js";
4
4
  //#region src/nips/nip13.ts
@@ -1 +1 @@
1
- {"version":3,"file":"nip13.mjs","names":[],"sources":["../../src/nips/nip13.ts"],"sourcesContent":["/**\n * NIP-13: Proof of Work. Does not import signer, relay, or client. Does not sign.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/13.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport { abortReason } from \"../core/abort.ts\";\nimport { NostrError } from \"../core/error.ts\";\nimport type { UnsignedEvent } from \"../core/event.ts\";\nimport { bytesToHex, isHex32, nowSeconds, utf8Encoder } from \"../core/util.ts\";\n\nexport class Nip13Error extends NostrError {\n override name = \"Nip13Error\";\n}\n\nexport type MinePowOptions = {\n /** Yield to the event loop after this many hashes. Default 1000. */\n yieldEvery?: number | undefined;\n signal?: AbortSignal | undefined;\n};\n\n/** Leading zero bits of a hex event id or raw sha256 bytes. */\nexport function getPow(idOrHash: string | Uint8Array): number {\n if (typeof idOrHash === \"string\") {\n if (!isHex32(idOrHash.toLowerCase())) {\n throw new Nip13Error(`expected 64-char hex event id, got ${idOrHash.length} chars`);\n }\n let count = 0;\n for (let i = 0; i < idOrHash.length; i += 8) {\n const chunk = Number.parseInt(idOrHash.slice(i, i + 8), 16);\n if (chunk === 0) {\n count += 32;\n } else {\n count += Math.clz32(chunk);\n break;\n }\n }\n return count;\n }\n\n let count = 0;\n for (const byte of idOrHash) {\n if (byte === 0) {\n count += 8;\n } else {\n count += Math.clz32(byte) - 24;\n break;\n }\n }\n return count;\n}\n\n/**\n * Returns a new unsigned event with nonce tag and computed id. Does not mutate input. `created_at`\n * is advanced to the current second whenever it elapses while mining. Yields every `yieldEvery`\n * hashes.\n */\nexport async function minePow(\n unsigned: UnsignedEvent,\n difficulty: number,\n opts?: MinePowOptions,\n): Promise<UnsignedEvent & { id: string }> {\n const signal = opts?.signal;\n const yieldEvery = Math.max(1, opts?.yieldEvery ?? 1000);\n\n const nonce: [string, string, string] = [\"nonce\", \"0\", String(difficulty)];\n const mined = {\n kind: unsigned.kind,\n tags: [...unsigned.tags, nonce],\n content: unsigned.content,\n created_at: unsigned.created_at,\n pubkey: unsigned.pubkey,\n };\n\n // Serialize once per created_at second: the reused buffer holds\n // [0, pubkey, created_at, kind, [...tags, [\"nonce\",\"<digits>\",\"<difficulty>\"]], content]\n // where `head` ends at the opening quote of the nonce value and `tail` completes the tag, the\n // tags array, and the trailing content element.\n let head = new Uint8Array(0);\n let tail = new Uint8Array(0);\n let buf = new Uint8Array(0);\n\n const rebuild = (): void => {\n const headText = JSON.stringify([\n 0,\n mined.pubkey,\n mined.created_at,\n mined.kind,\n [...unsigned.tags, [\"nonce\", \"\"]],\n ]).slice(0, -4);\n head = utf8Encoder.encode(headText);\n tail = utf8Encoder.encode(`\",\"${difficulty}\"]],${JSON.stringify(mined.content)}]`);\n buf = new Uint8Array(head.length + 24 + tail.length);\n buf.set(head, 0);\n };\n rebuild();\n\n let count = 0;\n let iterations = 0;\n\n while (true) {\n if (signal?.aborted === true) {\n throw abortReason(signal);\n }\n\n const now = nowSeconds();\n if (now !== mined.created_at) {\n count = 0;\n mined.created_at = now;\n rebuild();\n }\n\n nonce[1] = String(++count);\n let pos = head.length;\n for (let i = 0; i < nonce[1].length; i++) {\n buf[pos] = nonce[1].codePointAt(i) ?? 0;\n pos += 1;\n }\n buf.set(tail, pos);\n pos += tail.length;\n const hash = sha256(buf.subarray(0, pos));\n if (getPow(hash) >= difficulty) {\n return { ...mined, id: bytesToHex(hash) };\n }\n\n iterations++;\n if (iterations >= yieldEvery) {\n iterations = 0;\n // oxlint-disable-next-line no-await-in-loop -- the yield is part of each mining round\n await new Promise<void>((resolve) => {\n setTimeout(resolve);\n });\n }\n }\n}\n"],"mappings":";;;;;;;;;AAYA,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;AASA,SAAgB,OAAO,UAAuC;CAC5D,IAAI,OAAO,aAAa,UAAU;EAChC,IAAI,CAAC,QAAQ,SAAS,YAAY,CAAC,GACjC,MAAM,IAAI,WAAW,sCAAsC,SAAS,OAAO,OAAO;EAEpF,IAAI,QAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;GAC3C,MAAM,QAAQ,OAAO,SAAS,SAAS,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE;GAC1D,IAAI,UAAU,GACZ,SAAS;QACJ;IACL,SAAS,KAAK,MAAM,KAAK;IACzB;GACF;EACF;EACA,OAAO;CACT;CAEA,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,UACjB,IAAI,SAAS,GACX,SAAS;MACJ;EACL,SAAS,KAAK,MAAM,IAAI,IAAI;EAC5B;CACF;CAEF,OAAO;AACT;;;;;;AAOA,eAAsB,QACpB,UACA,YACA,MACyC;CACzC,MAAM,SAAS,MAAM;CACrB,MAAM,aAAa,KAAK,IAAI,GAAG,MAAM,cAAc,GAAI;CAEvD,MAAM,QAAkC;EAAC;EAAS;EAAK,OAAO,UAAU;CAAC;CACzE,MAAM,QAAQ;EACZ,MAAM,SAAS;EACf,MAAM,CAAC,GAAG,SAAS,MAAM,KAAK;EAC9B,SAAS,SAAS;EAClB,YAAY,SAAS;EACrB,QAAQ,SAAS;CACnB;CAMA,IAAI,uBAAO,IAAI,WAAW,CAAC;CAC3B,IAAI,uBAAO,IAAI,WAAW,CAAC;CAC3B,IAAI,sBAAM,IAAI,WAAW,CAAC;CAE1B,MAAM,gBAAsB;EAC1B,MAAM,WAAW,KAAK,UAAU;GAC9B;GACA,MAAM;GACN,MAAM;GACN,MAAM;GACN,CAAC,GAAG,SAAS,MAAM,CAAC,SAAS,EAAE,CAAC;EAClC,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE;EACd,OAAO,YAAY,OAAO,QAAQ;EAClC,OAAO,YAAY,OAAO,MAAM,WAAW,MAAM,KAAK,UAAU,MAAM,OAAO,EAAE,EAAE;EACjF,MAAM,IAAI,WAAW,KAAK,SAAS,KAAK,KAAK,MAAM;EACnD,IAAI,IAAI,MAAM,CAAC;CACjB;CACA,QAAQ;CAER,IAAI,QAAQ;CACZ,IAAI,aAAa;CAEjB,OAAO,MAAM;EACX,IAAI,QAAQ,YAAY,MACtB,MAAM,YAAY,MAAM;EAG1B,MAAM,MAAM,WAAW;EACvB,IAAI,QAAQ,MAAM,YAAY;GAC5B,QAAQ;GACR,MAAM,aAAa;GACnB,QAAQ;EACV;EAEA,MAAM,KAAK,OAAO,EAAE,KAAK;EACzB,IAAI,MAAM,KAAK;EACf,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC,QAAQ,KAAK;GACxC,IAAI,OAAO,MAAM,EAAE,CAAC,YAAY,CAAC,KAAK;GACtC,OAAO;EACT;EACA,IAAI,IAAI,MAAM,GAAG;EACjB,OAAO,KAAK;EACZ,MAAM,OAAO,OAAO,IAAI,SAAS,GAAG,GAAG,CAAC;EACxC,IAAI,OAAO,IAAI,KAAK,YAClB,OAAO;GAAE,GAAG;GAAO,IAAI,WAAW,IAAI;EAAE;EAG1C;EACA,IAAI,cAAc,YAAY;GAC5B,aAAa;GAEb,MAAM,IAAI,SAAe,YAAY;IACnC,WAAW,OAAO;GACpB,CAAC;EACH;CACF;AACF"}
1
+ {"version":3,"file":"nip13.mjs","names":[],"sources":["../../src/nips/nip13.ts"],"sourcesContent":["/**\n * NIP-13: Proof of Work. Does not import signer, relay, or client. Does not sign.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/13.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n\nimport { abortReason } from \"../core/abort.ts\";\nimport { NostrError } from \"../core/error.ts\";\nimport type { UnsignedEvent } from \"../core/event.ts\";\nimport { bytesToHex, isHex32, nowSeconds, utf8Encoder } from \"../core/util.ts\";\n\nexport class Nip13Error extends NostrError {\n override name = \"Nip13Error\";\n}\n\nexport type MinePowOptions = {\n /** Yield to the event loop after this many hashes. Default 1000. */\n yieldEvery?: number | undefined;\n signal?: AbortSignal | undefined;\n};\n\n/** Leading zero bits of a hex event id or raw sha256 bytes. */\nexport function getPow(idOrHash: string | Uint8Array): number {\n if (typeof idOrHash === \"string\") {\n if (!isHex32(idOrHash.toLowerCase())) {\n throw new Nip13Error(`expected 64-char hex event id, got ${idOrHash.length} chars`);\n }\n let count = 0;\n for (let i = 0; i < idOrHash.length; i += 8) {\n const chunk = Number.parseInt(idOrHash.slice(i, i + 8), 16);\n if (chunk === 0) {\n count += 32;\n } else {\n count += Math.clz32(chunk);\n break;\n }\n }\n return count;\n }\n\n let count = 0;\n for (const byte of idOrHash) {\n if (byte === 0) {\n count += 8;\n } else {\n count += Math.clz32(byte) - 24;\n break;\n }\n }\n return count;\n}\n\n/**\n * Returns a new unsigned event with nonce tag and computed id. Does not mutate input. `created_at`\n * is advanced to the current second whenever it elapses while mining. Yields every `yieldEvery`\n * hashes.\n */\nexport async function minePow(\n unsigned: UnsignedEvent,\n difficulty: number,\n opts?: MinePowOptions,\n): Promise<UnsignedEvent & { id: string }> {\n const signal = opts?.signal;\n const yieldEvery = Math.max(1, opts?.yieldEvery ?? 1000);\n\n const nonce: [string, string, string] = [\"nonce\", \"0\", String(difficulty)];\n const mined = {\n kind: unsigned.kind,\n tags: [...unsigned.tags, nonce],\n content: unsigned.content,\n created_at: unsigned.created_at,\n pubkey: unsigned.pubkey,\n };\n\n // Serialize once per created_at second: the reused buffer holds\n // [0, pubkey, created_at, kind, [...tags, [\"nonce\",\"<digits>\",\"<difficulty>\"]], content]\n // where `head` ends at the opening quote of the nonce value and `tail` completes the tag, the\n // tags array, and the trailing content element.\n let head = new Uint8Array(0);\n let tail = new Uint8Array(0);\n let buf = new Uint8Array(0);\n\n const rebuild = (): void => {\n const headText = JSON.stringify([\n 0,\n mined.pubkey,\n mined.created_at,\n mined.kind,\n [...unsigned.tags, [\"nonce\", \"\"]],\n ]).slice(0, -4);\n head = utf8Encoder.encode(headText);\n tail = utf8Encoder.encode(`\",\"${difficulty}\"]],${JSON.stringify(mined.content)}]`);\n buf = new Uint8Array(head.length + 24 + tail.length);\n buf.set(head, 0);\n };\n rebuild();\n\n let count = 0;\n let iterations = 0;\n\n while (true) {\n if (signal?.aborted === true) {\n throw abortReason(signal);\n }\n\n const now = nowSeconds();\n if (now !== mined.created_at) {\n count = 0;\n mined.created_at = now;\n rebuild();\n }\n\n nonce[1] = String(++count);\n let pos = head.length;\n for (let i = 0; i < nonce[1].length; i++) {\n buf[pos] = nonce[1].codePointAt(i) ?? 0;\n pos += 1;\n }\n buf.set(tail, pos);\n pos += tail.length;\n const hash = sha256(buf.subarray(0, pos));\n if (getPow(hash) >= difficulty) {\n return { ...mined, id: bytesToHex(hash) };\n }\n\n iterations++;\n if (iterations >= yieldEvery) {\n iterations = 0;\n // oxlint-disable-next-line eslint/no-await-in-loop -- the yield is part of each mining round\n await new Promise<void>((resolve) => {\n setTimeout(resolve);\n });\n }\n }\n}\n"],"mappings":";;;;;;;;;AAYA,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;AASA,SAAgB,OAAO,UAAuC;CAC5D,IAAI,OAAO,aAAa,UAAU;EAChC,IAAI,CAAC,QAAQ,SAAS,YAAY,CAAC,GACjC,MAAM,IAAI,WAAW,sCAAsC,SAAS,OAAO,OAAO;EAEpF,IAAI,QAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;GAC3C,MAAM,QAAQ,OAAO,SAAS,SAAS,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE;GAC1D,IAAI,UAAU,GACZ,SAAS;QACJ;IACL,SAAS,KAAK,MAAM,KAAK;IACzB;GACF;EACF;EACA,OAAO;CACT;CAEA,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,UACjB,IAAI,SAAS,GACX,SAAS;MACJ;EACL,SAAS,KAAK,MAAM,IAAI,IAAI;EAC5B;CACF;CAEF,OAAO;AACT;;;;;;AAOA,eAAsB,QACpB,UACA,YACA,MACyC;CACzC,MAAM,SAAS,MAAM;CACrB,MAAM,aAAa,KAAK,IAAI,GAAG,MAAM,cAAc,GAAI;CAEvD,MAAM,QAAkC;EAAC;EAAS;EAAK,OAAO,UAAU;CAAC;CACzE,MAAM,QAAQ;EACZ,MAAM,SAAS;EACf,MAAM,CAAC,GAAG,SAAS,MAAM,KAAK;EAC9B,SAAS,SAAS;EAClB,YAAY,SAAS;EACrB,QAAQ,SAAS;CACnB;CAMA,IAAI,uBAAO,IAAI,WAAW,CAAC;CAC3B,IAAI,uBAAO,IAAI,WAAW,CAAC;CAC3B,IAAI,sBAAM,IAAI,WAAW,CAAC;CAE1B,MAAM,gBAAsB;EAC1B,MAAM,WAAW,KAAK,UAAU;GAC9B;GACA,MAAM;GACN,MAAM;GACN,MAAM;GACN,CAAC,GAAG,SAAS,MAAM,CAAC,SAAS,EAAE,CAAC;EAClC,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE;EACd,OAAO,YAAY,OAAO,QAAQ;EAClC,OAAO,YAAY,OAAO,MAAM,WAAW,MAAM,KAAK,UAAU,MAAM,OAAO,EAAE,EAAE;EACjF,MAAM,IAAI,WAAW,KAAK,SAAS,KAAK,KAAK,MAAM;EACnD,IAAI,IAAI,MAAM,CAAC;CACjB;CACA,QAAQ;CAER,IAAI,QAAQ;CACZ,IAAI,aAAa;CAEjB,OAAO,MAAM;EACX,IAAI,QAAQ,YAAY,MACtB,MAAM,YAAY,MAAM;EAG1B,MAAM,MAAM,WAAW;EACvB,IAAI,QAAQ,MAAM,YAAY;GAC5B,QAAQ;GACR,MAAM,aAAa;GACnB,QAAQ;EACV;EAEA,MAAM,KAAK,OAAO,EAAE,KAAK;EACzB,IAAI,MAAM,KAAK;EACf,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC,QAAQ,KAAK;GACxC,IAAI,OAAO,MAAM,EAAE,CAAC,YAAY,CAAC,KAAK;GACtC,OAAO;EACT;EACA,IAAI,IAAI,MAAM,GAAG;EACjB,OAAO,KAAK;EACZ,MAAM,OAAO,OAAO,IAAI,SAAS,GAAG,GAAG,CAAC;EACxC,IAAI,OAAO,IAAI,KAAK,YAClB,OAAO;GAAE,GAAG;GAAO,IAAI,WAAW,IAAI;EAAE;EAG1C;EACA,IAAI,cAAc,YAAY;GAC5B,aAAa;GAEb,MAAM,IAAI,SAAe,YAAY;IACnC,WAAW,OAAO;GACpB,CAAC;EACH;CACF;AACF"}
@@ -1,2 +1,2 @@
1
- import { a as buildChatMessageRumor, c as normalizeRecipients, d as wrapDirectMessage, i as ReplyTo, l as parseDmRelayList, n as Nip17Error, o as dmRelayListEventBuilder, r as Recipient, s as dmRelayListToTags, t as ChatMessageOptions, u as requireDmRelays } from "../nip17-BM4hJYBq.mjs";
1
+ import { a as buildChatMessageRumor, c as normalizeRecipients, d as wrapDirectMessage, i as ReplyTo, l as parseDmRelayList, n as Nip17Error, o as dmRelayListEventBuilder, r as Recipient, s as dmRelayListToTags, t as ChatMessageOptions, u as requireDmRelays } from "../nip17-BZvFQLcs.mjs";
2
2
  export { ChatMessageOptions, Nip17Error, Recipient, ReplyTo, buildChatMessageRumor, dmRelayListEventBuilder, dmRelayListToTags, normalizeRecipients, parseDmRelayList, requireDmRelays, wrapDirectMessage };
@@ -1,7 +1,7 @@
1
- import { g as EventValidationError, n as assertHex32, u as normalizeRelayUrls, y as NostrError } from "../util-DDpPkiED.mjs";
2
- import { t as Kind } from "../kind-D8fENiiL.mjs";
3
- import { g as Tag } from "../event-B3ua2_oL.mjs";
4
- import { t as EventBuilder } from "../builder-Cf2N12CG.mjs";
1
+ import { d as normalizeRelayUrls, n as assertHex32, v as EventValidationError, x as NostrError } from "../util-DfsyJ9nz.mjs";
2
+ import { t as Kind } from "../kind-I2Tvbe82.mjs";
3
+ import { g as Tag } from "../event-DvM1f03b.mjs";
4
+ import { t as EventBuilder } from "../builder-CGgM_QHT.mjs";
5
5
  import { createGiftWrap, createRumor, createSeal } from "./nip59.mjs";
6
6
  //#region src/nips/nip17.ts
7
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"nip17.mjs","names":["TagBuilder"],"sources":["../../src/nips/nip17.ts"],"sourcesContent":["/**\n * NIP-17: Private Direct Messages. Kind 10050 advertises where gift-wraps should be delivered. Kind\n * 14 rumor construction and per-recipient wrap live here. Envelope primitives live in nip59.ts.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/17.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError, NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { Tag as TagBuilder } from \"../core/tag.ts\";\nimport { assertHex32, normalizeRelayUrls } from \"../core/util.ts\";\nimport { createGiftWrap, createRumor, createSeal } from \"./nip59.ts\";\nimport type { Nip59Crypto, Rumor, WrapOptions } from \"./nip59.ts\";\n\nexport class Nip17Error extends NostrError {\n override name = \"Nip17Error\";\n}\n\nexport type Recipient = {\n readonly pubkey: string;\n readonly relayHint?: string | undefined;\n};\n\nexport type ReplyTo = {\n readonly id: string;\n readonly relayHint?: string | undefined;\n};\n\nexport type ChatMessageOptions = {\n readonly created_at?: number | undefined;\n readonly subject?: string | undefined;\n readonly replyTo?: ReplyTo | undefined;\n};\n\n/** Parse kind:10050 DM relay list (`[\"relay\", url]` tags). */\nexport function parseDmRelayList(event: Pick<Event, \"kind\" | \"tags\">): string[] {\n if (event.kind !== Kind.DirectMessageRelaysList) {\n throw new EventValidationError(\n `expected kind ${Kind.DirectMessageRelaysList}, got ${event.kind}`,\n );\n }\n const urls: string[] = [];\n for (const tag of event.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\n/** Encode DM relay URLs as NIP-17 `relay` tags. */\nexport function dmRelayListToTags(relays: ReadonlyArray<string>): Tag[] {\n return normalizeRelayUrls(relays).map((url) => [\"relay\", url]);\n}\n\n/** Build an unsigned kind:10050 EventBuilder. NIP-17 requires ≥1 relay tag. */\nexport function dmRelayListEventBuilder(relays: ReadonlyArray<string>): EventBuilder {\n const tags = dmRelayListToTags(relays);\n if (tags.length === 0) {\n throw new Nip17Error(\"DM relay list requires at least one relay\");\n }\n return new EventBuilder(Kind.DirectMessageRelaysList, \"\").tags(tags);\n}\n\nfunction isRecipient(value: unknown): value is Recipient {\n return typeof value === \"object\" && value !== null && \"pubkey\" in value;\n}\n\nfunction asRecipientList(\n input: string | Recipient | ReadonlyArray<string | Recipient>,\n): ReadonlyArray<string | Recipient> {\n if (typeof input === \"string\" || isRecipient(input)) {\n return [input];\n }\n return input;\n}\n\n/** Accept a hex pubkey, a Recipient, or a readonly array of either. Dedup by pubkey. */\nexport function normalizeRecipients(\n input: string | Recipient | ReadonlyArray<string | Recipient>,\n): Recipient[] {\n const out: Recipient[] = [];\n const seen = new Set<string>();\n for (const item of asRecipientList(input)) {\n const rec: Recipient = typeof item === \"string\" ? { pubkey: item } : item;\n const pubkey = assertHex32(rec.pubkey, \"public key\");\n if (seen.has(pubkey)) {\n continue;\n }\n seen.add(pubkey);\n out.push({ pubkey, relayHint: rec.relayHint });\n }\n return out;\n}\n\nexport function buildChatMessageRumor(\n senderPubkey: string,\n recipients: ReadonlyArray<Recipient>,\n content: string,\n opts?: ChatMessageOptions,\n): Rumor {\n if (recipients.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n const tags: Tag[] = [];\n for (const recipient of recipients) {\n const pk = assertHex32(recipient.pubkey, \"public key\");\n tags.push(TagBuilder.p(pk, recipient.relayHint));\n }\n if (opts?.replyTo) {\n // Kind 14 reply e-tag is unmarked: [\"e\", <id>, <relay-url>]\n tags.push(TagBuilder.e(assertHex32(opts.replyTo.id, \"event id\"), opts.replyTo.relayHint ?? \"\"));\n }\n if (opts?.subject !== undefined) {\n tags.push([\"subject\", opts.subject]);\n }\n return createRumor(senderPubkey, {\n kind: Kind.PrivateDirectMessage,\n content,\n tags,\n created_at: opts?.created_at,\n });\n}\n\nfunction wrapTargets(sender: string, recipients: ReadonlyArray<Recipient>): Recipient[] {\n const senderPk = sender.toLowerCase();\n const self = recipients.find((r) => r.pubkey.toLowerCase() === senderPk);\n const out: Recipient[] = [{ pubkey: senderPk, relayHint: self?.relayHint }];\n const seen = new Set<string>([senderPk]);\n for (const recipient of recipients) {\n const pk = recipient.pubkey.toLowerCase();\n if (seen.has(pk)) {\n continue;\n }\n seen.add(pk);\n out.push({ pubkey: pk, relayHint: recipient.relayHint });\n }\n return out;\n}\n\nexport async function wrapDirectMessage(\n crypto: Nip59Crypto,\n recipients: ReadonlyArray<Recipient>,\n rumor: Rumor,\n opts?: Pick<\n WrapOptions,\n \"now\" | \"randomBytes\" | \"timestamps\" | \"expiration\" | \"ephemeral\" | \"randomize\"\n >,\n): Promise<ReadonlyArray<{ recipient: string; wrap: Event }>> {\n if (recipients.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n const targets = wrapTargets(rumor.pubkey, recipients);\n const out: Array<{ recipient: string; wrap: Event }> = [];\n const timeOpts = opts\n ? {\n now: opts.now,\n randomBytes: opts.randomBytes,\n timestamps: opts.timestamps,\n expiration: opts.expiration,\n randomize: opts.randomize,\n }\n : undefined;\n for (const target of targets) {\n // oxlint-disable-next-line no-await-in-loop -- signer calls stay ordered, one recipient at a time\n const seal = await createSeal(crypto, target.pubkey, rumor, timeOpts);\n const wrap = createGiftWrap(seal, target.pubkey, {\n ...timeOpts,\n relayHint: target.relayHint,\n ephemeral: opts?.ephemeral,\n });\n out.push({ recipient: target.pubkey, wrap });\n }\n return out;\n}\n\nexport function requireDmRelays(pubkey: string, relays: ReadonlyArray<string>): string[] {\n if (relays.length === 0) {\n throw new Nip17Error(`pubkey ${pubkey} is not ready to receive DMs (no kind 10050)`);\n }\n return [...relays];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip17.mjs","names":["TagBuilder"],"sources":["../../src/nips/nip17.ts"],"sourcesContent":["/**\n * NIP-17: Private Direct Messages. Kind 10050 advertises where gift-wraps should be delivered. Kind\n * 14 rumor construction and per-recipient wrap live here. Envelope primitives live in nip59.ts.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/17.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError, NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { Tag as TagBuilder } from \"../core/tag.ts\";\nimport { assertHex32, normalizeRelayUrls } from \"../core/util.ts\";\nimport { createGiftWrap, createRumor, createSeal } from \"./nip59.ts\";\nimport type { Nip59Crypto, Rumor, WrapOptions } from \"./nip59.ts\";\n\nexport class Nip17Error extends NostrError {\n override name = \"Nip17Error\";\n}\n\nexport type Recipient = {\n readonly pubkey: string;\n readonly relayHint?: string | undefined;\n};\n\nexport type ReplyTo = {\n readonly id: string;\n readonly relayHint?: string | undefined;\n};\n\nexport type ChatMessageOptions = {\n readonly created_at?: number | undefined;\n readonly subject?: string | undefined;\n readonly replyTo?: ReplyTo | undefined;\n};\n\n/** Parse kind:10050 DM relay list (`[\"relay\", url]` tags). */\nexport function parseDmRelayList(event: Pick<Event, \"kind\" | \"tags\">): string[] {\n if (event.kind !== Kind.DirectMessageRelaysList) {\n throw new EventValidationError(\n `expected kind ${Kind.DirectMessageRelaysList}, got ${event.kind}`,\n );\n }\n const urls: string[] = [];\n for (const tag of event.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\n/** Encode DM relay URLs as NIP-17 `relay` tags. */\nexport function dmRelayListToTags(relays: ReadonlyArray<string>): Tag[] {\n return normalizeRelayUrls(relays).map((url) => [\"relay\", url]);\n}\n\n/** Build an unsigned kind:10050 EventBuilder. NIP-17 requires ≥1 relay tag. */\nexport function dmRelayListEventBuilder(relays: ReadonlyArray<string>): EventBuilder {\n const tags = dmRelayListToTags(relays);\n if (tags.length === 0) {\n throw new Nip17Error(\"DM relay list requires at least one relay\");\n }\n return new EventBuilder(Kind.DirectMessageRelaysList, \"\").tags(tags);\n}\n\nfunction isRecipient(value: unknown): value is Recipient {\n return typeof value === \"object\" && value !== null && \"pubkey\" in value;\n}\n\nfunction asRecipientList(\n input: string | Recipient | ReadonlyArray<string | Recipient>,\n): ReadonlyArray<string | Recipient> {\n if (typeof input === \"string\" || isRecipient(input)) {\n return [input];\n }\n return input;\n}\n\n/** Accept a hex pubkey, a Recipient, or a readonly array of either. Dedup by pubkey. */\nexport function normalizeRecipients(\n input: string | Recipient | ReadonlyArray<string | Recipient>,\n): Recipient[] {\n const out: Recipient[] = [];\n const seen = new Set<string>();\n for (const item of asRecipientList(input)) {\n const rec: Recipient = typeof item === \"string\" ? { pubkey: item } : item;\n const pubkey = assertHex32(rec.pubkey, \"public key\");\n if (seen.has(pubkey)) {\n continue;\n }\n seen.add(pubkey);\n out.push({ pubkey, relayHint: rec.relayHint });\n }\n return out;\n}\n\nexport function buildChatMessageRumor(\n senderPubkey: string,\n recipients: ReadonlyArray<Recipient>,\n content: string,\n opts?: ChatMessageOptions,\n): Rumor {\n if (recipients.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n const tags: Tag[] = [];\n for (const recipient of recipients) {\n const pk = assertHex32(recipient.pubkey, \"public key\");\n tags.push(TagBuilder.p(pk, recipient.relayHint));\n }\n if (opts?.replyTo) {\n // Kind 14 reply e-tag is unmarked: [\"e\", <id>, <relay-url>]\n tags.push(TagBuilder.e(assertHex32(opts.replyTo.id, \"event id\"), opts.replyTo.relayHint ?? \"\"));\n }\n if (opts?.subject !== undefined) {\n tags.push([\"subject\", opts.subject]);\n }\n return createRumor(senderPubkey, {\n kind: Kind.PrivateDirectMessage,\n content,\n tags,\n created_at: opts?.created_at,\n });\n}\n\nfunction wrapTargets(sender: string, recipients: ReadonlyArray<Recipient>): Recipient[] {\n const senderPk = sender.toLowerCase();\n const self = recipients.find((r) => r.pubkey.toLowerCase() === senderPk);\n const out: Recipient[] = [{ pubkey: senderPk, relayHint: self?.relayHint }];\n const seen = new Set<string>([senderPk]);\n for (const recipient of recipients) {\n const pk = recipient.pubkey.toLowerCase();\n if (seen.has(pk)) {\n continue;\n }\n seen.add(pk);\n out.push({ pubkey: pk, relayHint: recipient.relayHint });\n }\n return out;\n}\n\nexport async function wrapDirectMessage(\n crypto: Nip59Crypto,\n recipients: ReadonlyArray<Recipient>,\n rumor: Rumor,\n opts?: Pick<\n WrapOptions,\n \"now\" | \"randomBytes\" | \"timestamps\" | \"expiration\" | \"ephemeral\" | \"randomize\"\n >,\n): Promise<ReadonlyArray<{ recipient: string; wrap: Event }>> {\n if (recipients.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n const targets = wrapTargets(rumor.pubkey, recipients);\n const out: Array<{ recipient: string; wrap: Event }> = [];\n const timeOpts = opts\n ? {\n now: opts.now,\n randomBytes: opts.randomBytes,\n timestamps: opts.timestamps,\n expiration: opts.expiration,\n randomize: opts.randomize,\n }\n : undefined;\n for (const target of targets) {\n // oxlint-disable-next-line eslint/no-await-in-loop -- signer calls stay ordered, one recipient at a time\n const seal = await createSeal(crypto, target.pubkey, rumor, timeOpts);\n const wrap = createGiftWrap(seal, target.pubkey, {\n ...timeOpts,\n relayHint: target.relayHint,\n ephemeral: opts?.ephemeral,\n });\n out.push({ recipient: target.pubkey, wrap });\n }\n return out;\n}\n\nexport function requireDmRelays(pubkey: string, relays: ReadonlyArray<string>): string[] {\n if (relays.length === 0) {\n throw new Nip17Error(`pubkey ${pubkey} is not ready to receive DMs (no kind 10050)`);\n }\n return [...relays];\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { _ as noteEncode, a as NAddr, b as nsecEncode, c as NPub, d as Note, f as ProfilePointer, g as neventEncode, h as naddrEncode, i as EventPointer, l as NSec, m as encodeBytes, n as Bech32MaxSize, o as NEvent, p as decode, r as DecodedResult, s as NProfile, t as AddressPointer, u as Nip19Error, v as nprofileEncode, y as npubEncode } from "../nip19-DpPN17bY.mjs";
1
+ import { _ as noteEncode, a as NAddr, b as nsecEncode, c as NPub, d as Note, f as ProfilePointer, g as neventEncode, h as naddrEncode, i as EventPointer, l as NSec, m as encodeBytes, n as Bech32MaxSize, o as NEvent, p as decode, r as DecodedResult, s as NProfile, t as AddressPointer, u as Nip19Error, v as nprofileEncode, y as npubEncode } from "../nip19-D4RKn6GC.mjs";
2
2
  export { AddressPointer, Bech32MaxSize, DecodedResult, EventPointer, NAddr, NEvent, NProfile, NPub, NSec, Nip19Error, Note, ProfilePointer, decode, encodeBytes, naddrEncode, neventEncode, noteEncode, nprofileEncode, npubEncode, nsecEncode };
@@ -1,4 +1,4 @@
1
- import { S as errorMessage, i as bytesToHex, m as utf8Encoder, n as assertHex32, o as hexToBytes, p as utf8Decoder, t as assertByteLength, y as NostrError } from "../util-DDpPkiED.mjs";
1
+ import { C as errorMessage, g as utf8Encoder, h as utf8Decoder, i as bytesToHex, n as assertHex32, s as hexToBytes, t as assertByteLength, x as NostrError } from "../util-DfsyJ9nz.mjs";
2
2
  import { secp256k1 } from "@noble/curves/secp256k1.js";
3
3
  import { bech32 } from "@scure/base";
4
4
  //#region src/nips/nip19.ts
@@ -34,7 +34,7 @@ function assertNip19Kind(kind) {
34
34
  }
35
35
  function encodeTLV(tlv) {
36
36
  const entries = [];
37
- for (const [t, vs] of Object.entries(tlv).reverse()) for (const v of vs) {
37
+ for (const [t, vs] of Object.entries(tlv)) for (const v of vs) {
38
38
  if (v.length > 255) throw new Nip19Error("TLV value exceeds 255 bytes");
39
39
  const entry = new Uint8Array(v.length + 2);
40
40
  entry[0] = Math.trunc(Number(t));
@@ -1 +1 @@
1
- {"version":3,"file":"nip19.mjs","names":[],"sources":["../../src/nips/nip19.ts"],"sourcesContent":["import { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { bech32 } from \"@scure/base\";\n\nimport { NostrError, errorMessage } from \"../core/error.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport {\n assertByteLength,\n assertHex32,\n bytesToHex,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\n/** Bech32 `nprofile1…` string (TLV profile pointer). */\nexport type NProfile = `nprofile1${string}`;\n/** Bech32 `nevent1…` string (TLV event pointer). */\nexport type NEvent = `nevent1${string}`;\n/** Bech32 `naddr1…` string (TLV address pointer). */\nexport type NAddr = `naddr1${string}`;\n/** Bech32 `nsec1…` string (32-byte secret key). */\nexport type NSec = `nsec1${string}`;\n/** Bech32 `npub1…` string (32-byte public key). */\nexport type NPub = `npub1${string}`;\n/** Bech32 `note1…` string (32-byte event id). */\nexport type Note = `note1${string}`;\n\n/** Maximum bech32 string length accepted by {@link decode} (NIP-19). */\nexport const Bech32MaxSize = 5000;\n\n/** Decoded `nprofile` payload: a pubkey plus optional relay hints. */\nexport type ProfilePointer = {\n readonly pubkey: string;\n readonly relays?: ReadonlyArray<string> | undefined;\n};\n\n/** Decoded `nevent` payload: an event id plus optional relay/author/kind hints. */\nexport type EventPointer = {\n readonly id: string;\n readonly relays?: ReadonlyArray<string> | undefined;\n readonly author?: string | undefined;\n readonly kind?: number | undefined;\n};\n\n/** Decoded `naddr` payload: a replaceable-event coordinate plus optional relay hints. */\nexport type AddressPointer = {\n readonly identifier: string;\n readonly pubkey: string;\n readonly kind: number;\n readonly relays?: ReadonlyArray<string> | undefined;\n};\n\n/** Result of {@link decode}: the bech32 prefix and its decoded payload. */\nexport type DecodedResult =\n | { readonly type: \"nprofile\"; readonly data: ProfilePointer }\n | { readonly type: \"nevent\"; readonly data: EventPointer }\n | { readonly type: \"naddr\"; readonly data: AddressPointer }\n | { readonly type: \"nsec\"; readonly data: Uint8Array }\n | { readonly type: \"npub\"; readonly data: string }\n | { readonly type: \"note\"; readonly data: string };\n\n/** Error thrown by NIP-19 encoding/decoding failures. */\nexport class Nip19Error extends NostrError {\n override name = \"Nip19Error\";\n}\n\ntype TLV = Record<number, Uint8Array[]>;\n\nfunction integerToUint8Array(number: number): Uint8Array {\n const out = new Uint8Array(4);\n new DataView(out.buffer).setUint32(0, number);\n return out;\n}\n\nfunction parseTLV(data: Uint8Array): TLV {\n const result: TLV = {};\n let rest = data;\n while (rest.length > 0) {\n if (rest.length < 2) {\n throw new Nip19Error(\"not enough data to read TLV\");\n }\n const t = rest.at(0);\n const l = rest.at(1);\n if (t === undefined || l === undefined) {\n throw new Nip19Error(\"not enough data to read TLV\");\n }\n const v = rest.slice(2, 2 + l);\n rest = rest.slice(2 + l);\n if (v.length < l) {\n throw new Nip19Error(`not enough data to read on TLV ${t}`);\n }\n result[t] = result[t] ?? [];\n result[t].push(v);\n }\n return result;\n}\n\n// NIP-01 kinds are u16, so the 4-byte wire TLV is limited to 0..=65535 on\n// both encode and decode.\nfunction assertNip19Kind(kind: number): void {\n if (!Number.isInteger(kind) || kind < 0 || kind > 0xffff) {\n throw new Nip19Error(`invalid kind: ${kind}`);\n }\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n const entries: Uint8Array[] = [];\n for (const [t, vs] of Object.entries(tlv).reverse()) {\n for (const v of vs) {\n if (v.length > 255) {\n throw new Nip19Error(\"TLV value exceeds 255 bytes\");\n }\n const entry = new Uint8Array(v.length + 2);\n entry[0] = Math.trunc(Number(t));\n entry[1] = v.length;\n entry.set(v, 2);\n entries.push(entry);\n }\n }\n let total = 0;\n for (const e of entries) {\n total += e.length;\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const e of entries) {\n out.set(e, offset);\n offset += e.length;\n }\n return out;\n}\n\nfunction encodeBech32<Prefix extends string>(\n prefix: Prefix,\n data: Uint8Array,\n): `${Prefix}1${string}` {\n const words = bech32.toWords(data);\n // oxlint-disable-next-line no-unsafe-type-assertion -- bech32 output is `${prefix}1…` by construction\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`;\n}\n\nexport function encodeBytes<Prefix extends string>(\n prefix: Prefix,\n bytes: Uint8Array,\n): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes);\n}\n\n/** Encode a 32-byte secret key as `nsec1…`; throws {@link Nip19Error} on wrong length. */\nexport function nsecEncode(key: Uint8Array): NSec {\n assertByteLength(key, 32, \"secret key\");\n return encodeBytes(\"nsec\", key);\n}\n\n/** Encode a hex public key as `npub1…`; throws {@link HexError} on bad hex. */\nexport function npubEncode(hex: string): NPub {\n return encodeBytes(\"npub\", hexToBytes(assertHex32(hex, \"pubkey\")));\n}\n\n/** Encode a hex event id as `note1…`; throws {@link HexError} on bad hex. */\nexport function noteEncode(hex: string): Note {\n return encodeBytes(\"note\", hexToBytes(assertHex32(hex, \"event id\")));\n}\n\n/** Encode a profile pointer as `nprofile1…`. */\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n const data = encodeTLV({\n 0: [hexToBytes(assertHex32(profile.pubkey, \"pubkey\"))],\n 1: (profile.relays ?? []).map((url) => utf8Encoder.encode(url)),\n });\n return encodeBech32(\"nprofile\", data);\n}\n\n/** Encode an event pointer as `nevent1…`. */\nexport function neventEncode(event: EventPointer): NEvent {\n if (event.kind !== undefined) {\n assertNip19Kind(event.kind);\n }\n const kindArray = event.kind === undefined ? undefined : integerToUint8Array(event.kind);\n const data = encodeTLV({\n 0: [hexToBytes(assertHex32(event.id, \"event id\"))],\n 1: (event.relays ?? []).map((url) => utf8Encoder.encode(url)),\n 2:\n event.author !== undefined && event.author !== \"\"\n ? [hexToBytes(assertHex32(event.author, \"author\"))]\n : [],\n 3: kindArray ? [kindArray] : [],\n });\n return encodeBech32(\"nevent\", data);\n}\n\n/** Encode an address pointer as `naddr1…`. */\nexport function naddrEncode(addr: AddressPointer): NAddr {\n assertNip19Kind(addr.kind);\n const kind = integerToUint8Array(addr.kind);\n const data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays ?? []).map((url) => utf8Encoder.encode(url)),\n 2: [hexToBytes(assertHex32(addr.pubkey, \"pubkey\"))],\n 3: [kind],\n });\n return encodeBech32(\"naddr\", data);\n}\n\nfunction readUint32(bytes: Uint8Array): number {\n return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);\n}\n\n/** Decode a NIP-19 bech32 entity; throws {@link Nip19Error} on bad input or unknown prefix. */\nexport function decode(code: string): DecodedResult {\n let prefix: string;\n let data: Uint8Array;\n try {\n const { prefix: hrp, words } = bech32.decode(code, Bech32MaxSize);\n prefix = hrp;\n data = new Uint8Array(bech32.fromWords(words));\n } catch (error) {\n throw new Nip19Error(`invalid bech32: ${errorMessage(error)}`, { cause: error });\n }\n\n switch (prefix) {\n case \"nprofile\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) {\n throw new Nip19Error(\"missing TLV 0 for nprofile\");\n }\n if (tlv[0][0].length !== 32) {\n throw new Nip19Error(\"TLV 0 should be 32 bytes\");\n }\n return {\n type: \"nprofile\",\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n },\n };\n }\n case \"nevent\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) {\n throw new Nip19Error(\"missing TLV 0 for nevent\");\n }\n if (tlv[0][0].length !== 32) {\n throw new Nip19Error(\"TLV 0 should be 32 bytes\");\n }\n if (tlv[2]?.[0] && tlv[2][0].length !== 32) {\n throw new Nip19Error(\"TLV 2 should be 32 bytes\");\n }\n if (tlv[3]?.[0] && tlv[3][0].length !== 4) {\n throw new Nip19Error(\"TLV 3 should be 4 bytes\");\n }\n const pointer: Mutable<EventPointer> = {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n };\n const author = tlv[2]?.[0];\n if (author !== undefined) {\n pointer.author = bytesToHex(author);\n }\n const kind = tlv[3]?.[0];\n if (kind !== undefined) {\n pointer.kind = readUint32(kind);\n assertNip19Kind(pointer.kind);\n }\n return { type: \"nevent\", data: pointer };\n }\n case \"naddr\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) {\n throw new Nip19Error(\"missing TLV 0 for naddr\");\n }\n if (!tlv[2]?.[0]) {\n throw new Nip19Error(\"missing TLV 2 for naddr\");\n }\n if (tlv[2][0].length !== 32) {\n throw new Nip19Error(\"TLV 2 should be 32 bytes\");\n }\n if (!tlv[3]?.[0]) {\n throw new Nip19Error(\"missing TLV 3 for naddr\");\n }\n if (tlv[3][0].length !== 4) {\n throw new Nip19Error(\"TLV 3 should be 4 bytes\");\n }\n const naddrKind = readUint32(tlv[3][0]);\n assertNip19Kind(naddrKind);\n return {\n type: \"naddr\",\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: naddrKind,\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n },\n };\n }\n case \"nsec\":\n if (data.length !== 32) {\n throw new Nip19Error(\"nsec must be 32 bytes\");\n }\n if (!secp256k1.utils.isValidSecretKey(data)) {\n throw new Nip19Error(\"invalid nsec secret key\");\n }\n return { type: \"nsec\", data };\n case \"npub\":\n case \"note\":\n if (data.length !== 32) {\n throw new Nip19Error(`${prefix} must be 32 bytes`);\n }\n return { type: prefix, data: bytesToHex(data) };\n default:\n throw new Nip19Error(`unknown prefix ${prefix}`);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip19.mjs","names":[],"sources":["../../src/nips/nip19.ts"],"sourcesContent":["import { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { bech32 } from \"@scure/base\";\n\nimport { NostrError, errorMessage } from \"../core/error.ts\";\nimport type { Mutable } from \"../core/util.ts\";\nimport {\n assertByteLength,\n assertHex32,\n bytesToHex,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\n/** Bech32 `nprofile1…` string (TLV profile pointer). */\nexport type NProfile = `nprofile1${string}`;\n/** Bech32 `nevent1…` string (TLV event pointer). */\nexport type NEvent = `nevent1${string}`;\n/** Bech32 `naddr1…` string (TLV address pointer). */\nexport type NAddr = `naddr1${string}`;\n/** Bech32 `nsec1…` string (32-byte secret key). */\nexport type NSec = `nsec1${string}`;\n/** Bech32 `npub1…` string (32-byte public key). */\nexport type NPub = `npub1${string}`;\n/** Bech32 `note1…` string (32-byte event id). */\nexport type Note = `note1${string}`;\n\n/** Maximum bech32 string length accepted by {@link decode} (NIP-19). */\nexport const Bech32MaxSize = 5000;\n\n/** Decoded `nprofile` payload: a pubkey plus optional relay hints. */\nexport type ProfilePointer = {\n readonly pubkey: string;\n readonly relays?: ReadonlyArray<string> | undefined;\n};\n\n/** Decoded `nevent` payload: an event id plus optional relay/author/kind hints. */\nexport type EventPointer = {\n readonly id: string;\n readonly relays?: ReadonlyArray<string> | undefined;\n readonly author?: string | undefined;\n readonly kind?: number | undefined;\n};\n\n/** Decoded `naddr` payload: a replaceable-event coordinate plus optional relay hints. */\nexport type AddressPointer = {\n readonly identifier: string;\n readonly pubkey: string;\n readonly kind: number;\n readonly relays?: ReadonlyArray<string> | undefined;\n};\n\n/** Result of {@link decode}: the bech32 prefix and its decoded payload. */\nexport type DecodedResult =\n | { readonly type: \"nprofile\"; readonly data: ProfilePointer }\n | { readonly type: \"nevent\"; readonly data: EventPointer }\n | { readonly type: \"naddr\"; readonly data: AddressPointer }\n | { readonly type: \"nsec\"; readonly data: Uint8Array }\n | { readonly type: \"npub\"; readonly data: string }\n | { readonly type: \"note\"; readonly data: string };\n\n/** Error thrown by NIP-19 encoding/decoding failures. */\nexport class Nip19Error extends NostrError {\n override name = \"Nip19Error\";\n}\n\ntype TLV = Record<number, Uint8Array[]>;\n\nfunction integerToUint8Array(number: number): Uint8Array {\n const out = new Uint8Array(4);\n new DataView(out.buffer).setUint32(0, number);\n return out;\n}\n\nfunction parseTLV(data: Uint8Array): TLV {\n const result: TLV = {};\n let rest = data;\n while (rest.length > 0) {\n if (rest.length < 2) {\n throw new Nip19Error(\"not enough data to read TLV\");\n }\n const t = rest.at(0);\n const l = rest.at(1);\n if (t === undefined || l === undefined) {\n throw new Nip19Error(\"not enough data to read TLV\");\n }\n const v = rest.slice(2, 2 + l);\n rest = rest.slice(2 + l);\n if (v.length < l) {\n throw new Nip19Error(`not enough data to read on TLV ${t}`);\n }\n result[t] = result[t] ?? [];\n result[t].push(v);\n }\n return result;\n}\n\n// NIP-01 kinds are u16, so the 4-byte wire TLV is limited to 0..=65535 on\n// both encode and decode.\nfunction assertNip19Kind(kind: number): void {\n if (!Number.isInteger(kind) || kind < 0 || kind > 0xffff) {\n throw new Nip19Error(`invalid kind: ${kind}`);\n }\n}\n\n// TLV entries are emitted in ascending type order (0 special, 1 relays, 2 author, 3 kind),\n// matching the NIP-19 example; values within one type keep their input order.\nfunction encodeTLV(tlv: TLV): Uint8Array {\n const entries: Uint8Array[] = [];\n for (const [t, vs] of Object.entries(tlv)) {\n for (const v of vs) {\n if (v.length > 255) {\n throw new Nip19Error(\"TLV value exceeds 255 bytes\");\n }\n const entry = new Uint8Array(v.length + 2);\n entry[0] = Math.trunc(Number(t));\n entry[1] = v.length;\n entry.set(v, 2);\n entries.push(entry);\n }\n }\n let total = 0;\n for (const e of entries) {\n total += e.length;\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const e of entries) {\n out.set(e, offset);\n offset += e.length;\n }\n return out;\n}\n\nfunction encodeBech32<Prefix extends string>(\n prefix: Prefix,\n data: Uint8Array,\n): `${Prefix}1${string}` {\n const words = bech32.toWords(data);\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- bech32 output is `${prefix}1…` by construction\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`;\n}\n\nexport function encodeBytes<Prefix extends string>(\n prefix: Prefix,\n bytes: Uint8Array,\n): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes);\n}\n\n/** Encode a 32-byte secret key as `nsec1…`; throws {@link Nip19Error} on wrong length. */\nexport function nsecEncode(key: Uint8Array): NSec {\n assertByteLength(key, 32, \"secret key\");\n return encodeBytes(\"nsec\", key);\n}\n\n/** Encode a hex public key as `npub1…`; throws {@link HexError} on bad hex. */\nexport function npubEncode(hex: string): NPub {\n return encodeBytes(\"npub\", hexToBytes(assertHex32(hex, \"pubkey\")));\n}\n\n/** Encode a hex event id as `note1…`; throws {@link HexError} on bad hex. */\nexport function noteEncode(hex: string): Note {\n return encodeBytes(\"note\", hexToBytes(assertHex32(hex, \"event id\")));\n}\n\n/** Encode a profile pointer as `nprofile1…`. */\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n const data = encodeTLV({\n 0: [hexToBytes(assertHex32(profile.pubkey, \"pubkey\"))],\n 1: (profile.relays ?? []).map((url) => utf8Encoder.encode(url)),\n });\n return encodeBech32(\"nprofile\", data);\n}\n\n/** Encode an event pointer as `nevent1…`. */\nexport function neventEncode(event: EventPointer): NEvent {\n if (event.kind !== undefined) {\n assertNip19Kind(event.kind);\n }\n const kindArray = event.kind === undefined ? undefined : integerToUint8Array(event.kind);\n const data = encodeTLV({\n 0: [hexToBytes(assertHex32(event.id, \"event id\"))],\n 1: (event.relays ?? []).map((url) => utf8Encoder.encode(url)),\n 2:\n event.author !== undefined && event.author !== \"\"\n ? [hexToBytes(assertHex32(event.author, \"author\"))]\n : [],\n 3: kindArray ? [kindArray] : [],\n });\n return encodeBech32(\"nevent\", data);\n}\n\n/** Encode an address pointer as `naddr1…`. */\nexport function naddrEncode(addr: AddressPointer): NAddr {\n assertNip19Kind(addr.kind);\n const kind = integerToUint8Array(addr.kind);\n const data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays ?? []).map((url) => utf8Encoder.encode(url)),\n 2: [hexToBytes(assertHex32(addr.pubkey, \"pubkey\"))],\n 3: [kind],\n });\n return encodeBech32(\"naddr\", data);\n}\n\nfunction readUint32(bytes: Uint8Array): number {\n return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);\n}\n\n/** Decode a NIP-19 bech32 entity; throws {@link Nip19Error} on bad input or unknown prefix. */\nexport function decode(code: string): DecodedResult {\n let prefix: string;\n let data: Uint8Array;\n try {\n const { prefix: hrp, words } = bech32.decode(code, Bech32MaxSize);\n prefix = hrp;\n data = new Uint8Array(bech32.fromWords(words));\n } catch (error) {\n throw new Nip19Error(`invalid bech32: ${errorMessage(error)}`, { cause: error });\n }\n\n switch (prefix) {\n case \"nprofile\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) {\n throw new Nip19Error(\"missing TLV 0 for nprofile\");\n }\n if (tlv[0][0].length !== 32) {\n throw new Nip19Error(\"TLV 0 should be 32 bytes\");\n }\n return {\n type: \"nprofile\",\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n },\n };\n }\n case \"nevent\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) {\n throw new Nip19Error(\"missing TLV 0 for nevent\");\n }\n if (tlv[0][0].length !== 32) {\n throw new Nip19Error(\"TLV 0 should be 32 bytes\");\n }\n if (tlv[2]?.[0] && tlv[2][0].length !== 32) {\n throw new Nip19Error(\"TLV 2 should be 32 bytes\");\n }\n if (tlv[3]?.[0] && tlv[3][0].length !== 4) {\n throw new Nip19Error(\"TLV 3 should be 4 bytes\");\n }\n const pointer: Mutable<EventPointer> = {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n };\n const author = tlv[2]?.[0];\n if (author !== undefined) {\n pointer.author = bytesToHex(author);\n }\n const kind = tlv[3]?.[0];\n if (kind !== undefined) {\n pointer.kind = readUint32(kind);\n assertNip19Kind(pointer.kind);\n }\n return { type: \"nevent\", data: pointer };\n }\n case \"naddr\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) {\n throw new Nip19Error(\"missing TLV 0 for naddr\");\n }\n if (!tlv[2]?.[0]) {\n throw new Nip19Error(\"missing TLV 2 for naddr\");\n }\n if (tlv[2][0].length !== 32) {\n throw new Nip19Error(\"TLV 2 should be 32 bytes\");\n }\n if (!tlv[3]?.[0]) {\n throw new Nip19Error(\"missing TLV 3 for naddr\");\n }\n if (tlv[3][0].length !== 4) {\n throw new Nip19Error(\"TLV 3 should be 4 bytes\");\n }\n const naddrKind = readUint32(tlv[3][0]);\n assertNip19Kind(naddrKind);\n return {\n type: \"naddr\",\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: naddrKind,\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n },\n };\n }\n case \"nsec\":\n if (data.length !== 32) {\n throw new Nip19Error(\"nsec must be 32 bytes\");\n }\n if (!secp256k1.utils.isValidSecretKey(data)) {\n throw new Nip19Error(\"invalid nsec secret key\");\n }\n return { type: \"nsec\", data };\n case \"npub\":\n case \"note\":\n if (data.length !== 32) {\n throw new Nip19Error(`${prefix} must be 32 bytes`);\n }\n return { type: prefix, data: bytesToHex(data) };\n default:\n throw new Nip19Error(`unknown prefix ${prefix}`);\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,14 @@
1
- import { a as NostrError } from "../error-BquXDmIN.mjs";
2
- import { r as DecodedResult } from "../nip19-DpPN17bY.mjs";
1
+ import { a as NostrError } from "../error-BPqAU5RB.mjs";
2
+ import { r as DecodedResult } from "../nip19-D4RKn6GC.mjs";
3
3
  //#region src/nips/nip21.d.ts
4
4
  /** Error thrown by {@link parseNostrURI} on malformed or non-decodable URIs. */
5
5
  export declare class Nip21Error extends NostrError {
6
6
  name: string;
7
7
  }
8
- /** Matches `nostr:<bech32>` (not anchored). */
8
+ /**
9
+ * Matches `nostr:<bech32>` (not anchored). Case classes are written out so matching is
10
+ * case-insensitive for ASCII only: the `i` flag would also fold U+212A (K) into `k`.
11
+ */
9
12
  export declare const NOSTR_URI_REGEX: RegExp;
10
13
  export type NostrURI = {
11
14
  /** Full URI including `nostr:` */
@@ -1,4 +1,4 @@
1
- import { y as NostrError } from "../util-DDpPkiED.mjs";
1
+ import { x as NostrError } from "../util-DfsyJ9nz.mjs";
2
2
  import { decode } from "./nip19.mjs";
3
3
  //#region src/nips/nip21.ts
4
4
  /**
@@ -10,16 +10,19 @@ import { decode } from "./nip19.mjs";
10
10
  var Nip21Error = class extends NostrError {
11
11
  name = "Nip21Error";
12
12
  };
13
- /** Matches `nostr:<bech32>` (not anchored). */
14
- const NOSTR_URI_REGEX = /nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)/i;
13
+ /**
14
+ * Matches `nostr:<bech32>` (not anchored). Case classes are written out so matching is
15
+ * case-insensitive for ASCII only: the `i` flag would also fold U+212A (K) into `k`.
16
+ */
17
+ const NOSTR_URI_REGEX = /[Nn][Oo][Ss][Tt][Rr]:([A-Za-z0-9]+1[02-9AC-HJ-NP-Zac-hj-np-z]+)/;
15
18
  function isNostrURI(value) {
16
19
  if (typeof value !== "string") return false;
17
- if (!/^nostr:[a-z0-9]+1[02-9ac-hj-np-z]+$/i.test(value)) return false;
20
+ if (!/^[Nn][Oo][Ss][Tt][Rr]:[A-Za-z0-9]+1[02-9AC-HJ-NP-Zac-hj-np-z]+$/.test(value)) return false;
18
21
  return !value.toLowerCase().startsWith("nostr:nsec1");
19
22
  }
20
23
  /** Parse and decode a full `nostr:…` URI. NIP-21 excludes `nsec`. */
21
24
  function parseNostrURI(uri) {
22
- const entity = /^nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)$/i.exec(uri)?.at(1);
25
+ const entity = /^[Nn][Oo][Ss][Tt][Rr]:([A-Za-z0-9]+1[02-9AC-HJ-NP-Zac-hj-np-z]+)$/.exec(uri)?.at(1);
23
26
  if (entity === void 0) throw new Nip21Error(`invalid Nostr URI: ${uri}`);
24
27
  if (entity.toLowerCase().startsWith("nsec1")) throw new Nip21Error("NIP-21 identifiers exclude nsec");
25
28
  let decoded;
@@ -1 +1 @@
1
- {"version":3,"file":"nip21.mjs","names":[],"sources":["../../src/nips/nip21.ts"],"sourcesContent":["/**\n * NIP-21: `nostr:` URI scheme\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/21.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { decode } from \"./nip19.ts\";\nimport type { DecodedResult } from \"./nip19.ts\";\n\n/** Error thrown by {@link parseNostrURI} on malformed or non-decodable URIs. */\nexport class Nip21Error extends NostrError {\n override name = \"Nip21Error\";\n}\n\n/** Matches `nostr:<bech32>` (not anchored). */\n// oxlint-disable-next-line no-inferrable-types -- isolatedDeclarations requires the annotation for dts emit\nexport const NOSTR_URI_REGEX: RegExp = /nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)/i;\n\nexport type NostrURI = {\n /** Full URI including `nostr:` */\n readonly uri: `nostr:${string}`;\n /** Bech32 entity without prefix */\n readonly value: string;\n readonly decoded: DecodedResult;\n};\n\nexport function isNostrURI(value: unknown): value is `nostr:${string}` {\n if (typeof value !== \"string\") {\n return false;\n }\n if (!/^nostr:[a-z0-9]+1[02-9ac-hj-np-z]+$/i.test(value)) {\n return false;\n }\n return !value.toLowerCase().startsWith(\"nostr:nsec1\");\n}\n\n/** Parse and decode a full `nostr:…` URI. NIP-21 excludes `nsec`. */\nexport function parseNostrURI(uri: string): NostrURI {\n const match = /^nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)$/i.exec(uri);\n const entity = match?.at(1);\n if (entity === undefined) {\n throw new Nip21Error(`invalid Nostr URI: ${uri}`);\n }\n if (entity.toLowerCase().startsWith(\"nsec1\")) {\n throw new Nip21Error(\"NIP-21 identifiers exclude nsec\");\n }\n let decoded: DecodedResult;\n try {\n decoded = decode(entity);\n } catch (error) {\n throw new Nip21Error(`invalid Nostr URI: ${uri}`, { cause: error });\n }\n return {\n uri: `nostr:${entity}`,\n value: entity,\n decoded,\n };\n}\n"],"mappings":";;;;;;;;;AAUA,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;AAIA,MAAa,kBAA0B;AAUvC,SAAgB,WAAW,OAA4C;CACrE,IAAI,OAAO,UAAU,UACnB,OAAO;CAET,IAAI,CAAC,uCAAuC,KAAK,KAAK,GACpD,OAAO;CAET,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC,WAAW,aAAa;AACtD;;AAGA,SAAgB,cAAc,KAAuB;CAEnD,MAAM,SADQ,yCAAyC,KAAK,GACzC,CAAC,EAAE,GAAG,CAAC;CAC1B,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,WAAW,sBAAsB,KAAK;CAElD,IAAI,OAAO,YAAY,CAAC,CAAC,WAAW,OAAO,GACzC,MAAM,IAAI,WAAW,iCAAiC;CAExD,IAAI;CACJ,IAAI;EACF,UAAU,OAAO,MAAM;CACzB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,sBAAsB,OAAO,EAAE,OAAO,MAAM,CAAC;CACpE;CACA,OAAO;EACL,KAAK,SAAS;EACd,OAAO;EACP;CACF;AACF"}
1
+ {"version":3,"file":"nip21.mjs","names":[],"sources":["../../src/nips/nip21.ts"],"sourcesContent":["/**\n * NIP-21: `nostr:` URI scheme\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/21.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { decode } from \"./nip19.ts\";\nimport type { DecodedResult } from \"./nip19.ts\";\n\n/** Error thrown by {@link parseNostrURI} on malformed or non-decodable URIs. */\nexport class Nip21Error extends NostrError {\n override name = \"Nip21Error\";\n}\n\n/**\n * Matches `nostr:<bech32>` (not anchored). Case classes are written out so matching is\n * case-insensitive for ASCII only: the `i` flag would also fold U+212A (K) into `k`.\n */\n// oxlint-disable-next-line typescript/no-inferrable-types -- isolatedDeclarations requires the annotation for dts emit\nexport const NOSTR_URI_REGEX: RegExp =\n /[Nn][Oo][Ss][Tt][Rr]:([A-Za-z0-9]+1[02-9AC-HJ-NP-Zac-hj-np-z]+)/;\n\nexport type NostrURI = {\n /** Full URI including `nostr:` */\n readonly uri: `nostr:${string}`;\n /** Bech32 entity without prefix */\n readonly value: string;\n readonly decoded: DecodedResult;\n};\n\nexport function isNostrURI(value: unknown): value is `nostr:${string}` {\n if (typeof value !== \"string\") {\n return false;\n }\n if (!/^[Nn][Oo][Ss][Tt][Rr]:[A-Za-z0-9]+1[02-9AC-HJ-NP-Zac-hj-np-z]+$/.test(value)) {\n return false;\n }\n return !value.toLowerCase().startsWith(\"nostr:nsec1\");\n}\n\n/** Parse and decode a full `nostr:…` URI. NIP-21 excludes `nsec`. */\nexport function parseNostrURI(uri: string): NostrURI {\n const match = /^[Nn][Oo][Ss][Tt][Rr]:([A-Za-z0-9]+1[02-9AC-HJ-NP-Zac-hj-np-z]+)$/.exec(uri);\n const entity = match?.at(1);\n if (entity === undefined) {\n throw new Nip21Error(`invalid Nostr URI: ${uri}`);\n }\n if (entity.toLowerCase().startsWith(\"nsec1\")) {\n throw new Nip21Error(\"NIP-21 identifiers exclude nsec\");\n }\n let decoded: DecodedResult;\n try {\n decoded = decode(entity);\n } catch (error) {\n throw new Nip21Error(`invalid Nostr URI: ${uri}`, { cause: error });\n }\n return {\n uri: `nostr:${entity}`,\n value: entity,\n decoded,\n };\n}\n"],"mappings":";;;;;;;;;AAUA,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;;;;AAOA,MAAa,kBACX;AAUF,SAAgB,WAAW,OAA4C;CACrE,IAAI,OAAO,UAAU,UACnB,OAAO;CAET,IAAI,CAAC,kEAAkE,KAAK,KAAK,GAC/E,OAAO;CAET,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC,WAAW,aAAa;AACtD;;AAGA,SAAgB,cAAc,KAAuB;CAEnD,MAAM,SADQ,oEAAoE,KAAK,GACpE,CAAC,EAAE,GAAG,CAAC;CAC1B,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,WAAW,sBAAsB,KAAK;CAElD,IAAI,OAAO,YAAY,CAAC,CAAC,WAAW,OAAO,GACzC,MAAM,IAAI,WAAW,iCAAiC;CAExD,IAAI;CACJ,IAAI;EACF,UAAU,OAAO,MAAM;CACzB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,sBAAsB,OAAO,EAAE,OAAO,MAAM,CAAC;CACpE;CACA,OAAO;EACL,KAAK,SAAS;EACd,OAAO;EACP;CACF;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { t as Event } from "../event-j3BwI5dF.mjs";
2
- import { f as ProfilePointer, i as EventPointer, t as AddressPointer } from "../nip19-DpPN17bY.mjs";
2
+ import { f as ProfilePointer, i as EventPointer, t as AddressPointer } from "../nip19-D4RKn6GC.mjs";
3
3
  //#region src/nips/nip27.d.ts
4
4
  export type ContentBlock = {
5
5
  readonly type: "text";
@@ -1,19 +1,25 @@
1
1
  import { decode } from "./nip19.mjs";
2
2
  //#region src/nips/nip27.ts
3
- const BECH32_CHARS = "02-9ac-hj-np-z";
4
- const NIP19_PREFIX = "(?:npub|nprofile|note|nevent|naddr|nsec)1";
5
- const NIP19_BARE_PREFIX = "(?:npub|nprofile|note|nevent|naddr)1";
3
+ const BECH32_CHARS = "02-9AC-HJ-NP-Zac-hj-np-z";
4
+ /**
5
+ * Pair every letter of an ASCII literal into a case class (`[Nn]…`) so keywords match
6
+ * case-insensitively for ASCII only. The regex `i` flag cannot be used here: under `u` it also
7
+ * folds U+017F (ſ)→s and U+212A (K)→k, which would let `noſtr:` or `lnbc1K…` slip through.
8
+ */
9
+ const asciiCi = (word) => word.replaceAll(/[a-zA-Z]/g, (c) => `[${c.toLowerCase()}${c.toUpperCase()}]`);
10
+ const NIP19_PREFIX = asciiCi("(?:npub|nprofile|note|nevent|naddr|nsec)1");
11
+ const NIP19_BARE_PREFIX = asciiCi("(?:npub|nprofile|note|nevent|naddr)1");
6
12
  const URL_RUN = `[^\\s<>"\\u3000-\\u303f\\uff00-\\uffef]+`;
7
13
  const SCAN_BODY = [
8
- `(?<nostr>nostr:${NIP19_PREFIX}[${BECH32_CHARS}]+)`,
9
- `(?<url>https?://${URL_RUN})`,
10
- `(?<relay>wss?://${URL_RUN})`,
11
- `(?<invoice>(?:lightning:)?ln(?:bcrt|tbs|bc|tb)(?:\\d+[munp]?)?1[${BECH32_CHARS}]+)`,
14
+ `(?<nostr>${asciiCi("nostr:")}${NIP19_PREFIX}[${BECH32_CHARS}]+)`,
15
+ `(?<url>${asciiCi("https")}?://${URL_RUN})`,
16
+ `(?<relay>${asciiCi("wss")}?://${URL_RUN})`,
17
+ `(?<invoice>(?:${asciiCi("lightning:")})?${asciiCi("ln")}(?:${asciiCi("bcrt")}|${asciiCi("tbs")}|${asciiCi("bc")}|${asciiCi("tb")})(?:\\d+[munpMUNP]?)?1[${BECH32_CHARS}]+)`,
12
18
  `(?<hashtag>#[\\p{L}\\p{M}\\p{N}_]{1,42})`,
13
19
  `(?<emoji>:[A-Za-z0-9_-]+:)`
14
20
  ].join("|");
15
- const SCAN = new RegExp(SCAN_BODY, "giu");
16
- const SCAN_LEGACY = new RegExp(`${SCAN_BODY}|(?<bare>${NIP19_BARE_PREFIX}[${BECH32_CHARS}]+)`, "giu");
21
+ const SCAN = new RegExp(SCAN_BODY, "gu");
22
+ const SCAN_LEGACY = new RegExp(`${SCAN_BODY}|(?<bare>${NIP19_BARE_PREFIX}[${BECH32_CHARS}]+)`, "gu");
17
23
  const TAG_CHAR = /[\p{L}\p{M}\p{N}_]/u;
18
24
  const WORD_CHAR = /[\p{L}\p{N}]/u;
19
25
  const TRAILING_URL_CHARS = /* @__PURE__ */ new Set([
@@ -1 +1 @@
1
- {"version":3,"file":"nip27.mjs","names":[],"sources":["../../src/nips/nip27.ts"],"sourcesContent":["/**\n * NIP-27: Text Note References. Tokenizes note content into text, `nostr:` references, URLs, media,\n * relays, hashtags, custom emoji, and Lightning invoices.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/27.md\n */\nimport type { Event } from \"../core/event.ts\";\nimport { decode } from \"./nip19.ts\";\nimport type { AddressPointer, EventPointer, ProfilePointer } from \"./nip19.ts\";\n\nexport type ContentBlock =\n | { readonly type: \"text\"; readonly text: string }\n | {\n readonly type: \"reference\";\n readonly pointer: ProfilePointer | EventPointer | AddressPointer;\n /**\n * `true` when the reference had no `nostr:` prefix (see\n * {@link ParseContentOptions.legacyBech32}).\n */\n readonly bare: boolean;\n }\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"image\" | \"video\" | \"audio\"; readonly url: string }\n | { readonly type: \"relay\"; readonly url: string }\n | { readonly type: \"hashtag\"; readonly value: string }\n | { readonly type: \"emoji\"; readonly shortcode: string; readonly url: string }\n | { readonly type: \"invoice\"; readonly bolt11: string };\n\nexport type ParseContentOptions = {\n /**\n * Recognize bare `npub`, `nprofile`, `note`, `nevent`, `naddr` without the `nostr:` prefix.\n * Default `false`.\n */\n legacyBech32?: boolean | undefined;\n /**\n * URL → MIME type (the `m` field of NIP-92 `imeta` tags); takes precedence over extension\n * classification.\n */\n imeta?: ReadonlyMap<string, string> | undefined;\n};\n\n// Bech32 data charset ([02-9ac-hj-np-z], case-insensitive) and the NIP-19 entity prefixes.\nconst BECH32_CHARS = \"02-9ac-hj-np-z\";\nconst NIP19_PREFIX = \"(?:npub|nprofile|note|nevent|naddr|nsec)1\";\nconst NIP19_BARE_PREFIX = \"(?:npub|nprofile|note|nevent|naddr)1\";\n\n// A URL run ends at whitespace, angle brackets, double quotes, CJK punctuation\n// (U+3000–U+303F), or full-width forms (U+FF00–U+FFEF).\nconst URL_RUN = `[^\\\\s<>\"\\\\u3000-\\\\u303f\\\\uff00-\\\\uffef]+`;\n\n// Rule order matters: at every position the first matching alternative wins.\nconst SCAN_BODY = [\n // 1. nostr: URIs carry a NIP-19 entity.\n `(?<nostr>nostr:${NIP19_PREFIX}[${BECH32_CHARS}]+)`,\n // 2./3. http(s) and ws(s) URLs share the same boundary rules.\n `(?<url>https?://${URL_RUN})`,\n `(?<relay>wss?://${URL_RUN})`,\n // 4. BOLT11 invoices, with an optional lightning: URI prefix. The HRP may carry an amount\n // (`lnbc10u1…` = ln + currency + amount + multiplier + the `1` separator).\n `(?<invoice>(?:lightning:)?ln(?:bcrt|tbs|bc|tb)(?:\\\\d+[munp]?)?1[${BECH32_CHARS}]+)`,\n // 5. Hashtags: Unicode letters/marks/numbers and underscore, at most 42 code points.\n `(?<hashtag>#[\\\\p{L}\\\\p{M}\\\\p{N}_]{1,42})`,\n // 6. Custom emoji shortcodes (validated against the event's emoji tags). NIP-30 allows\n // alphanumerics, hyphens, and underscores.\n `(?<emoji>:[A-Za-z0-9_-]+:)`,\n].join(\"|\");\n\nconst SCAN = new RegExp(SCAN_BODY, \"giu\");\n// 7. Bare NIP-19 entities — legacy rendering support, opt-in only.\nconst SCAN_LEGACY = new RegExp(\n `${SCAN_BODY}|(?<bare>${NIP19_BARE_PREFIX}[${BECH32_CHARS}]+)`,\n \"giu\",\n);\n\n// The character before `#` must be absent or a non-tag character.\nconst TAG_CHAR = /[\\p{L}\\p{M}\\p{N}_]/u;\n// Invoice and bare-bech32 tokens must not be glued to a letter or digit.\nconst WORD_CHAR = /[\\p{L}\\p{N}]/u;\n\nconst TRAILING_URL_CHARS = new Set([\".\", \",\", \";\", \":\", \"!\", \"?\", \"'\"]);\n\nconst IMAGE_EXT = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"avif\", \"svg\"]);\nconst VIDEO_EXT = new Set([\"mp4\", \"webm\", \"mov\", \"m4v\", \"m3u8\"]);\nconst AUDIO_EXT = new Set([\"mp3\", \"m4a\", \"ogg\", \"oga\", \"opus\", \"wav\", \"flac\", \"aac\"]);\n\nfunction isChar(cp: number | undefined, re: RegExp): boolean {\n return cp !== undefined && re.test(String.fromCodePoint(cp));\n}\n\n/** Drop trailing sentence punctuation and closers that have no opener inside the URL. */\nfunction trimUrlTail(raw: string): string {\n let url = raw;\n for (;;) {\n const last = url.at(-1);\n if (last !== undefined && TRAILING_URL_CHARS.has(last)) {\n url = url.slice(0, -1);\n continue;\n }\n if (last === \")\" || last === \"]\") {\n const open = last === \")\" ? \"(\" : \"[\";\n let opens = 0;\n let closes = 0;\n for (const c of url) {\n if (c === open) {\n opens++;\n } else if (c === last) {\n closes++;\n }\n }\n if (closes > opens) {\n url = url.slice(0, -1);\n continue;\n }\n }\n return url;\n }\n}\n\ntype MediaType = \"image\" | \"video\" | \"audio\";\n\nfunction mimeMedia(mime: string | undefined): MediaType | undefined {\n if (mime === undefined) {\n return undefined;\n }\n const slash = mime.indexOf(\"/\");\n const base = slash === -1 ? mime : mime.slice(0, slash);\n return base === \"image\" || base === \"video\" || base === \"audio\" ? base : undefined;\n}\n\nfunction extMedia(pathname: string): MediaType | undefined {\n const dot = pathname.lastIndexOf(\".\");\n if (dot === -1) {\n return undefined;\n }\n const ext = pathname.slice(dot + 1).toLowerCase();\n if (IMAGE_EXT.has(ext)) {\n return \"image\";\n }\n if (VIDEO_EXT.has(ext)) {\n return \"video\";\n }\n return AUDIO_EXT.has(ext) ? \"audio\" : undefined;\n}\n\nfunction urlBlock(\n raw: string,\n relay: boolean,\n imeta: ReadonlyMap<string, string> | undefined,\n): ContentBlock | undefined {\n const url = trimUrlTail(raw);\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return undefined;\n }\n if (!parsed.hostname.includes(\".\")) {\n return undefined;\n }\n if (relay) {\n return { type: \"relay\", url };\n }\n const media = mimeMedia(imeta?.get(url)) ?? extMedia(parsed.pathname);\n return media === undefined ? { type: \"url\", url } : { type: media, url };\n}\n\nfunction referenceFor(code: string, bare: boolean): ContentBlock | undefined {\n let pointer: ProfilePointer | EventPointer | AddressPointer;\n try {\n const decoded = decode(code);\n switch (decoded.type) {\n case \"npub\":\n pointer = { pubkey: decoded.data };\n break;\n case \"note\":\n pointer = { id: decoded.data };\n break;\n case \"nprofile\":\n case \"nevent\":\n case \"naddr\":\n pointer = decoded.data;\n break;\n case \"nsec\":\n return undefined;\n }\n } catch {\n return undefined;\n }\n return { type: \"reference\", pointer, bare };\n}\n\n/**\n * Parse note content (or a full event, to pick up `emoji` tags) into ordered blocks. One\n * left-to-right scan; at every position the rules fire in the alternation order above. Adjacent\n * text merges and no empty text blocks are emitted.\n */\nexport function parseContent(\n content: string | Pick<Event, \"content\" | \"tags\">,\n opts?: ParseContentOptions,\n): ContentBlock[] {\n const emojis = new Map<string, string>();\n let text: string;\n if (typeof content === \"string\") {\n text = content;\n } else {\n for (const tag of content.tags) {\n if (\n tag[0] === \"emoji\" &&\n tag[1] !== undefined &&\n tag[1] !== \"\" &&\n tag[2] !== undefined &&\n tag[2] !== \"\"\n ) {\n emojis.set(tag[1], tag[2]);\n }\n }\n text = content.content;\n }\n\n const scan = opts?.legacyBech32 === true ? SCAN_LEGACY : SCAN;\n scan.lastIndex = 0;\n\n const blocks: ContentBlock[] = [];\n let textStart = 0;\n const flushText = (end: number): void => {\n if (end > textStart) {\n blocks.push({ type: \"text\", text: text.slice(textStart, end) });\n }\n };\n\n for (let m = scan.exec(text); m !== null; m = scan.exec(text)) {\n const start = m.index;\n const [token] = m;\n const end = start + token.length;\n const g = m.groups;\n let block: ContentBlock | undefined;\n\n if (g?.[\"nostr\"] !== undefined) {\n block = referenceFor(token.slice(\"nostr:\".length), false);\n } else if (g?.[\"url\"] !== undefined) {\n block = urlBlock(token, false, opts?.imeta);\n } else if (g?.[\"relay\"] !== undefined) {\n block = urlBlock(token, true, undefined);\n } else if (g?.[\"invoice\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n if (!isChar(prev, WORD_CHAR) && !isChar(text.codePointAt(end), WORD_CHAR)) {\n const invoice = token.toLowerCase().startsWith(\"lightning:\")\n ? token.slice(\"lightning:\".length)\n : token;\n block = { type: \"invoice\", bolt11: invoice.toLowerCase() };\n }\n } else if (g?.[\"hashtag\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n // A tag char right after the match means the {1,42} cap truncated a longer\n // run; the whole run is text, not a hashtag plus remainder.\n if (!isChar(prev, TAG_CHAR) && !isChar(text.codePointAt(end), TAG_CHAR)) {\n block = { type: \"hashtag\", value: token.slice(1) };\n }\n } else if (g?.[\"emoji\"] !== undefined) {\n const url = emojis.get(token.slice(1, -1));\n if (url !== undefined) {\n block = { type: \"emoji\", shortcode: token.slice(1, -1), url };\n }\n } else if (g?.[\"bare\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n if (!isChar(prev, WORD_CHAR) && !isChar(text.codePointAt(end), WORD_CHAR)) {\n block = referenceFor(token, true);\n }\n }\n\n if (block === undefined) {\n // The candidate is plain text; resume scanning one code unit later so any token inside\n // its span can still match.\n scan.lastIndex = start + 1;\n continue;\n }\n flushText(start);\n blocks.push(block);\n // URL-family blocks may end before the regex run once trailing punctuation is trimmed.\n textStart =\n block.type === \"url\" ||\n block.type === \"relay\" ||\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"audio\"\n ? start + block.url.length\n : end;\n }\n\n flushText(text.length);\n return blocks;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip27.mjs","names":[],"sources":["../../src/nips/nip27.ts"],"sourcesContent":["/**\n * NIP-27: Text Note References. Tokenizes note content into text, `nostr:` references, URLs, media,\n * relays, hashtags, custom emoji, and Lightning invoices.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/27.md\n */\nimport type { Event } from \"../core/event.ts\";\nimport { decode } from \"./nip19.ts\";\nimport type { AddressPointer, EventPointer, ProfilePointer } from \"./nip19.ts\";\n\nexport type ContentBlock =\n | { readonly type: \"text\"; readonly text: string }\n | {\n readonly type: \"reference\";\n readonly pointer: ProfilePointer | EventPointer | AddressPointer;\n /**\n * `true` when the reference had no `nostr:` prefix (see\n * {@link ParseContentOptions.legacyBech32}).\n */\n readonly bare: boolean;\n }\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"image\" | \"video\" | \"audio\"; readonly url: string }\n | { readonly type: \"relay\"; readonly url: string }\n | { readonly type: \"hashtag\"; readonly value: string }\n | { readonly type: \"emoji\"; readonly shortcode: string; readonly url: string }\n | { readonly type: \"invoice\"; readonly bolt11: string };\n\nexport type ParseContentOptions = {\n /**\n * Recognize bare `npub`, `nprofile`, `note`, `nevent`, `naddr` without the `nostr:` prefix.\n * Default `false`.\n */\n legacyBech32?: boolean | undefined;\n /**\n * URL → MIME type (the `m` field of NIP-92 `imeta` tags); takes precedence over extension\n * classification.\n */\n imeta?: ReadonlyMap<string, string> | undefined;\n};\n\n// Bech32 data charset ([02-9ac-hj-np-z], both ASCII cases) and the NIP-19 entity prefixes.\nconst BECH32_CHARS = \"02-9AC-HJ-NP-Zac-hj-np-z\";\n\n/**\n * Pair every letter of an ASCII literal into a case class (`[Nn]…`) so keywords match\n * case-insensitively for ASCII only. The regex `i` flag cannot be used here: under `u` it also\n * folds U+017F (ſ)→s and U+212A (K)→k, which would let `noſtr:` or `lnbc1K…` slip through.\n */\nconst asciiCi = (word: string): string =>\n word.replaceAll(/[a-zA-Z]/g, (c) => `[${c.toLowerCase()}${c.toUpperCase()}]`);\n\nconst NIP19_PREFIX = asciiCi(\"(?:npub|nprofile|note|nevent|naddr|nsec)1\");\nconst NIP19_BARE_PREFIX = asciiCi(\"(?:npub|nprofile|note|nevent|naddr)1\");\n\n// A URL run ends at whitespace, angle brackets, double quotes, CJK punctuation\n// (U+3000–U+303F), or full-width forms (U+FF00–U+FFEF).\nconst URL_RUN = `[^\\\\s<>\"\\\\u3000-\\\\u303f\\\\uff00-\\\\uffef]+`;\n\n// Rule order matters: at every position the first matching alternative wins.\nconst SCAN_BODY = [\n // 1. nostr: URIs carry a NIP-19 entity.\n `(?<nostr>${asciiCi(\"nostr:\")}${NIP19_PREFIX}[${BECH32_CHARS}]+)`,\n // 2./3. http(s) and ws(s) URLs share the same boundary rules.\n `(?<url>${asciiCi(\"https\")}?://${URL_RUN})`,\n `(?<relay>${asciiCi(\"wss\")}?://${URL_RUN})`,\n // 4. BOLT11 invoices, with an optional lightning: URI prefix. The HRP may carry an amount\n // (`lnbc10u1…` = ln + currency + amount + multiplier + the `1` separator).\n `(?<invoice>(?:${asciiCi(\"lightning:\")})?${asciiCi(\"ln\")}(?:${asciiCi(\"bcrt\")}|${asciiCi(\"tbs\")}|${asciiCi(\"bc\")}|${asciiCi(\"tb\")})(?:\\\\d+[munpMUNP]?)?1[${BECH32_CHARS}]+)`,\n // 5. Hashtags: Unicode letters/marks/numbers and underscore, at most 42 code points.\n `(?<hashtag>#[\\\\p{L}\\\\p{M}\\\\p{N}_]{1,42})`,\n // 6. Custom emoji shortcodes (validated against the event's emoji tags). NIP-30 allows\n // alphanumerics, hyphens, and underscores.\n `(?<emoji>:[A-Za-z0-9_-]+:)`,\n].join(\"|\");\n\nconst SCAN = new RegExp(SCAN_BODY, \"gu\");\n// 7. Bare NIP-19 entities — legacy rendering support, opt-in only.\nconst SCAN_LEGACY = new RegExp(\n `${SCAN_BODY}|(?<bare>${NIP19_BARE_PREFIX}[${BECH32_CHARS}]+)`,\n \"gu\",\n);\n\n// The character before `#` must be absent or a non-tag character.\nconst TAG_CHAR = /[\\p{L}\\p{M}\\p{N}_]/u;\n// Invoice and bare-bech32 tokens must not be glued to a letter or digit.\nconst WORD_CHAR = /[\\p{L}\\p{N}]/u;\n\nconst TRAILING_URL_CHARS = new Set([\".\", \",\", \";\", \":\", \"!\", \"?\", \"'\"]);\n\nconst IMAGE_EXT = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"avif\", \"svg\"]);\nconst VIDEO_EXT = new Set([\"mp4\", \"webm\", \"mov\", \"m4v\", \"m3u8\"]);\nconst AUDIO_EXT = new Set([\"mp3\", \"m4a\", \"ogg\", \"oga\", \"opus\", \"wav\", \"flac\", \"aac\"]);\n\nfunction isChar(cp: number | undefined, re: RegExp): boolean {\n return cp !== undefined && re.test(String.fromCodePoint(cp));\n}\n\n/** Drop trailing sentence punctuation and closers that have no opener inside the URL. */\nfunction trimUrlTail(raw: string): string {\n let url = raw;\n for (;;) {\n const last = url.at(-1);\n if (last !== undefined && TRAILING_URL_CHARS.has(last)) {\n url = url.slice(0, -1);\n continue;\n }\n if (last === \")\" || last === \"]\") {\n const open = last === \")\" ? \"(\" : \"[\";\n let opens = 0;\n let closes = 0;\n for (const c of url) {\n if (c === open) {\n opens++;\n } else if (c === last) {\n closes++;\n }\n }\n if (closes > opens) {\n url = url.slice(0, -1);\n continue;\n }\n }\n return url;\n }\n}\n\ntype MediaType = \"image\" | \"video\" | \"audio\";\n\nfunction mimeMedia(mime: string | undefined): MediaType | undefined {\n if (mime === undefined) {\n return undefined;\n }\n const slash = mime.indexOf(\"/\");\n const base = slash === -1 ? mime : mime.slice(0, slash);\n return base === \"image\" || base === \"video\" || base === \"audio\" ? base : undefined;\n}\n\nfunction extMedia(pathname: string): MediaType | undefined {\n const dot = pathname.lastIndexOf(\".\");\n if (dot === -1) {\n return undefined;\n }\n const ext = pathname.slice(dot + 1).toLowerCase();\n if (IMAGE_EXT.has(ext)) {\n return \"image\";\n }\n if (VIDEO_EXT.has(ext)) {\n return \"video\";\n }\n return AUDIO_EXT.has(ext) ? \"audio\" : undefined;\n}\n\nfunction urlBlock(\n raw: string,\n relay: boolean,\n imeta: ReadonlyMap<string, string> | undefined,\n): ContentBlock | undefined {\n const url = trimUrlTail(raw);\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return undefined;\n }\n if (!parsed.hostname.includes(\".\")) {\n return undefined;\n }\n if (relay) {\n return { type: \"relay\", url };\n }\n const media = mimeMedia(imeta?.get(url)) ?? extMedia(parsed.pathname);\n return media === undefined ? { type: \"url\", url } : { type: media, url };\n}\n\nfunction referenceFor(code: string, bare: boolean): ContentBlock | undefined {\n let pointer: ProfilePointer | EventPointer | AddressPointer;\n try {\n const decoded = decode(code);\n switch (decoded.type) {\n case \"npub\":\n pointer = { pubkey: decoded.data };\n break;\n case \"note\":\n pointer = { id: decoded.data };\n break;\n case \"nprofile\":\n case \"nevent\":\n case \"naddr\":\n pointer = decoded.data;\n break;\n case \"nsec\":\n return undefined;\n }\n } catch {\n return undefined;\n }\n return { type: \"reference\", pointer, bare };\n}\n\n/**\n * Parse note content (or a full event, to pick up `emoji` tags) into ordered blocks. One\n * left-to-right scan; at every position the rules fire in the alternation order above. Adjacent\n * text merges and no empty text blocks are emitted.\n */\nexport function parseContent(\n content: string | Pick<Event, \"content\" | \"tags\">,\n opts?: ParseContentOptions,\n): ContentBlock[] {\n const emojis = new Map<string, string>();\n let text: string;\n if (typeof content === \"string\") {\n text = content;\n } else {\n for (const tag of content.tags) {\n if (\n tag[0] === \"emoji\" &&\n tag[1] !== undefined &&\n tag[1] !== \"\" &&\n tag[2] !== undefined &&\n tag[2] !== \"\"\n ) {\n emojis.set(tag[1], tag[2]);\n }\n }\n text = content.content;\n }\n\n const scan = opts?.legacyBech32 === true ? SCAN_LEGACY : SCAN;\n scan.lastIndex = 0;\n\n const blocks: ContentBlock[] = [];\n let textStart = 0;\n const flushText = (end: number): void => {\n if (end > textStart) {\n blocks.push({ type: \"text\", text: text.slice(textStart, end) });\n }\n };\n\n for (let m = scan.exec(text); m !== null; m = scan.exec(text)) {\n const start = m.index;\n const [token] = m;\n const end = start + token.length;\n const g = m.groups;\n let block: ContentBlock | undefined;\n\n if (g?.[\"nostr\"] !== undefined) {\n block = referenceFor(token.slice(\"nostr:\".length), false);\n } else if (g?.[\"url\"] !== undefined) {\n block = urlBlock(token, false, opts?.imeta);\n } else if (g?.[\"relay\"] !== undefined) {\n block = urlBlock(token, true, undefined);\n } else if (g?.[\"invoice\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n if (!isChar(prev, WORD_CHAR) && !isChar(text.codePointAt(end), WORD_CHAR)) {\n const invoice = token.toLowerCase().startsWith(\"lightning:\")\n ? token.slice(\"lightning:\".length)\n : token;\n block = { type: \"invoice\", bolt11: invoice.toLowerCase() };\n }\n } else if (g?.[\"hashtag\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n // A tag char right after the match means the {1,42} cap truncated a longer\n // run; the whole run is text, not a hashtag plus remainder.\n if (!isChar(prev, TAG_CHAR) && !isChar(text.codePointAt(end), TAG_CHAR)) {\n block = { type: \"hashtag\", value: token.slice(1) };\n }\n } else if (g?.[\"emoji\"] !== undefined) {\n const url = emojis.get(token.slice(1, -1));\n if (url !== undefined) {\n block = { type: \"emoji\", shortcode: token.slice(1, -1), url };\n }\n } else if (g?.[\"bare\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n if (!isChar(prev, WORD_CHAR) && !isChar(text.codePointAt(end), WORD_CHAR)) {\n block = referenceFor(token, true);\n }\n }\n\n if (block === undefined) {\n // The candidate is plain text; resume scanning one code unit later so any token inside\n // its span can still match.\n scan.lastIndex = start + 1;\n continue;\n }\n flushText(start);\n blocks.push(block);\n // URL-family blocks may end before the regex run once trailing punctuation is trimmed.\n textStart =\n block.type === \"url\" ||\n block.type === \"relay\" ||\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"audio\"\n ? start + block.url.length\n : end;\n }\n\n flushText(text.length);\n return blocks;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { f as nowSeconds } from "../util-DDpPkiED.mjs";
2
- import { t as Kind } from "../kind-D8fENiiL.mjs";
1
+ import { p as nowSeconds } from "../util-DfsyJ9nz.mjs";
2
+ import { t as Kind } from "../kind-I2Tvbe82.mjs";
3
3
  //#region src/nips/nip42.ts
4
4
  /**
5
5
  * Build an unsigned NIP-42 AUTH event template for the given relay challenge. Caller must sign with