@qntx/nostr 0.1.0 → 0.2.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 (185) hide show
  1. package/dist/abort-Ba8tDrOI.mjs +38 -0
  2. package/dist/abort-Ba8tDrOI.mjs.map +1 -0
  3. package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
  4. package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
  5. package/dist/builder-CyrGKRVP.mjs.map +1 -0
  6. package/dist/{client-Bd8B1mYm.mjs → client-BWsX9Cv1.mjs} +354 -195
  7. package/dist/client-BWsX9Cv1.mjs.map +1 -0
  8. package/dist/client.d.mts +1 -1
  9. package/dist/client.mjs +1 -1
  10. package/dist/core.d.mts +8 -8
  11. package/dist/core.mjs +8 -9
  12. package/dist/error-BW7re1UN.d.mts +9 -0
  13. package/dist/error-B__OMBRY.mjs +14 -0
  14. package/dist/error-B__OMBRY.mjs.map +1 -0
  15. package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
  16. package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
  17. package/dist/error-DrEIM7FO.mjs.map +1 -0
  18. package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
  19. package/dist/event-D8Nfoh4K.mjs.map +1 -0
  20. package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
  21. package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
  22. package/dist/filter-BQl3GlHt.mjs.map +1 -0
  23. package/dist/filter-C0IMByE3.d.mts +37 -0
  24. package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
  25. package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
  26. package/dist/gossip-oloDMWEj.mjs.map +1 -0
  27. package/dist/gossip.d.mts +1 -1
  28. package/dist/gossip.mjs +1 -1
  29. package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
  30. package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
  31. package/dist/http-CuazNRIP.mjs.map +1 -0
  32. package/dist/index-B425CAcQ.d.mts +328 -0
  33. package/dist/index-BVyfUBuG.d.mts +181 -0
  34. package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
  35. package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
  36. package/dist/index-CUIvFQdE.d.mts +73 -0
  37. package/dist/index-JdaS_uce.d.mts +207 -0
  38. package/dist/index.d.mts +22 -20
  39. package/dist/index.mjs +19 -18
  40. package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
  41. package/dist/key-Bbtys6o7.mjs.map +1 -0
  42. package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
  43. package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
  44. package/dist/kind-Cxwhhkx8.mjs.map +1 -0
  45. package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
  46. package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
  47. package/dist/loaders.d.mts +3 -3
  48. package/dist/loaders.mjs +2 -2
  49. package/dist/memory-C2EVx0_O.mjs +46 -0
  50. package/dist/memory-C2EVx0_O.mjs.map +1 -0
  51. package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
  52. package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
  53. package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
  54. package/dist/message-DcY6Fi9o.mjs.map +1 -0
  55. package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
  56. package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
  57. package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
  58. package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
  59. package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
  60. package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
  61. package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
  62. package/dist/nips/blossom.d.mts +37 -37
  63. package/dist/nips/blossom.mjs +67 -52
  64. package/dist/nips/blossom.mjs.map +1 -1
  65. package/dist/nips/nip04.d.mts +2 -3
  66. package/dist/nips/nip04.mjs +6 -10
  67. package/dist/nips/nip04.mjs.map +1 -1
  68. package/dist/nips/nip05.d.mts +37 -43
  69. package/dist/nips/nip05.mjs +57 -57
  70. package/dist/nips/nip05.mjs.map +1 -1
  71. package/dist/nips/nip10.d.mts +18 -19
  72. package/dist/nips/nip10.mjs +76 -61
  73. package/dist/nips/nip10.mjs.map +1 -1
  74. package/dist/nips/nip11.d.mts +9 -8
  75. package/dist/nips/nip11.mjs +14 -11
  76. package/dist/nips/nip11.mjs.map +1 -1
  77. package/dist/nips/nip13.d.mts +9 -9
  78. package/dist/nips/nip13.mjs +15 -17
  79. package/dist/nips/nip13.mjs.map +1 -1
  80. package/dist/nips/nip17.d.mts +1 -1
  81. package/dist/nips/nip17.mjs +17 -12
  82. package/dist/nips/nip17.mjs.map +1 -1
  83. package/dist/nips/nip19.d.mts +1 -1
  84. package/dist/nips/nip19.mjs +52 -29
  85. package/dist/nips/nip19.mjs.map +1 -1
  86. package/dist/nips/nip21.d.mts +5 -6
  87. package/dist/nips/nip21.mjs +8 -7
  88. package/dist/nips/nip21.mjs.map +1 -1
  89. package/dist/nips/nip27.d.mts +7 -8
  90. package/dist/nips/nip27.mjs +26 -25
  91. package/dist/nips/nip27.mjs.map +1 -1
  92. package/dist/nips/nip42.d.mts +5 -6
  93. package/dist/nips/nip42.mjs +3 -3
  94. package/dist/nips/nip42.mjs.map +1 -1
  95. package/dist/nips/nip44.d.mts +24 -7
  96. package/dist/nips/nip44.mjs +25 -15
  97. package/dist/nips/nip44.mjs.map +1 -1
  98. package/dist/nips/nip46.d.mts +1 -1
  99. package/dist/nips/nip46.mjs +52 -49
  100. package/dist/nips/nip46.mjs.map +1 -1
  101. package/dist/nips/nip49.d.mts +8 -7
  102. package/dist/nips/nip49.mjs +16 -12
  103. package/dist/nips/nip49.mjs.map +1 -1
  104. package/dist/nips/nip51.d.mts +25 -27
  105. package/dist/nips/nip51.mjs +47 -36
  106. package/dist/nips/nip51.mjs.map +1 -1
  107. package/dist/nips/nip57.d.mts +12 -13
  108. package/dist/nips/nip57.mjs +35 -38
  109. package/dist/nips/nip57.mjs.map +1 -1
  110. package/dist/nips/nip59.d.mts +1 -1
  111. package/dist/nips/nip59.mjs +14 -18
  112. package/dist/nips/nip59.mjs.map +1 -1
  113. package/dist/nips/nip65.d.mts +1 -1
  114. package/dist/nips/nip65.mjs +11 -8
  115. package/dist/nips/nip65.mjs.map +1 -1
  116. package/dist/nips/nip77.d.mts +1 -1
  117. package/dist/nips/nip77.mjs +56 -34
  118. package/dist/nips/nip77.mjs.map +1 -1
  119. package/dist/nips/nip96.d.mts +21 -12
  120. package/dist/nips/nip96.mjs +59 -36
  121. package/dist/nips/nip96.mjs.map +1 -1
  122. package/dist/nips/nip98.d.mts +12 -12
  123. package/dist/nips/nip98.mjs +18 -19
  124. package/dist/nips/nip98.mjs.map +1 -1
  125. package/dist/{pool-C_j1OmBB.mjs → pool-XmAGaufS.mjs} +611 -487
  126. package/dist/pool-XmAGaufS.mjs.map +1 -0
  127. package/dist/pool-tEr7c5bG.d.mts +302 -0
  128. package/dist/reactive-DDlThDSZ.mjs +324 -0
  129. package/dist/reactive-DDlThDSZ.mjs.map +1 -0
  130. package/dist/reactive-DFpqtIf_.d.mts +66 -0
  131. package/dist/relay.d.mts +5 -4
  132. package/dist/relay.mjs +2 -2
  133. package/dist/report-CHld4uWj.mjs +32 -0
  134. package/dist/report-CHld4uWj.mjs.map +1 -0
  135. package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
  136. package/dist/signer-CKR9EHce.mjs.map +1 -0
  137. package/dist/signer.d.mts +3 -3
  138. package/dist/signer.mjs +3 -2
  139. package/dist/storage-xXxLGCVb.mjs +1376 -0
  140. package/dist/storage-xXxLGCVb.mjs.map +1 -0
  141. package/dist/storage.d.mts +4 -4
  142. package/dist/storage.mjs +5 -4
  143. package/dist/store.d.mts +2 -0
  144. package/dist/store.mjs +2 -0
  145. package/dist/testing.d.mts +125 -0
  146. package/dist/testing.mjs +957 -0
  147. package/dist/testing.mjs.map +1 -0
  148. package/dist/types-2pCUprRU.d.mts +47 -0
  149. package/dist/types-CRQEk0Oi.d.mts +19 -0
  150. package/dist/util-LfQirgDk.mjs +113 -0
  151. package/dist/util-LfQirgDk.mjs.map +1 -0
  152. package/dist/wasm.d.mts +4 -4
  153. package/dist/wasm.mjs +54 -55
  154. package/dist/wasm.mjs.map +1 -1
  155. package/dist/websocket-CmeqvAkp.d.mts +27 -0
  156. package/package.json +31 -9
  157. package/dist/builder-q0VsvnYE.mjs.map +0 -1
  158. package/dist/client-Bd8B1mYm.mjs.map +0 -1
  159. package/dist/error-B49rbDzT.d.mts +0 -7
  160. package/dist/error-CwBxle6N.mjs.map +0 -1
  161. package/dist/error-DZEs5OuZ.mjs +0 -24
  162. package/dist/error-DZEs5OuZ.mjs.map +0 -1
  163. package/dist/event-CzoQEFMF.mjs.map +0 -1
  164. package/dist/filter-BxwjEkZp.mjs.map +0 -1
  165. package/dist/filter-DUEKP3q1.d.mts +0 -40
  166. package/dist/gossip-3xkRRCm_.mjs.map +0 -1
  167. package/dist/http-D2rhokZo.mjs.map +0 -1
  168. package/dist/index-C0C4e-Gn.d.mts +0 -55
  169. package/dist/index-DgOugrpF.d.mts +0 -275
  170. package/dist/index-EwL6fHLo.d.mts +0 -267
  171. package/dist/index-k2YhCgsh.d.mts +0 -57
  172. package/dist/key-Dn-aX4x4.mjs.map +0 -1
  173. package/dist/kind-BLl4cLOc.mjs.map +0 -1
  174. package/dist/loaders-BNQszDAn.mjs.map +0 -1
  175. package/dist/memory-BCmhZPek.mjs.map +0 -1
  176. package/dist/message-C_Uj-Y-c.mjs.map +0 -1
  177. package/dist/pool-C_j1OmBB.mjs.map +0 -1
  178. package/dist/pool-DhRi7l-b.d.mts +0 -297
  179. package/dist/signer-CZ6K6FUc.mjs.map +0 -1
  180. package/dist/storage-DOy0iLRE.mjs +0 -870
  181. package/dist/storage-DOy0iLRE.mjs.map +0 -1
  182. package/dist/types-BzyADqnw.d.mts +0 -19
  183. package/dist/types-Dn24TPFs.d.mts +0 -43
  184. package/dist/util-6tfLQ9JF.mjs +0 -73
  185. package/dist/util-6tfLQ9JF.mjs.map +0 -1
@@ -1,8 +1,8 @@
1
- import { t as Kind } from "../kind-BLl4cLOc.mjs";
1
+ import { t as Kind } from "../kind-Cxwhhkx8.mjs";
2
2
  //#region src/nips/nip42.ts
3
3
  /**
4
- * Build an unsigned NIP-42 AUTH event template for the given relay challenge.
5
- * Caller must sign with their `NostrSigner` / keys.
4
+ * Build an unsigned NIP-42 AUTH event template for the given relay challenge. Caller must sign with
5
+ * their `NostrSigner` / keys.
6
6
  */
7
7
  function makeAuthEvent(relayURL, challenge) {
8
8
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"nip42.mjs","names":[],"sources":["../../src/nips/nip42.ts"],"sourcesContent":["import type { EventTemplate } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\n\n/**\n * Build an unsigned NIP-42 AUTH event template for the given relay challenge.\n * Caller must sign with their `NostrSigner` / keys.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n return {\n kind: Kind.ClientAuth,\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n [\"relay\", relayURL],\n [\"challenge\", challenge],\n ],\n content: \"\",\n };\n}\n\n/** True when a CLOSED reason requests NIP-42 authentication. */\nexport function isAuthRequired(reason: string): boolean {\n return reason.startsWith(\"auth-required:\");\n}\n"],"mappings":";;;;;;AAOA,SAAgB,cAAc,UAAkB,WAAkC;CAChF,OAAO;EACL,MAAM,KAAK;EACX,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACxC,MAAM,CACJ,CAAC,SAAS,QAAQ,GAClB,CAAC,aAAa,SAAS,CACzB;EACA,SAAS;CACX;AACF;;AAGA,SAAgB,eAAe,QAAyB;CACtD,OAAO,OAAO,WAAW,gBAAgB;AAC3C"}
1
+ {"version":3,"file":"nip42.mjs","names":[],"sources":["../../src/nips/nip42.ts"],"sourcesContent":["import type { EventTemplate } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\n\n/**\n * Build an unsigned NIP-42 AUTH event template for the given relay challenge. Caller must sign with\n * their `NostrSigner` / keys.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n return {\n kind: Kind.ClientAuth,\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n [\"relay\", relayURL],\n [\"challenge\", challenge],\n ],\n content: \"\",\n };\n}\n\n/** True when a CLOSED reason requests NIP-42 authentication. */\nexport function isAuthRequired(reason: string): boolean {\n return reason.startsWith(\"auth-required:\");\n}\n"],"mappings":";;;;;;AAOA,SAAgB,cAAc,UAAkB,WAAkC;CAChF,OAAO;EACL,MAAM,KAAK;EACX,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACxC,MAAM,CACJ,CAAC,SAAS,QAAQ,GAClB,CAAC,aAAa,SAAS,CACzB;EACA,SAAS;CACX;AACF;;AAGA,SAAgB,eAAe,QAAyB;CACtD,OAAO,OAAO,WAAW,gBAAgB;AAC3C"}
@@ -1,12 +1,29 @@
1
1
  //#region src/nips/nip44.d.ts
2
- declare function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array;
3
- declare function calcPaddedLen(len: number): number;
4
- declare function encrypt(plaintext: string, conversationKey: Uint8Array, nonce?: Uint8Array): string;
5
- declare function decrypt(payload: string, conversationKey: Uint8Array): string;
2
+ /**
3
+ * Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000
4
+ * bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +
5
+ * mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap
6
+ * events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for
7
+ * large local payloads.
8
+ */
9
+ export declare const DEFAULT_MAX_PAYLOAD_CHARS: number;
10
+ export declare function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array;
11
+ /** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */
12
+ export declare function getMessageKeys(conversationKey: Uint8Array, nonce: Uint8Array): {
13
+ chacha_key: Uint8Array;
14
+ chacha_nonce: Uint8Array;
15
+ hmac_key: Uint8Array;
16
+ };
17
+ export declare function calcPaddedLen(len: number): number;
18
+ export declare function encrypt(plaintext: string, conversationKey: Uint8Array, nonce?: Uint8Array): string;
19
+ export declare function decrypt(payload: string, conversationKey: Uint8Array, opts?: {
20
+ maxPayloadChars?: number;
21
+ }): string;
6
22
  /** Convenience: derive conversation key from secret/public pair and encrypt. */
7
- declare function encryptToPubkey(plaintext: string, secretKey: Uint8Array, peerPubkey: string): string;
23
+ export declare function encryptToPubkey(plaintext: string, secretKey: Uint8Array, peerPubkey: string): string;
8
24
  /** Convenience: decrypt a payload from a peer pubkey. */
9
- declare function decryptFromPubkey(payload: string, secretKey: Uint8Array, peerPubkey: string): string;
25
+ export declare function decryptFromPubkey(payload: string, secretKey: Uint8Array, peerPubkey: string, opts?: {
26
+ maxPayloadChars?: number;
27
+ }): string;
10
28
  //#endregion
11
- export { calcPaddedLen, decrypt, decryptFromPubkey, encrypt, encryptToPubkey, getConversationKey };
12
29
  //# sourceMappingURL=nip44.d.mts.map
@@ -1,8 +1,7 @@
1
- import { c as utf8Decoder, i as hexToBytes, l as utf8Encoder, n as assertSecretKeyBytes, t as assertHex32 } from "../util-6tfLQ9JF.mjs";
2
- import { t as CryptoError } from "../error-DZEs5OuZ.mjs";
1
+ import { a as hexToBytes$1, d as utf8Encoder, f as CryptoError, n as assertHex32, r as assertSecretKeyBytes, u as utf8Decoder } from "../util-LfQirgDk.mjs";
2
+ import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
3
3
  import { sha256 } from "@noble/hashes/sha2.js";
4
4
  import { secp256k1 } from "@noble/curves/secp256k1.js";
5
- import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
6
5
  import { base64 } from "@scure/base";
7
6
  import { chacha20 } from "@noble/ciphers/chacha.js";
8
7
  import { equalBytes } from "@noble/ciphers/utils.js";
@@ -12,15 +11,24 @@ import { hmac } from "@noble/hashes/hmac.js";
12
11
  const minPlaintextSize = 1;
13
12
  const maxPlaintextSize = 4294967295;
14
13
  const extendedPrefixThreshold = 65536;
14
+ /**
15
+ * Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000
16
+ * bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +
17
+ * mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap
18
+ * events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for
19
+ * large local payloads.
20
+ */
21
+ const DEFAULT_MAX_PAYLOAD_CHARS = Math.ceil((71 + calcPaddedLen(1048576)) / 3) * 4;
15
22
  function assert32(bytes, label) {
16
23
  if (bytes.length !== 32) throw new CryptoError(`${label} must be 32 bytes`);
17
24
  }
18
25
  function getConversationKey(privkeyA, pubkeyB) {
19
26
  assertSecretKeyBytes(privkeyA);
20
27
  assertHex32(pubkeyB, "public key");
21
- const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes("02" + pubkeyB.toLowerCase())).subarray(1, 33);
28
+ const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes$1(`02${pubkeyB.toLowerCase()}`)).subarray(1, 33);
22
29
  return extract(sha256, sharedX, utf8Encoder.encode("nip44-v2"));
23
30
  }
31
+ /** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */
24
32
  function getMessageKeys(conversationKey, nonce) {
25
33
  assert32(conversationKey, "conversation_key");
26
34
  assert32(nonce, "nonce");
@@ -79,18 +87,20 @@ function hmacAad(key, message, aad) {
79
87
  if (aad.length !== 32) throw new CryptoError("AAD associated data must be 32 bytes");
80
88
  return hmac(sha256, key, concatBytes(aad, message));
81
89
  }
82
- function decodePayload(payload) {
90
+ function decodePayload(payload, maxPayloadChars) {
83
91
  if (typeof payload !== "string") throw new CryptoError("payload must be a valid string");
84
- if (payload.length < 132) throw new CryptoError("invalid payload length: " + payload.length);
85
- if (payload[0] === "#") throw new CryptoError("unknown encryption version");
92
+ if (payload.startsWith("#")) throw new CryptoError("unknown encryption version");
93
+ if (payload.length < 132 || payload.length > maxPayloadChars) throw new CryptoError(`invalid payload length: ${payload.length}`);
86
94
  let data;
87
95
  try {
88
96
  data = base64.decode(payload);
89
97
  } catch (error) {
90
- throw new CryptoError("invalid base64: " + (error instanceof Error ? error.message : "decode failed"));
98
+ throw new CryptoError(`invalid base64: ${error instanceof Error ? error.message : "decode failed"}`);
91
99
  }
92
- if (data.length < 99) throw new CryptoError("invalid data length: " + data.length);
93
- if (data[0] !== 2) throw new CryptoError("unknown encryption version " + data[0]);
100
+ if (data.length < 99) throw new CryptoError(`invalid data length: ${data.length}`);
101
+ const version = data.at(0);
102
+ if (version === void 0) throw new CryptoError("invalid data length");
103
+ if (version !== 2) throw new CryptoError(`unknown encryption version ${version}`);
94
104
  return {
95
105
  nonce: data.subarray(1, 33),
96
106
  ciphertext: data.subarray(33, -32),
@@ -104,8 +114,8 @@ function encrypt(plaintext, conversationKey, nonce = randomBytes(32)) {
104
114
  const mac = hmacAad(hmac_key, ciphertext, nonce);
105
115
  return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));
106
116
  }
107
- function decrypt(payload, conversationKey) {
108
- const { nonce, ciphertext, mac } = decodePayload(payload);
117
+ function decrypt(payload, conversationKey, opts) {
118
+ const { nonce, ciphertext, mac } = decodePayload(payload, opts?.maxPayloadChars ?? DEFAULT_MAX_PAYLOAD_CHARS);
109
119
  const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
110
120
  const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);
111
121
  if (!equalBytes(calculatedMac, mac)) throw new CryptoError("invalid MAC");
@@ -116,10 +126,10 @@ function encryptToPubkey(plaintext, secretKey, peerPubkey) {
116
126
  return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));
117
127
  }
118
128
  /** Convenience: decrypt a payload from a peer pubkey. */
119
- function decryptFromPubkey(payload, secretKey, peerPubkey) {
120
- return decrypt(payload, getConversationKey(secretKey, peerPubkey));
129
+ function decryptFromPubkey(payload, secretKey, peerPubkey, opts) {
130
+ return decrypt(payload, getConversationKey(secretKey, peerPubkey), opts);
121
131
  }
122
132
  //#endregion
123
- export { calcPaddedLen, decrypt, decryptFromPubkey, encrypt, encryptToPubkey, getConversationKey };
133
+ export { DEFAULT_MAX_PAYLOAD_CHARS, calcPaddedLen, decrypt, decryptFromPubkey, encrypt, encryptToPubkey, getConversationKey, getMessageKeys };
124
134
 
125
135
  //# sourceMappingURL=nip44.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"nip44.mjs","names":["hkdf_extract","hkdf_expand"],"sources":["../../src/nips/nip44.ts"],"sourcesContent":["import { chacha20 } from \"@noble/ciphers/chacha.js\";\nimport { equalBytes } from \"@noble/ciphers/utils.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { extract as hkdf_extract, expand as hkdf_expand } from \"@noble/hashes/hkdf.js\";\nimport { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\nimport { CryptoError } from \"../core/error.ts\";\nimport {\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\nconst minPlaintextSize = 0x0001;\nconst maxPlaintextSize = 0xffffffff;\nconst extendedPrefixThreshold = 0x10000;\n\nfunction assert32(bytes: Uint8Array, label: string): void {\n if (bytes.length !== 32) throw new CryptoError(`${label} must be 32 bytes`);\n}\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n assertSecretKeyBytes(privkeyA);\n assertHex32(pubkeyB, \"public key\");\n const sharedX = secp256k1\n .getSharedSecret(privkeyA, hexToBytes(\"02\" + pubkeyB.toLowerCase()))\n .subarray(1, 33);\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode(\"nip44-v2\"));\n}\n\nfunction getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n assert32(conversationKey, \"conversation_key\");\n assert32(nonce, \"nonce\");\n const keys = hkdf_expand(sha256, conversationKey, nonce, 76);\n return {\n chacha_key: keys.subarray(0, 32),\n chacha_nonce: keys.subarray(32, 44),\n hmac_key: keys.subarray(44, 76),\n };\n}\n\nexport function calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) throw new CryptoError(\"expected positive integer\");\n if (len <= 32) return 32;\n const nextPower = 2 ** (Math.floor(Math.log2(len - 1)) + 1);\n const chunk = nextPower <= 256 ? 32 : nextPower / 8;\n return chunk * (Math.floor((len - 1) / chunk) + 1);\n}\n\nfunction writeU16BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > 0xffff) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 65535 bytes\");\n }\n const arr = new Uint8Array(2);\n new DataView(arr.buffer).setUint16(0, num, false);\n return arr;\n}\n\nfunction writeU32BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < extendedPrefixThreshold || num > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");\n }\n const arr = new Uint8Array(4);\n new DataView(arr.buffer).setUint32(0, num, false);\n return arr;\n}\n\nfunction pad(plaintext: string): Uint8Array {\n const unpadded = utf8Encoder.encode(plaintext);\n const unpaddedLen = unpadded.length;\n if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");\n }\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen))\n : writeU16BE(unpaddedLen);\n const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);\n return concatBytes(prefix, unpadded, suffix);\n}\n\nfunction unpad(padded: Uint8Array): string {\n const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);\n const firstTwo = dv.getUint16(0);\n let unpaddedLen: number;\n let prefixLen: number;\n if (firstTwo === 0) {\n unpaddedLen = dv.getUint32(2);\n if (unpaddedLen < extendedPrefixThreshold) throw new CryptoError(\"invalid padding\");\n prefixLen = 6;\n } else {\n unpaddedLen = firstTwo;\n prefixLen = 2;\n }\n const unpadded = padded.subarray(prefixLen, prefixLen + unpaddedLen);\n if (\n unpaddedLen < minPlaintextSize ||\n unpaddedLen > maxPlaintextSize ||\n unpadded.length !== unpaddedLen ||\n padded.length !== prefixLen + calcPaddedLen(unpaddedLen)\n ) {\n throw new CryptoError(\"invalid padding\");\n }\n return utf8Decoder.decode(unpadded);\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) throw new CryptoError(\"AAD associated data must be 32 bytes\");\n return hmac(sha256, key, concatBytes(aad, message));\n}\n\nfunction decodePayload(payload: string): {\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n mac: Uint8Array;\n} {\n if (typeof payload !== \"string\") throw new CryptoError(\"payload must be a valid string\");\n if (payload.length < 132) throw new CryptoError(\"invalid payload length: \" + payload.length);\n if (payload[0] === \"#\") throw new CryptoError(\"unknown encryption version\");\n let data: Uint8Array;\n try {\n data = base64.decode(payload);\n } catch (error) {\n throw new CryptoError(\n \"invalid base64: \" + (error instanceof Error ? error.message : \"decode failed\"),\n );\n }\n if (data.length < 99) throw new CryptoError(\"invalid data length: \" + data.length);\n if (data[0] !== 2) throw new CryptoError(\"unknown encryption version \" + data[0]);\n return {\n nonce: data.subarray(1, 33),\n ciphertext: data.subarray(33, -32),\n mac: data.subarray(-32),\n };\n}\n\nexport function encrypt(\n plaintext: string,\n conversationKey: Uint8Array,\n nonce: Uint8Array = randomBytes(32),\n): string {\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const padded = pad(plaintext);\n const ciphertext = chacha20(chacha_key, chacha_nonce, padded);\n const mac = hmacAad(hmac_key, ciphertext, nonce);\n return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n const { nonce, ciphertext, mac } = decodePayload(payload);\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);\n if (!equalBytes(calculatedMac, mac)) throw new CryptoError(\"invalid MAC\");\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext);\n return unpad(padded);\n}\n\n/** Convenience: derive conversation key from secret/public pair and encrypt. */\nexport function encryptToPubkey(\n plaintext: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));\n}\n\n/** Convenience: decrypt a payload from a peer pubkey. */\nexport function decryptFromPubkey(\n payload: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return decrypt(payload, getConversationKey(secretKey, peerPubkey));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip44.mjs","names":["hexToBytes","hkdf_extract","hkdf_expand"],"sources":["../../src/nips/nip44.ts"],"sourcesContent":["import { chacha20 } from \"@noble/ciphers/chacha.js\";\nimport { equalBytes } from \"@noble/ciphers/utils.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { extract as hkdf_extract, expand as hkdf_expand } from \"@noble/hashes/hkdf.js\";\nimport { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { CryptoError } from \"../core/error.ts\";\nimport {\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\nconst minPlaintextSize = 0x0001;\nconst maxPlaintextSize = 0xffffffff;\nconst extendedPrefixThreshold = 0x10000;\n\n/**\n * Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000\n * bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +\n * mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap\n * events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for\n * large local payloads.\n */\nexport const DEFAULT_MAX_PAYLOAD_CHARS: number = Math.ceil((71 + calcPaddedLen(0x100000)) / 3) * 4;\n\nfunction assert32(bytes: Uint8Array, label: string): void {\n if (bytes.length !== 32) {\n throw new CryptoError(`${label} must be 32 bytes`);\n }\n}\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n assertSecretKeyBytes(privkeyA);\n assertHex32(pubkeyB, \"public key\");\n const sharedX = secp256k1\n .getSharedSecret(privkeyA, hexToBytes(`02${pubkeyB.toLowerCase()}`))\n .subarray(1, 33);\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode(\"nip44-v2\"));\n}\n\n/** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */\nexport function getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n assert32(conversationKey, \"conversation_key\");\n assert32(nonce, \"nonce\");\n const keys = hkdf_expand(sha256, conversationKey, nonce, 76);\n return {\n chacha_key: keys.subarray(0, 32),\n chacha_nonce: keys.subarray(32, 44),\n hmac_key: keys.subarray(44, 76),\n };\n}\n\nexport function calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) {\n throw new CryptoError(\"expected positive integer\");\n }\n if (len <= 32) {\n return 32;\n }\n const nextPower = 2 ** (Math.floor(Math.log2(len - 1)) + 1);\n const chunk = nextPower <= 256 ? 32 : nextPower / 8;\n return chunk * (Math.floor((len - 1) / chunk) + 1);\n}\n\nfunction writeU16BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > 0xffff) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 65535 bytes\");\n }\n const arr = new Uint8Array(2);\n new DataView(arr.buffer).setUint16(0, num, false);\n return arr;\n}\n\nfunction writeU32BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < extendedPrefixThreshold || num > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");\n }\n const arr = new Uint8Array(4);\n new DataView(arr.buffer).setUint32(0, num, false);\n return arr;\n}\n\nfunction pad(plaintext: string): Uint8Array {\n const unpadded = utf8Encoder.encode(plaintext);\n const unpaddedLen = unpadded.length;\n if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");\n }\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen))\n : writeU16BE(unpaddedLen);\n const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);\n return concatBytes(prefix, unpadded, suffix);\n}\n\nfunction unpad(padded: Uint8Array): string {\n const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);\n const firstTwo = dv.getUint16(0);\n let unpaddedLen: number;\n let prefixLen: number;\n if (firstTwo === 0) {\n unpaddedLen = dv.getUint32(2);\n if (unpaddedLen < extendedPrefixThreshold) {\n throw new CryptoError(\"invalid padding\");\n }\n prefixLen = 6;\n } else {\n unpaddedLen = firstTwo;\n prefixLen = 2;\n }\n const unpadded = padded.subarray(prefixLen, prefixLen + unpaddedLen);\n if (\n unpaddedLen < minPlaintextSize ||\n unpaddedLen > maxPlaintextSize ||\n unpadded.length !== unpaddedLen ||\n padded.length !== prefixLen + calcPaddedLen(unpaddedLen)\n ) {\n throw new CryptoError(\"invalid padding\");\n }\n return utf8Decoder.decode(unpadded);\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) {\n throw new CryptoError(\"AAD associated data must be 32 bytes\");\n }\n return hmac(sha256, key, concatBytes(aad, message));\n}\n\nfunction decodePayload(\n payload: string,\n maxPayloadChars: number,\n): {\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n mac: Uint8Array;\n} {\n if (typeof payload !== \"string\") {\n throw new CryptoError(\"payload must be a valid string\");\n }\n if (payload.startsWith(\"#\")) {\n throw new CryptoError(\"unknown encryption version\");\n }\n if (payload.length < 132 || payload.length > maxPayloadChars) {\n throw new CryptoError(`invalid payload length: ${payload.length}`);\n }\n let data: Uint8Array;\n try {\n data = base64.decode(payload);\n } catch (error) {\n throw new CryptoError(\n `invalid base64: ${error instanceof Error ? error.message : \"decode failed\"}`,\n );\n }\n if (data.length < 99) {\n throw new CryptoError(`invalid data length: ${data.length}`);\n }\n const version = data.at(0);\n if (version === undefined) {\n throw new CryptoError(\"invalid data length\");\n }\n if (version !== 2) {\n throw new CryptoError(`unknown encryption version ${version}`);\n }\n return {\n nonce: data.subarray(1, 33),\n ciphertext: data.subarray(33, -32),\n mac: data.subarray(-32),\n };\n}\n\nexport function encrypt(\n plaintext: string,\n conversationKey: Uint8Array,\n nonce: Uint8Array = randomBytes(32),\n): string {\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const padded = pad(plaintext);\n const ciphertext = chacha20(chacha_key, chacha_nonce, padded);\n const mac = hmacAad(hmac_key, ciphertext, nonce);\n return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));\n}\n\nexport function decrypt(\n payload: string,\n conversationKey: Uint8Array,\n opts?: { maxPayloadChars?: number },\n): string {\n const { nonce, ciphertext, mac } = decodePayload(\n payload,\n opts?.maxPayloadChars ?? DEFAULT_MAX_PAYLOAD_CHARS,\n );\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);\n if (!equalBytes(calculatedMac, mac)) {\n throw new CryptoError(\"invalid MAC\");\n }\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext);\n return unpad(padded);\n}\n\n/** Convenience: derive conversation key from secret/public pair and encrypt. */\nexport function encryptToPubkey(\n plaintext: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));\n}\n\n/** Convenience: decrypt a payload from a peer pubkey. */\nexport function decryptFromPubkey(\n payload: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n opts?: { maxPayloadChars?: number },\n): string {\n return decrypt(payload, getConversationKey(secretKey, peerPubkey), opts);\n}\n"],"mappings":";;;;;;;;;;AAkBA,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AACzB,MAAM,0BAA0B;;;;;;;;AAShC,MAAa,4BAAoC,KAAK,MAAM,KAAK,cAAc,OAAQ,KAAK,CAAC,IAAI;AAEjG,SAAS,SAAS,OAAmB,OAAqB;CACxD,IAAI,MAAM,WAAW,IACnB,MAAM,IAAI,YAAY,GAAG,MAAM,kBAAkB;AAErD;AAEA,SAAgB,mBAAmB,UAAsB,SAA6B;CACpF,qBAAqB,QAAQ;CAC7B,YAAY,SAAS,YAAY;CACjC,MAAM,UAAU,UACb,gBAAgB,UAAUA,aAAW,KAAK,QAAQ,YAAY,GAAG,CAAC,CAAC,CACnE,SAAS,GAAG,EAAE;CACjB,OAAOC,QAAa,QAAQ,SAAS,YAAY,OAAO,UAAU,CAAC;AACrE;;AAGA,SAAgB,eACd,iBACA,OAC4E;CAC5E,SAAS,iBAAiB,kBAAkB;CAC5C,SAAS,OAAO,OAAO;CACvB,MAAM,OAAOC,OAAY,QAAQ,iBAAiB,OAAO,EAAE;CAC3D,OAAO;EACL,YAAY,KAAK,SAAS,GAAG,EAAE;EAC/B,cAAc,KAAK,SAAS,IAAI,EAAE;EAClC,UAAU,KAAK,SAAS,IAAI,EAAE;CAChC;AACF;AAEA,SAAgB,cAAc,KAAqB;CACjD,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,MAAM,GACtC,MAAM,IAAI,YAAY,2BAA2B;CAEnD,IAAI,OAAO,IACT,OAAO;CAET,MAAM,YAAY,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,CAAC,CAAC,IAAI;CACzD,MAAM,QAAQ,aAAa,MAAM,KAAK,YAAY;CAClD,OAAO,SAAS,KAAK,OAAO,MAAM,KAAK,KAAK,IAAI;AAClD;AAEA,SAAS,WAAW,KAAyB;CAC3C,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,MAAM,oBAAoB,MAAM,OAChE,MAAM,IAAI,YAAY,2DAA2D;CAEnF,MAAM,sBAAM,IAAI,WAAW,CAAC;CAC5B,IAAI,SAAS,IAAI,MAAM,CAAC,CAAC,UAAU,GAAG,KAAK,KAAK;CAChD,OAAO;AACT;AAEA,SAAS,WAAW,KAAyB;CAC3C,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,MAAM,2BAA2B,MAAM,kBACvE,MAAM,IAAI,YAAY,oEAAoE;CAE5F,MAAM,sBAAM,IAAI,WAAW,CAAC;CAC5B,IAAI,SAAS,IAAI,MAAM,CAAC,CAAC,UAAU,GAAG,KAAK,KAAK;CAChD,OAAO;AACT;AAEA,SAAS,IAAI,WAA+B;CAC1C,MAAM,WAAW,YAAY,OAAO,SAAS;CAC7C,MAAM,cAAc,SAAS;CAC7B,IAAI,cAAc,oBAAoB,cAAc,kBAClD,MAAM,IAAI,YAAY,gEAAgE;CAExF,MAAM,SACJ,eAAe,0BACX,YAAY,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,WAAW,WAAW,CAAC,IAC3D,WAAW,WAAW;CAC5B,MAAM,SAAS,IAAI,WAAW,cAAc,WAAW,IAAI,WAAW;CACtE,OAAO,YAAY,QAAQ,UAAU,MAAM;AAC7C;AAEA,SAAS,MAAM,QAA4B;CACzC,MAAM,KAAK,IAAI,SAAS,OAAO,QAAQ,OAAO,YAAY,OAAO,UAAU;CAC3E,MAAM,WAAW,GAAG,UAAU,CAAC;CAC/B,IAAI;CACJ,IAAI;CACJ,IAAI,aAAa,GAAG;EAClB,cAAc,GAAG,UAAU,CAAC;EAC5B,IAAI,cAAc,yBAChB,MAAM,IAAI,YAAY,iBAAiB;EAEzC,YAAY;CACd,OAAO;EACL,cAAc;EACd,YAAY;CACd;CACA,MAAM,WAAW,OAAO,SAAS,WAAW,YAAY,WAAW;CACnE,IACE,cAAc,oBACd,cAAc,oBACd,SAAS,WAAW,eACpB,OAAO,WAAW,YAAY,cAAc,WAAW,GAEvD,MAAM,IAAI,YAAY,iBAAiB;CAEzC,OAAO,YAAY,OAAO,QAAQ;AACpC;AAEA,SAAS,QAAQ,KAAiB,SAAqB,KAA6B;CAClF,IAAI,IAAI,WAAW,IACjB,MAAM,IAAI,YAAY,sCAAsC;CAE9D,OAAO,KAAK,QAAQ,KAAK,YAAY,KAAK,OAAO,CAAC;AACpD;AAEA,SAAS,cACP,SACA,iBAKA;CACA,IAAI,OAAO,YAAY,UACrB,MAAM,IAAI,YAAY,gCAAgC;CAExD,IAAI,QAAQ,WAAW,GAAG,GACxB,MAAM,IAAI,YAAY,4BAA4B;CAEpD,IAAI,QAAQ,SAAS,OAAO,QAAQ,SAAS,iBAC3C,MAAM,IAAI,YAAY,2BAA2B,QAAQ,QAAQ;CAEnE,IAAI;CACJ,IAAI;EACF,OAAO,OAAO,OAAO,OAAO;CAC9B,SAAS,OAAO;EACd,MAAM,IAAI,YACR,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,iBAC9D;CACF;CACA,IAAI,KAAK,SAAS,IAChB,MAAM,IAAI,YAAY,wBAAwB,KAAK,QAAQ;CAE7D,MAAM,UAAU,KAAK,GAAG,CAAC;CACzB,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,YAAY,qBAAqB;CAE7C,IAAI,YAAY,GACd,MAAM,IAAI,YAAY,8BAA8B,SAAS;CAE/D,OAAO;EACL,OAAO,KAAK,SAAS,GAAG,EAAE;EAC1B,YAAY,KAAK,SAAS,IAAI,GAAG;EACjC,KAAK,KAAK,SAAS,GAAG;CACxB;AACF;AAEA,SAAgB,QACd,WACA,iBACA,QAAoB,YAAY,EAAE,GAC1B;CACR,MAAM,EAAE,YAAY,cAAc,aAAa,eAAe,iBAAiB,KAAK;CACpF,MAAM,SAAS,IAAI,SAAS;CAC5B,MAAM,aAAa,SAAS,YAAY,cAAc,MAAM;CAC5D,MAAM,MAAM,QAAQ,UAAU,YAAY,KAAK;CAC/C,OAAO,OAAO,OAAO,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,YAAY,GAAG,CAAC;AAC/E;AAEA,SAAgB,QACd,SACA,iBACA,MACQ;CACR,MAAM,EAAE,OAAO,YAAY,QAAQ,cACjC,SACA,MAAM,mBAAmB,yBAC3B;CACA,MAAM,EAAE,YAAY,cAAc,aAAa,eAAe,iBAAiB,KAAK;CACpF,MAAM,gBAAgB,QAAQ,UAAU,YAAY,KAAK;CACzD,IAAI,CAAC,WAAW,eAAe,GAAG,GAChC,MAAM,IAAI,YAAY,aAAa;CAGrC,OAAO,MADQ,SAAS,YAAY,cAAc,UAChC,CAAC;AACrB;;AAGA,SAAgB,gBACd,WACA,WACA,YACQ;CACR,OAAO,QAAQ,WAAW,mBAAmB,WAAW,UAAU,CAAC;AACrE;;AAGA,SAAgB,kBACd,SACA,WACA,YACA,MACQ;CACR,OAAO,QAAQ,SAAS,mBAAmB,WAAW,UAAU,GAAG,IAAI;AACzE"}
@@ -1,2 +1,2 @@
1
- import { a as Nip46Request, c as createNostrConnectURI, d as encodeNip46Request, f as encodeNip46Response, h as toBunkerURL, i as Nip46Error, l as decodeNip46Request, m as parseNostrConnectURI, n as BunkerPointer, o as Nip46Response, p as parseBunkerURL, r as ClientMetadata, s as NostrConnectParams, t as BUNKER_REGEX, u as decodeNip46Response } from "../nip46-C_DoxiS_.mjs";
1
+ import { a as Nip46Request, c as createNostrConnectURI, d as encodeNip46Request, f as encodeNip46Response, h as toBunkerURL, i as Nip46Error, l as decodeNip46Request, m as parseNostrConnectURI, n as BunkerPointer, o as Nip46Response, p as parseBunkerURL, r as ClientMetadata, s as NostrConnectParams, t as BUNKER_REGEX, u as decodeNip46Response } from "../nip46-DDV4dC56.mjs";
2
2
  export { BUNKER_REGEX, BunkerPointer, ClientMetadata, Nip46Error, Nip46Request, Nip46Response, NostrConnectParams, createNostrConnectURI, decodeNip46Request, decodeNip46Response, encodeNip46Request, encodeNip46Response, parseBunkerURL, parseNostrConnectURI, toBunkerURL };
@@ -1,5 +1,4 @@
1
- import { a as isHex32, t as assertHex32 } from "../util-6tfLQ9JF.mjs";
2
- import { a as NostrError } from "../error-DZEs5OuZ.mjs";
1
+ import { c as isRecord, g as NostrError, n as assertHex32, o as isHex32 } from "../util-LfQirgDk.mjs";
3
2
  //#region src/nips/nip46.ts
4
3
  /**
5
4
  * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.
@@ -9,6 +8,7 @@ import { a as NostrError } from "../error-DZEs5OuZ.mjs";
9
8
  */
10
9
  const BUNKER_REGEX = /^bunker:\/\/([0-9a-fA-F]{64})\??([?/\w:.=&%-]*)$/;
11
10
  var Nip46Error = class extends NostrError {
11
+ name = "Nip46Error";
12
12
  constructor(message, options) {
13
13
  super(message, options);
14
14
  }
@@ -17,40 +17,41 @@ var Nip46Error = class extends NostrError {
17
17
  function toBunkerURL(pointer) {
18
18
  const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);
19
19
  for (const relay of pointer.relays) url.searchParams.append("relay", relay);
20
- if (pointer.secret) url.searchParams.set("secret", pointer.secret);
20
+ if (pointer.secret !== void 0 && pointer.secret !== "") url.searchParams.set("secret", pointer.secret);
21
21
  return url.toString();
22
22
  }
23
23
  /**
24
- * Parse a `bunker://` URL into a pointer.
25
- * Returns null when the input is not a bunker URL (including NIP-05 identifiers).
24
+ * Parse a `bunker://` URL into a pointer. Returns undefined when the input is not a bunker URL
25
+ * (including NIP-05 identifiers).
26
26
  */
27
27
  function parseBunkerURL(input) {
28
- const match = input.trim().match(BUNKER_REGEX);
29
- if (!match?.[1]) return null;
28
+ const match = BUNKER_REGEX.exec(input.trim());
29
+ const hex = match?.at(1);
30
+ if (match === null || hex === void 0) return;
30
31
  try {
31
- const pubkey = assertHex32(match[1], "bunker pubkey");
32
- const qs = new URLSearchParams(match[2] ?? "");
32
+ const pubkey = assertHex32(hex, "bunker pubkey");
33
+ const qs = new URLSearchParams(match.at(2) ?? "");
33
34
  return {
34
35
  pubkey,
35
36
  relays: qs.getAll("relay"),
36
- secret: qs.get("secret")
37
+ secret: qs.get("secret") ?? void 0
37
38
  };
38
39
  } catch {
39
- return null;
40
+ return;
40
41
  }
41
42
  }
42
43
  /** Build a client-initiated `nostrconnect://` URI. */
43
44
  function createNostrConnectURI(params) {
44
- if (!isHex32(params.clientPubkey)) throw new Nip46Error("invalid client pubkey");
45
+ if (!isHex32(params.clientPubkey.toLowerCase())) throw new Nip46Error("invalid client pubkey");
45
46
  if (!params.secret) throw new Nip46Error("nostrconnect secret is required");
46
47
  if (params.relays.length === 0) throw new Nip46Error("at least one relay is required");
47
48
  const query = new URLSearchParams();
48
49
  for (const relay of params.relays) query.append("relay", relay);
49
50
  query.set("secret", params.secret);
50
- if (params.perms && params.perms.length > 0) query.set("perms", params.perms.join(","));
51
- if (params.name) query.set("name", params.name);
52
- if (params.url) query.set("url", params.url);
53
- if (params.image) query.set("image", params.image);
51
+ if (params.perms !== void 0 && params.perms.length > 0) query.set("perms", params.perms.join(","));
52
+ if (params.name !== void 0 && params.name !== "") query.set("name", params.name);
53
+ if (params.url !== void 0 && params.url !== "") query.set("url", params.url);
54
+ if (params.image !== void 0 && params.image !== "") query.set("image", params.image);
54
55
  return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;
55
56
  }
56
57
  /** Parse a `nostrconnect://` URI. */
@@ -58,26 +59,30 @@ function parseNostrConnectURI(uri) {
58
59
  let url;
59
60
  try {
60
61
  url = new URL(uri);
61
- } catch (cause) {
62
- throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, { cause: cause instanceof Error ? cause : void 0 });
62
+ } catch (error) {
63
+ throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, { cause: error instanceof Error ? error : void 0 });
63
64
  }
64
65
  if (url.protocol !== "nostrconnect:") throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);
65
66
  const clientPubkey = url.hostname || url.pathname.replace(/^\/*/, "");
66
- if (!isHex32(clientPubkey)) throw new Nip46Error("invalid client pubkey in nostrconnect URI");
67
+ if (!isHex32(clientPubkey.toLowerCase())) throw new Nip46Error("invalid client pubkey in nostrconnect URI");
67
68
  const secret = url.searchParams.get("secret");
68
- if (!secret) throw new Nip46Error("missing secret in nostrconnect URI");
69
+ if (secret === null || secret === "") throw new Nip46Error("missing secret in nostrconnect URI");
69
70
  const relays = url.searchParams.getAll("relay");
70
71
  if (relays.length === 0) throw new Nip46Error("missing relays in nostrconnect URI");
71
- const permsRaw = url.searchParams.get("perms");
72
- return {
72
+ const params = {
73
73
  clientPubkey: clientPubkey.toLowerCase(),
74
74
  relays,
75
- secret,
76
- perms: permsRaw ? permsRaw.split(",").filter(Boolean) : void 0,
77
- name: url.searchParams.get("name") ?? void 0,
78
- url: url.searchParams.get("url") ?? void 0,
79
- image: url.searchParams.get("image") ?? void 0
75
+ secret
80
76
  };
77
+ const permsRaw = url.searchParams.get("perms");
78
+ if (permsRaw !== null && permsRaw !== "") params.perms = permsRaw.split(",").filter((p) => p !== "");
79
+ const name = url.searchParams.get("name");
80
+ if (name !== null) params.name = name;
81
+ const metadataUrl = url.searchParams.get("url");
82
+ if (metadataUrl !== null) params.url = metadataUrl;
83
+ const image = url.searchParams.get("image");
84
+ if (image !== null) params.image = image;
85
+ return params;
81
86
  }
82
87
  function encodeNip46Request(req) {
83
88
  return JSON.stringify({
@@ -90,41 +95,39 @@ function decodeNip46Request(json) {
90
95
  let data;
91
96
  try {
92
97
  data = JSON.parse(json);
93
- } catch (cause) {
94
- throw new Nip46Error("invalid NIP-46 request JSON", { cause: cause instanceof Error ? cause : void 0 });
98
+ } catch (error) {
99
+ throw new Nip46Error("invalid NIP-46 request JSON", { cause: error instanceof Error ? error : void 0 });
95
100
  }
96
- if (!isRecord(data) || typeof data.id !== "string" || typeof data.method !== "string") throw new Nip46Error("invalid NIP-46 request shape");
97
- if (!Array.isArray(data.params) || !data.params.every((p) => typeof p === "string")) throw new Nip46Error("invalid NIP-46 request params");
101
+ if (!isRecord(data) || typeof data["id"] !== "string" || typeof data["method"] !== "string") throw new Nip46Error("invalid NIP-46 request shape");
102
+ if (!Array.isArray(data["params"]) || !data["params"].every((p) => typeof p === "string")) throw new Nip46Error("invalid NIP-46 request params");
98
103
  return {
99
- id: data.id,
100
- method: data.method,
101
- params: data.params
104
+ id: data["id"],
105
+ method: data["method"],
106
+ params: data["params"]
102
107
  };
103
108
  }
104
109
  function encodeNip46Response(res) {
105
110
  const body = { id: res.id };
106
- if (res.result !== void 0) body.result = res.result;
107
- if (res.error !== void 0) body.error = res.error;
111
+ if (res.result !== void 0) body["result"] = res.result;
112
+ if (res.error !== void 0) body["error"] = res.error;
108
113
  return JSON.stringify(body);
109
114
  }
110
115
  function decodeNip46Response(json) {
111
116
  let data;
112
117
  try {
113
118
  data = JSON.parse(json);
114
- } catch (cause) {
115
- throw new Nip46Error("invalid NIP-46 response JSON", { cause: cause instanceof Error ? cause : void 0 });
119
+ } catch (error) {
120
+ throw new Nip46Error("invalid NIP-46 response JSON", { cause: error instanceof Error ? error : void 0 });
116
121
  }
117
- if (!isRecord(data) || typeof data.id !== "string") throw new Nip46Error("invalid NIP-46 response shape");
118
- if (data.result !== void 0 && typeof data.result !== "string") throw new Nip46Error("invalid NIP-46 response result");
119
- if (data.error !== void 0 && typeof data.error !== "string") throw new Nip46Error("invalid NIP-46 response error");
120
- return {
121
- id: data.id,
122
- result: data.result,
123
- error: data.error
124
- };
125
- }
126
- function isRecord(value) {
127
- return typeof value === "object" && value !== null && !Array.isArray(value);
122
+ if (!isRecord(data) || typeof data["id"] !== "string") throw new Nip46Error("invalid NIP-46 response shape");
123
+ const result = data["result"] === null ? void 0 : data["result"];
124
+ const error = data["error"] === null ? void 0 : data["error"];
125
+ if (result !== void 0 && typeof result !== "string") throw new Nip46Error("invalid NIP-46 response result");
126
+ if (error !== void 0 && typeof error !== "string") throw new Nip46Error("invalid NIP-46 response error");
127
+ const response = { id: data["id"] };
128
+ if (result !== void 0) response.result = result;
129
+ if (error !== void 0) response.error = error;
130
+ return response;
128
131
  }
129
132
  //#endregion
130
133
  export { BUNKER_REGEX, Nip46Error, createNostrConnectURI, decodeNip46Request, decodeNip46Response, encodeNip46Request, encodeNip46Response, parseBunkerURL, parseNostrConnectURI, toBunkerURL };
@@ -1 +1 @@
1
- {"version":3,"file":"nip46.mjs","names":[],"sources":["../../src/nips/nip46.ts"],"sourcesContent":["/**\n * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.\n * Transport/signing live in {@link Nip46Signer}.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/46.md\n */\nimport { assertHex32, isHex32 } from \"../core/util.ts\";\nimport { NostrError } from \"../core/error.ts\";\n\nexport const BUNKER_REGEX = /^bunker:\\/\\/([0-9a-fA-F]{64})\\??([?/\\w:.=&%-]*)$/;\n\nexport type BunkerPointer = {\n /** Remote signer / bunker public key (hex). */\n pubkey: string;\n relays: string[];\n secret: string | null;\n};\n\nexport type ClientMetadata = {\n name?: string;\n url?: string;\n image?: string;\n};\n\nexport type NostrConnectParams = {\n clientPubkey: string;\n relays: string[];\n secret: string;\n perms?: string[];\n} & ClientMetadata;\n\nexport type Nip46Request = {\n id: string;\n method: string;\n params: string[];\n};\n\nexport type Nip46Response = {\n id: string;\n result?: string;\n error?: string;\n};\n\nexport class Nip46Error extends NostrError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\n/** Encode a bunker pointer as `bunker://…`. */\nexport function toBunkerURL(pointer: BunkerPointer): string {\n const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);\n for (const relay of pointer.relays) {\n url.searchParams.append(\"relay\", relay);\n }\n if (pointer.secret) {\n url.searchParams.set(\"secret\", pointer.secret);\n }\n return url.toString();\n}\n\n/**\n * Parse a `bunker://` URL into a pointer.\n * Returns null when the input is not a bunker URL (including NIP-05 identifiers).\n */\nexport function parseBunkerURL(input: string): BunkerPointer | null {\n const match = input.trim().match(BUNKER_REGEX);\n if (!match?.[1]) return null;\n try {\n const pubkey = assertHex32(match[1], \"bunker pubkey\");\n const qs = new URLSearchParams(match[2] ?? \"\");\n return {\n pubkey,\n relays: qs.getAll(\"relay\"),\n secret: qs.get(\"secret\"),\n };\n } catch {\n return null;\n }\n}\n\n/** Build a client-initiated `nostrconnect://` URI. */\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n if (!isHex32(params.clientPubkey)) {\n throw new Nip46Error(\"invalid client pubkey\");\n }\n if (!params.secret) {\n throw new Nip46Error(\"nostrconnect secret is required\");\n }\n if (params.relays.length === 0) {\n throw new Nip46Error(\"at least one relay is required\");\n }\n\n const query = new URLSearchParams();\n for (const relay of params.relays) {\n query.append(\"relay\", relay);\n }\n query.set(\"secret\", params.secret);\n if (params.perms && params.perms.length > 0) {\n query.set(\"perms\", params.perms.join(\",\"));\n }\n if (params.name) query.set(\"name\", params.name);\n if (params.url) query.set(\"url\", params.url);\n if (params.image) query.set(\"image\", params.image);\n\n return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;\n}\n\n/** Parse a `nostrconnect://` URI. */\nexport function parseNostrConnectURI(uri: string): NostrConnectParams {\n let url: URL;\n try {\n url = new URL(uri);\n } catch (cause) {\n throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n if (url.protocol !== \"nostrconnect:\") {\n throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);\n }\n const clientPubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(clientPubkey)) {\n throw new Nip46Error(\"invalid client pubkey in nostrconnect URI\");\n }\n const secret = url.searchParams.get(\"secret\");\n if (!secret) throw new Nip46Error(\"missing secret in nostrconnect URI\");\n const relays = url.searchParams.getAll(\"relay\");\n if (relays.length === 0) throw new Nip46Error(\"missing relays in nostrconnect URI\");\n const permsRaw = url.searchParams.get(\"perms\");\n return {\n clientPubkey: clientPubkey.toLowerCase(),\n relays,\n secret,\n perms: permsRaw ? permsRaw.split(\",\").filter(Boolean) : undefined,\n name: url.searchParams.get(\"name\") ?? undefined,\n url: url.searchParams.get(\"url\") ?? undefined,\n image: url.searchParams.get(\"image\") ?? undefined,\n };\n}\n\nexport function encodeNip46Request(req: Nip46Request): string {\n return JSON.stringify({ id: req.id, method: req.method, params: req.params });\n}\n\nexport function decodeNip46Request(json: string): Nip46Request {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (cause) {\n throw new Nip46Error(\"invalid NIP-46 request JSON\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n if (!isRecord(data) || typeof data.id !== \"string\" || typeof data.method !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 request shape\");\n }\n if (!Array.isArray(data.params) || !data.params.every((p) => typeof p === \"string\")) {\n throw new Nip46Error(\"invalid NIP-46 request params\");\n }\n return { id: data.id, method: data.method, params: data.params as string[] };\n}\n\nexport function encodeNip46Response(res: Nip46Response): string {\n const body: Record<string, string> = { id: res.id };\n if (res.result !== undefined) body.result = res.result;\n if (res.error !== undefined) body.error = res.error;\n return JSON.stringify(body);\n}\n\nexport function decodeNip46Response(json: string): Nip46Response {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (cause) {\n throw new Nip46Error(\"invalid NIP-46 response JSON\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n if (!isRecord(data) || typeof data.id !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response shape\");\n }\n if (data.result !== undefined && typeof data.result !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response result\");\n }\n if (data.error !== undefined && typeof data.error !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response error\");\n }\n return {\n id: data.id,\n result: data.result as string | undefined,\n error: data.error as string | undefined,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"nip46.mjs","names":[],"sources":["../../src/nips/nip46.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n/**\n * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.\n * Transport/signing live in {@link Nip46Signer}.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/46.md\n */\nimport { assertHex32, isHex32, isRecord } from \"../core/util.ts\";\n\n// oxlint-disable-next-line no-inferrable-types -- isolatedDeclarations requires the annotation for dts emit\nexport const BUNKER_REGEX: RegExp = /^bunker:\\/\\/([0-9a-fA-F]{64})\\??([?/\\w:.=&%-]*)$/;\n\nexport type BunkerPointer = {\n /** Remote signer / bunker public key (hex). */\n pubkey: string;\n relays: string[];\n secret: string | undefined;\n};\n\nexport type ClientMetadata = {\n name?: string | undefined;\n url?: string | undefined;\n image?: string | undefined;\n};\n\nexport type NostrConnectParams = {\n clientPubkey: string;\n relays: string[];\n secret: string;\n perms?: string[] | undefined;\n} & ClientMetadata;\n\nexport type Nip46Request = {\n id: string;\n method: string;\n params: string[];\n};\n\nexport type Nip46Response = {\n id: string;\n result?: string | undefined;\n error?: string | undefined;\n};\n\nexport class Nip46Error extends NostrError {\n override name = \"Nip46Error\";\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\n/** Encode a bunker pointer as `bunker://…`. */\nexport function toBunkerURL(pointer: BunkerPointer): string {\n const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);\n for (const relay of pointer.relays) {\n url.searchParams.append(\"relay\", relay);\n }\n if (pointer.secret !== undefined && pointer.secret !== \"\") {\n url.searchParams.set(\"secret\", pointer.secret);\n }\n return url.toString();\n}\n\n/**\n * Parse a `bunker://` URL into a pointer. Returns undefined when the input is not a bunker URL\n * (including NIP-05 identifiers).\n */\nexport function parseBunkerURL(input: string): BunkerPointer | undefined {\n const match = BUNKER_REGEX.exec(input.trim());\n const hex = match?.at(1);\n if (match === null || hex === undefined) {\n return undefined;\n }\n try {\n const pubkey = assertHex32(hex, \"bunker pubkey\");\n const qs = new URLSearchParams(match.at(2) ?? \"\");\n return {\n pubkey,\n relays: qs.getAll(\"relay\"),\n secret: qs.get(\"secret\") ?? undefined,\n };\n } catch {\n return undefined;\n }\n}\n\n/** Build a client-initiated `nostrconnect://` URI. */\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n if (!isHex32(params.clientPubkey.toLowerCase())) {\n throw new Nip46Error(\"invalid client pubkey\");\n }\n if (!params.secret) {\n throw new Nip46Error(\"nostrconnect secret is required\");\n }\n if (params.relays.length === 0) {\n throw new Nip46Error(\"at least one relay is required\");\n }\n\n const query = new URLSearchParams();\n for (const relay of params.relays) {\n query.append(\"relay\", relay);\n }\n query.set(\"secret\", params.secret);\n if (params.perms !== undefined && params.perms.length > 0) {\n query.set(\"perms\", params.perms.join(\",\"));\n }\n if (params.name !== undefined && params.name !== \"\") {\n query.set(\"name\", params.name);\n }\n if (params.url !== undefined && params.url !== \"\") {\n query.set(\"url\", params.url);\n }\n if (params.image !== undefined && params.image !== \"\") {\n query.set(\"image\", params.image);\n }\n\n return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;\n}\n\n/** Parse a `nostrconnect://` URI. */\nexport function parseNostrConnectURI(uri: string): NostrConnectParams {\n let url: URL;\n try {\n url = new URL(uri);\n } catch (error) {\n throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (url.protocol !== \"nostrconnect:\") {\n throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);\n }\n const clientPubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(clientPubkey.toLowerCase())) {\n throw new Nip46Error(\"invalid client pubkey in nostrconnect URI\");\n }\n const secret = url.searchParams.get(\"secret\");\n if (secret === null || secret === \"\") {\n throw new Nip46Error(\"missing secret in nostrconnect URI\");\n }\n const relays = url.searchParams.getAll(\"relay\");\n if (relays.length === 0) {\n throw new Nip46Error(\"missing relays in nostrconnect URI\");\n }\n const params: NostrConnectParams = {\n clientPubkey: clientPubkey.toLowerCase(),\n relays,\n secret,\n };\n const permsRaw = url.searchParams.get(\"perms\");\n if (permsRaw !== null && permsRaw !== \"\") {\n params.perms = permsRaw.split(\",\").filter((p) => p !== \"\");\n }\n const name = url.searchParams.get(\"name\");\n if (name !== null) {\n params.name = name;\n }\n const metadataUrl = url.searchParams.get(\"url\");\n if (metadataUrl !== null) {\n params.url = metadataUrl;\n }\n const image = url.searchParams.get(\"image\");\n if (image !== null) {\n params.image = image;\n }\n return params;\n}\n\nexport function encodeNip46Request(req: Nip46Request): string {\n return JSON.stringify({ id: req.id, method: req.method, params: req.params });\n}\n\nexport function decodeNip46Request(json: string): Nip46Request {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (error) {\n throw new Nip46Error(\"invalid NIP-46 request JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\" || typeof data[\"method\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 request shape\");\n }\n if (!Array.isArray(data[\"params\"]) || !data[\"params\"].every((p) => typeof p === \"string\")) {\n throw new Nip46Error(\"invalid NIP-46 request params\");\n }\n return { id: data[\"id\"], method: data[\"method\"], params: data[\"params\"] };\n}\n\nexport function encodeNip46Response(res: Nip46Response): string {\n const body: Record<string, string> = { id: res.id };\n if (res.result !== undefined) {\n body[\"result\"] = res.result;\n }\n if (res.error !== undefined) {\n body[\"error\"] = res.error;\n }\n return JSON.stringify(body);\n}\n\nexport function decodeNip46Response(json: string): Nip46Response {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (error) {\n throw new Nip46Error(\"invalid NIP-46 response JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response shape\");\n }\n // Some remote signers send explicit nulls for absent fields.\n const result = data[\"result\"] === null ? undefined : data[\"result\"];\n const error = data[\"error\"] === null ? undefined : data[\"error\"];\n if (result !== undefined && typeof result !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response result\");\n }\n if (error !== undefined && typeof error !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response error\");\n }\n const response: Nip46Response = { id: data[\"id\"] };\n if (result !== undefined) {\n response.result = result;\n }\n if (error !== undefined) {\n response.error = error;\n }\n return response;\n}\n"],"mappings":"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"}
@@ -1,12 +1,13 @@
1
- import { a as NostrError } from "../error-WEFS-R-4.mjs";
1
+ import { a as NostrError } from "../error-CR8Urc84.mjs";
2
2
  //#region src/nips/nip49.d.ts
3
- type Ncryptsec = `ncryptsec1${string}`;
4
- type KeySecurityByte = 0x00 | 0x01 | 0x02;
5
- declare class Nip49Error extends NostrError {}
3
+ export type Ncryptsec = `ncryptsec1${string}`;
4
+ export type KeySecurityByte = 0x00 | 0x01 | 0x02;
5
+ export declare class Nip49Error extends NostrError {
6
+ name: string;
7
+ }
6
8
  /** Encrypt a 32-byte secret key to an `ncryptsec` bech32 string. */
7
- declare function encrypt(secretKey: Uint8Array, password: string, logn?: number, ksb?: KeySecurityByte): Ncryptsec;
9
+ export declare function encrypt(secretKey: Uint8Array, password: string, logn?: number, ksb?: KeySecurityByte): Ncryptsec;
8
10
  /** Decrypt an `ncryptsec` bech32 string to a 32-byte secret key. */
9
- declare function decrypt(ncryptsec: string, password: string): Uint8Array;
11
+ export declare function decrypt(ncryptsec: string, password: string): Uint8Array;
10
12
  //#endregion
11
- export { KeySecurityByte, Ncryptsec, Nip49Error, decrypt, encrypt };
12
13
  //# sourceMappingURL=nip49.d.mts.map
@@ -1,5 +1,4 @@
1
- import { n as assertSecretKeyBytes } from "../util-6tfLQ9JF.mjs";
2
- import { a as NostrError } from "../error-DZEs5OuZ.mjs";
1
+ import { g as NostrError, r as assertSecretKeyBytes } from "../util-LfQirgDk.mjs";
3
2
  import { Bech32MaxSize, encodeBytes } from "./nip19.mjs";
4
3
  import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
5
4
  import { bech32 } from "@scure/base";
@@ -19,7 +18,9 @@ const LOGN_MIN = 1;
19
18
  const LOGN_MAX = 22;
20
19
  const SCRYPT_R = 8;
21
20
  const SCRYPT_P = 1;
22
- var Nip49Error = class extends NostrError {};
21
+ var Nip49Error = class extends NostrError {
22
+ name = "Nip49Error";
23
+ };
23
24
  function assertLogn(logn) {
24
25
  if (!Number.isInteger(logn) || logn < LOGN_MIN || logn > LOGN_MAX) throw new Nip49Error(`invalid logn ${logn}, expected integer ${LOGN_MIN}..${LOGN_MAX}`);
25
26
  }
@@ -51,27 +52,30 @@ function decrypt(ncryptsec, password) {
51
52
  let b;
52
53
  try {
53
54
  const decoded = bech32.decode(ncryptsec, Bech32MaxSize);
54
- prefix = decoded.prefix;
55
+ ({prefix} = decoded);
55
56
  b = new Uint8Array(bech32.fromWords(decoded.words));
56
- } catch (cause) {
57
- throw new Nip49Error("invalid ncryptsec", { cause: cause instanceof Error ? cause : void 0 });
57
+ } catch (error) {
58
+ throw new Nip49Error("invalid ncryptsec", { cause: error instanceof Error ? error : void 0 });
58
59
  }
59
60
  if (prefix !== "ncryptsec") throw new Nip49Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);
60
61
  if (b.length !== PAYLOAD_LEN) throw new Nip49Error("invalid ncryptsec length");
61
- const version = b[0];
62
+ const version = b.at(0);
63
+ if (version === void 0) throw new Nip49Error("invalid ncryptsec length");
62
64
  if (version !== VERSION) throw new Nip49Error(`invalid version ${version}, expected 0x02`);
63
- const logn = b[1];
65
+ const logn = b.at(1);
66
+ if (logn === void 0) throw new Nip49Error("invalid ncryptsec length");
64
67
  const salt = b.subarray(2, 18);
65
68
  const nonce = b.subarray(18, 42);
66
- const ksb = b[42];
69
+ const ksb = b.at(42);
70
+ if (ksb === void 0) throw new Nip49Error("invalid ncryptsec length");
67
71
  const aad = Uint8Array.from([ksb]);
68
72
  const ciphertext = b.subarray(43);
69
73
  try {
70
74
  const key = deriveKey(password, salt, logn);
71
75
  return xchacha20poly1305(key, nonce, aad).decrypt(ciphertext);
72
- } catch (cause) {
73
- if (cause instanceof Nip49Error) throw cause;
74
- throw new Nip49Error("failed to decrypt", { cause: cause instanceof Error ? cause : void 0 });
76
+ } catch (error) {
77
+ if (error instanceof Nip49Error) throw error;
78
+ throw new Nip49Error("failed to decrypt", { cause: error instanceof Error ? error : void 0 });
75
79
  }
76
80
  }
77
81
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"nip49.mjs","names":[],"sources":["../../src/nips/nip49.ts"],"sourcesContent":["/**\n * NIP-49: Private Key Encryption (`ncryptsec`).\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/49.md\n */\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport { scrypt } from \"@noble/hashes/scrypt.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { bech32 } from \"@scure/base\";\nimport { NostrError } from \"../core/error.ts\";\nimport { assertSecretKeyBytes } from \"../core/util.ts\";\nimport { Bech32MaxSize, encodeBytes } from \"./nip19.ts\";\n\nexport type Ncryptsec = `ncryptsec1${string}`;\nexport type KeySecurityByte = 0x00 | 0x01 | 0x02;\n\nconst VERSION = 0x02;\nconst SALT_LEN = 16;\nconst NONCE_LEN = 24;\nconst PAYLOAD_LEN = 91;\nconst LOGN_MIN = 1;\nconst LOGN_MAX = 22;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\n\nexport class Nip49Error extends NostrError {}\n\nfunction assertLogn(logn: number): void {\n if (!Number.isInteger(logn) || logn < LOGN_MIN || logn > LOGN_MAX) {\n throw new Nip49Error(`invalid logn ${logn}, expected integer ${LOGN_MIN}..${LOGN_MAX}`);\n }\n}\n\nfunction deriveKey(password: string, salt: Uint8Array, logn: number): Uint8Array {\n assertLogn(logn);\n const N = 2 ** logn;\n return scrypt(password.normalize(\"NFKC\"), salt, {\n N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n dkLen: 32,\n // noble 2.3: V (N blocks) + p B blocks + one tmp scratch block.\n maxmem: 128 * SCRYPT_R * (N + SCRYPT_P + 1),\n });\n}\n\n/** Encrypt a 32-byte secret key to an `ncryptsec` bech32 string. */\nexport function encrypt(\n secretKey: Uint8Array,\n password: string,\n logn: number = 16,\n ksb: KeySecurityByte = 0x02,\n): Ncryptsec {\n assertSecretKeyBytes(secretKey);\n const salt = randomBytes(SALT_LEN);\n const key = deriveKey(password, salt, logn);\n const nonce = randomBytes(NONCE_LEN);\n const aad = Uint8Array.from([ksb]);\n const ciphertext = xchacha20poly1305(key, nonce, aad).encrypt(secretKey);\n const bytes = concatBytes(\n Uint8Array.from([VERSION]),\n Uint8Array.from([logn]),\n salt,\n nonce,\n aad,\n ciphertext,\n );\n return encodeBytes(\"ncryptsec\", bytes);\n}\n\n/** Decrypt an `ncryptsec` bech32 string to a 32-byte secret key. */\nexport function decrypt(ncryptsec: string, password: string): Uint8Array {\n let prefix: string;\n let b: Uint8Array;\n try {\n const decoded = bech32.decode(ncryptsec as `${string}1${string}`, Bech32MaxSize);\n prefix = decoded.prefix;\n b = new Uint8Array(bech32.fromWords(decoded.words));\n } catch (cause) {\n throw new Nip49Error(\"invalid ncryptsec\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n if (prefix !== \"ncryptsec\") {\n throw new Nip49Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);\n }\n if (b.length !== PAYLOAD_LEN) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n const version = b[0]!;\n if (version !== VERSION) {\n throw new Nip49Error(`invalid version ${version}, expected 0x02`);\n }\n const logn = b[1]!;\n const salt = b.subarray(2, 2 + SALT_LEN);\n const nonce = b.subarray(2 + SALT_LEN, 2 + SALT_LEN + NONCE_LEN);\n const ksb = b[2 + SALT_LEN + NONCE_LEN]!;\n const aad = Uint8Array.from([ksb]);\n const ciphertext = b.subarray(2 + SALT_LEN + NONCE_LEN + 1);\n try {\n const key = deriveKey(password, salt, logn);\n return xchacha20poly1305(key, nonce, aad).decrypt(ciphertext);\n } catch (cause) {\n if (cause instanceof Nip49Error) throw cause;\n throw new Nip49Error(\"failed to decrypt\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,UAAU;AAChB,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,cAAc;AACpB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;AAEjB,IAAa,aAAb,cAAgC,WAAW,CAAC;AAE5C,SAAS,WAAW,MAAoB;CACtC,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,YAAY,OAAO,UACvD,MAAM,IAAI,WAAW,gBAAgB,KAAK,qBAAqB,SAAS,IAAI,UAAU;AAE1F;AAEA,SAAS,UAAU,UAAkB,MAAkB,MAA0B;CAC/E,WAAW,IAAI;CACf,MAAM,IAAI,KAAK;CACf,OAAO,OAAO,SAAS,UAAU,MAAM,GAAG,MAAM;EAC9C;EACA,GAAG;EACH,GAAG;EACH,OAAO;EAEP,QAAQ,QAAkB,IAAI,WAAW;CAC3C,CAAC;AACH;;AAGA,SAAgB,QACd,WACA,UACA,OAAe,IACf,MAAuB,GACZ;CACX,qBAAqB,SAAS;CAC9B,MAAM,OAAO,YAAY,QAAQ;CACjC,MAAM,MAAM,UAAU,UAAU,MAAM,IAAI;CAC1C,MAAM,QAAQ,YAAY,SAAS;CACnC,MAAM,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;CACjC,MAAM,aAAa,kBAAkB,KAAK,OAAO,GAAG,CAAC,CAAC,QAAQ,SAAS;CACvE,MAAM,QAAQ,YACZ,WAAW,KAAK,CAAC,OAAO,CAAC,GACzB,WAAW,KAAK,CAAC,IAAI,CAAC,GACtB,MACA,OACA,KACA,UACF;CACA,OAAO,YAAY,aAAa,KAAK;AACvC;;AAGA,SAAgB,QAAQ,WAAmB,UAA8B;CACvE,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,MAAM,UAAU,OAAO,OAAO,WAAoC,aAAa;EAC/E,SAAS,QAAQ;EACjB,IAAI,IAAI,WAAW,OAAO,UAAU,QAAQ,KAAK,CAAC;CACpD,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,qBAAqB,EACxC,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,WAAW,aACb,MAAM,IAAI,WAAW,kBAAkB,OAAO,uBAAuB;CAEvE,IAAI,EAAE,WAAW,aACf,MAAM,IAAI,WAAW,0BAA0B;CAEjD,MAAM,UAAU,EAAE;CAClB,IAAI,YAAY,SACd,MAAM,IAAI,WAAW,mBAAmB,QAAQ,gBAAgB;CAElE,MAAM,OAAO,EAAE;CACf,MAAM,OAAO,EAAE,SAAS,GAAG,EAAY;CACvC,MAAM,QAAQ,EAAE,SAAS,IAAc,EAAwB;CAC/D,MAAM,MAAM,EAAE;CACd,MAAM,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;CACjC,MAAM,aAAa,EAAE,SAAS,EAA4B;CAC1D,IAAI;EACF,MAAM,MAAM,UAAU,UAAU,MAAM,IAAI;EAC1C,OAAO,kBAAkB,KAAK,OAAO,GAAG,CAAC,CAAC,QAAQ,UAAU;CAC9D,SAAS,OAAO;EACd,IAAI,iBAAiB,YAAY,MAAM;EACvC,MAAM,IAAI,WAAW,qBAAqB,EACxC,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"nip49.mjs","names":[],"sources":["../../src/nips/nip49.ts"],"sourcesContent":["/**\n * NIP-49: Private Key Encryption (`ncryptsec`).\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/49.md\n */\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport { scrypt } from \"@noble/hashes/scrypt.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { bech32 } from \"@scure/base\";\n\nimport { NostrError } from \"../core/error.ts\";\nimport { assertSecretKeyBytes } from \"../core/util.ts\";\nimport { Bech32MaxSize, encodeBytes } from \"./nip19.ts\";\n\nexport type Ncryptsec = `ncryptsec1${string}`;\nexport type KeySecurityByte = 0x00 | 0x01 | 0x02;\n\nconst VERSION = 0x02;\nconst SALT_LEN = 16;\nconst NONCE_LEN = 24;\nconst PAYLOAD_LEN = 91;\nconst LOGN_MIN = 1;\nconst LOGN_MAX = 22;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\n\nexport class Nip49Error extends NostrError {\n override name = \"Nip49Error\";\n}\n\nfunction assertLogn(logn: number): void {\n if (!Number.isInteger(logn) || logn < LOGN_MIN || logn > LOGN_MAX) {\n throw new Nip49Error(`invalid logn ${logn}, expected integer ${LOGN_MIN}..${LOGN_MAX}`);\n }\n}\n\nfunction deriveKey(password: string, salt: Uint8Array, logn: number): Uint8Array {\n assertLogn(logn);\n const N = 2 ** logn;\n return scrypt(password.normalize(\"NFKC\"), salt, {\n N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n dkLen: 32,\n // noble 2.3: V (N blocks) + p B blocks + one tmp scratch block.\n maxmem: 128 * SCRYPT_R * (N + SCRYPT_P + 1),\n });\n}\n\n/** Encrypt a 32-byte secret key to an `ncryptsec` bech32 string. */\nexport function encrypt(\n secretKey: Uint8Array,\n password: string,\n logn = 16,\n ksb: KeySecurityByte = 0x02,\n): Ncryptsec {\n assertSecretKeyBytes(secretKey);\n const salt = randomBytes(SALT_LEN);\n const key = deriveKey(password, salt, logn);\n const nonce = randomBytes(NONCE_LEN);\n const aad = Uint8Array.from([ksb]);\n const ciphertext = xchacha20poly1305(key, nonce, aad).encrypt(secretKey);\n const bytes = concatBytes(\n Uint8Array.from([VERSION]),\n Uint8Array.from([logn]),\n salt,\n nonce,\n aad,\n ciphertext,\n );\n return encodeBytes(\"ncryptsec\", bytes);\n}\n\n/** Decrypt an `ncryptsec` bech32 string to a 32-byte secret key. */\nexport function decrypt(ncryptsec: string, password: string): Uint8Array {\n let prefix: string;\n let b: Uint8Array;\n try {\n const decoded = bech32.decode(ncryptsec, Bech32MaxSize);\n ({ prefix } = decoded);\n b = new Uint8Array(bech32.fromWords(decoded.words));\n } catch (error) {\n throw new Nip49Error(\"invalid ncryptsec\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (prefix !== \"ncryptsec\") {\n throw new Nip49Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);\n }\n if (b.length !== PAYLOAD_LEN) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n const version = b.at(0);\n if (version === undefined) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n if (version !== VERSION) {\n throw new Nip49Error(`invalid version ${version}, expected 0x02`);\n }\n const logn = b.at(1);\n if (logn === undefined) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n const salt = b.subarray(2, 2 + SALT_LEN);\n const nonce = b.subarray(2 + SALT_LEN, 2 + SALT_LEN + NONCE_LEN);\n const ksb = b.at(2 + SALT_LEN + NONCE_LEN);\n if (ksb === undefined) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n const aad = Uint8Array.from([ksb]);\n const ciphertext = b.subarray(2 + SALT_LEN + NONCE_LEN + 1);\n try {\n const key = deriveKey(password, salt, logn);\n return xchacha20poly1305(key, nonce, aad).decrypt(ciphertext);\n } catch (error) {\n if (error instanceof Nip49Error) {\n throw error;\n }\n throw new Nip49Error(\"failed to decrypt\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,UAAU;AAChB,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,cAAc;AACpB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;AAEjB,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;AAEA,SAAS,WAAW,MAAoB;CACtC,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,YAAY,OAAO,UACvD,MAAM,IAAI,WAAW,gBAAgB,KAAK,qBAAqB,SAAS,IAAI,UAAU;AAE1F;AAEA,SAAS,UAAU,UAAkB,MAAkB,MAA0B;CAC/E,WAAW,IAAI;CACf,MAAM,IAAI,KAAK;CACf,OAAO,OAAO,SAAS,UAAU,MAAM,GAAG,MAAM;EAC9C;EACA,GAAG;EACH,GAAG;EACH,OAAO;EAEP,QAAQ,QAAkB,IAAI,WAAW;CAC3C,CAAC;AACH;;AAGA,SAAgB,QACd,WACA,UACA,OAAO,IACP,MAAuB,GACZ;CACX,qBAAqB,SAAS;CAC9B,MAAM,OAAO,YAAY,QAAQ;CACjC,MAAM,MAAM,UAAU,UAAU,MAAM,IAAI;CAC1C,MAAM,QAAQ,YAAY,SAAS;CACnC,MAAM,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;CACjC,MAAM,aAAa,kBAAkB,KAAK,OAAO,GAAG,CAAC,CAAC,QAAQ,SAAS;CACvE,MAAM,QAAQ,YACZ,WAAW,KAAK,CAAC,OAAO,CAAC,GACzB,WAAW,KAAK,CAAC,IAAI,CAAC,GACtB,MACA,OACA,KACA,UACF;CACA,OAAO,YAAY,aAAa,KAAK;AACvC;;AAGA,SAAgB,QAAQ,WAAmB,UAA8B;CACvE,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,MAAM,UAAU,OAAO,OAAO,WAAW,aAAa;EACtD,CAAC,CAAE,UAAW;EACd,IAAI,IAAI,WAAW,OAAO,UAAU,QAAQ,KAAK,CAAC;CACpD,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,qBAAqB,EACxC,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,WAAW,aACb,MAAM,IAAI,WAAW,kBAAkB,OAAO,uBAAuB;CAEvE,IAAI,EAAE,WAAW,aACf,MAAM,IAAI,WAAW,0BAA0B;CAEjD,MAAM,UAAU,EAAE,GAAG,CAAC;CACtB,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,WAAW,0BAA0B;CAEjD,IAAI,YAAY,SACd,MAAM,IAAI,WAAW,mBAAmB,QAAQ,gBAAgB;CAElE,MAAM,OAAO,EAAE,GAAG,CAAC;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,WAAW,0BAA0B;CAEjD,MAAM,OAAO,EAAE,SAAS,GAAG,EAAY;CACvC,MAAM,QAAQ,EAAE,SAAS,IAAc,EAAwB;CAC/D,MAAM,MAAM,EAAE,GAAG,EAAwB;CACzC,IAAI,QAAQ,KAAA,GACV,MAAM,IAAI,WAAW,0BAA0B;CAEjD,MAAM,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;CACjC,MAAM,aAAa,EAAE,SAAS,EAA4B;CAC1D,IAAI;EACF,MAAM,MAAM,UAAU,UAAU,MAAM,IAAI;EAC1C,OAAO,kBAAkB,KAAK,OAAO,GAAG,CAAC,CAAC,QAAQ,UAAU;CAC9D,SAAS,OAAO;EACd,IAAI,iBAAiB,YACnB,MAAM;EAER,MAAM,IAAI,WAAW,qBAAqB,EACxC,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;AACF"}